From 50cdbfa42481fb0c6be44eb02fd3cf1fc79725ad Mon Sep 17 00:00:00 2001 From: Antonio Juarez Date: Thu, 30 Jul 2015 16:22:07 +0100 Subject: [PATCH] Bytecoin v.1.0.6 release --- CMakeLists.txt | 7 +- ReleaseNotes.txt | 6 + include/BlockchainExplorerData.h | 34 +- include/CryptoNote.h | 114 ++ .../json_utils.h => include/CryptoTypes.h | 36 +- include/IBlockchainExplorer.h | 13 +- include/IMultiWallet.h | 84 - include/INode.h | 57 +- include/ITransaction.h | 106 +- include/ITransfersContainer.h | 30 +- include/ITransfersSynchronizer.h | 15 +- include/IWallet.h | 156 +- include/IWalletLegacy.h | 116 ++ src/BlockchainExplorer/BlockchainExplorer.cpp | 261 +++- src/BlockchainExplorer/BlockchainExplorer.h | 29 +- .../BlockchainExplorerDataBuilder.cpp | 245 ++- .../BlockchainExplorerDataBuilder.h | 11 +- src/CMakeLists.txt | 52 +- src/Common/ArrayRef.h | 2 +- src/Common/ArrayView.h | 2 +- src/Common/{base58.cpp => Base58.cpp} | 14 +- src/Common/{base58.h => Base58.h} | 4 +- src/Common/BlockingQueue.cpp | 4 + .../{command_line.cpp => CommandLine.cpp} | 2 +- src/Common/{command_line.h => CommandLine.h} | 0 src/Common/IInputStream.h | 2 +- src/Common/IOutputStream.h | 2 +- src/Common/JsonValue.cpp | 12 +- src/Common/JsonValue.h | 12 +- src/Common/Math.cpp | 4 + .../MemoryInputStream.cpp} | 35 +- .../MemoryInputStream.h} | 29 +- src/Common/ObserverManager.h | 3 +- src/Common/SignalHandler.cpp | 23 +- src/Common/SignalHandler.h | 2 +- src/Common/StdInputStream.cpp | 30 + src/Common/StdInputStream.h | 35 + src/Common/StdOutputStream.cpp | 34 + src/Common/StdOutputStream.h | 35 + src/Common/StreamTools.cpp | 20 +- src/Common/StreamTools.h | 10 +- src/Common/StringBuffer.h | 6 +- src/Common/StringInputStream.cpp | 2 +- src/Common/StringInputStream.h | 4 +- src/Common/StringOutputStream.cpp | 2 +- src/Common/StringOutputStream.h | 2 +- src/Common/StringTools.cpp | 37 +- src/Common/StringTools.h | 19 +- src/Common/StringView.h | 2 +- src/Common/{util.cpp => Util.cpp} | 8 +- src/Common/{util.h => Util.h} | 4 +- src/Common/{varint.h => Varint.h} | 2 +- src/Common/VectorOutputStream.cpp | 30 + src/Common/VectorOutputStream.h | 36 + src/Common/static_assert.h | 52 +- .../ConnectivityTool.cpp} | 85 +- src/CryptoNote/BaseTransaction.cpp | 79 - src/CryptoNote/BaseTransaction.h | 62 - src/CryptoNote/Block.cpp | 118 -- src/CryptoNote/Block.h | 78 - src/CryptoNote/KeyInput.cpp | 45 - src/CryptoNote/KeyInput.h | 46 - src/CryptoNote/MultisignatureInput.cpp | 41 - src/CryptoNote/MultisignatureInput.h | 40 - src/CryptoNote/Transaction.cpp | 111 -- src/CryptoNote/Transaction.h | 89 -- .../UnsignedMultisignatureInput.cpp | 33 - src/CryptoNote/UnsignedTransaction.cpp | 111 -- src/CryptoNote/UnsignedTransaction.h | 89 -- ...cryptonote_config.h => CryptoNoteConfig.h} | 13 +- .../Account.cpp} | 24 +- .../account.h => CryptoNoteCore/Account.h} | 28 +- src/CryptoNoteCore/BlockIndex.cpp | 88 ++ .../BlockIndex.h | 34 +- .../Blockchain.cpp} | 1384 ++++++++++------- src/CryptoNoteCore/Blockchain.h | 382 +++++ src/CryptoNoteCore/BlockchainIndices.cpp | 262 ++++ src/CryptoNoteCore/BlockchainIndices.h | 121 ++ src/CryptoNoteCore/BlockchainMessages.cpp | 109 ++ src/CryptoNoteCore/BlockchainMessages.h | 84 + .../Checkpoints.cpp} | 20 +- .../Checkpoints.h} | 18 +- src/CryptoNoteCore/Core.cpp | 1031 ++++++++++++ src/CryptoNoteCore/Core.h | 194 +++ .../CoreConfig.cpp | 6 +- .../CoreConfig.h | 0 .../CryptoNoteBasic.cpp} | 15 +- src/CryptoNoteCore/CryptoNoteBasic.h | 46 + .../CryptoNoteBasicImpl.cpp} | 47 +- .../CryptoNoteBasicImpl.h} | 24 +- src/CryptoNoteCore/CryptoNoteFormatUtils.cpp | 550 +++++++ src/CryptoNoteCore/CryptoNoteFormatUtils.h | 128 ++ .../CryptoNoteSerialization.cpp} | 276 ++-- .../CryptoNoteSerialization.h} | 50 +- .../CryptoNoteStatInfo.h} | 2 +- src/CryptoNoteCore/CryptoNoteTools.cpp | 79 + src/CryptoNoteCore/CryptoNoteTools.h | 125 ++ .../Currency.cpp | 89 +- .../Currency.h | 28 +- .../Difficulty.cpp} | 23 +- .../Difficulty.h} | 2 +- .../IBlock.cpp} | 15 +- .../KeyOutput.cpp => CryptoNoteCore/IBlock.h} | 22 +- .../IBlockchainStorageObserver.h | 0 src/CryptoNoteCore/ICore.h | 122 ++ .../ICoreObserver.h | 0 .../IMinerHandler.h} | 10 +- .../ITimeProvider.cpp | 0 .../ITimeProvider.h | 0 .../ITransactionValidator.h | 11 +- .../ITxPoolObserver.h | 0 src/CryptoNoteCore/IntrusiveLinkedList.h | 211 +++ src/CryptoNoteCore/MessageQueue.h | 114 ++ .../miner.cpp => CryptoNoteCore/Miner.cpp} | 58 +- .../miner.h => CryptoNoteCore/Miner.h} | 32 +- .../MinerConfig.cpp | 4 +- .../MinerConfig.h | 2 +- .../OnceInInterval.h | 0 .../SwappedMap.cpp | 4 + .../SwappedMap.h | 14 +- .../SwappedVector.cpp | 4 + .../SwappedVector.h | 29 +- src/CryptoNoteCore/Transaction.cpp | 532 +++++++ .../TransactionApi.h | 11 +- .../TransactionApiExtra.h} | 42 +- src/CryptoNoteCore/TransactionExtra.cpp | 247 +++ src/CryptoNoteCore/TransactionExtra.h | 90 ++ .../TransactionPool.cpp} | 198 ++- .../TransactionPool.h} | 88 +- src/CryptoNoteCore/TransactionPrefixImpl.cpp | 227 +++ src/CryptoNoteCore/TransactionUtils.cpp | 164 ++ src/CryptoNoteCore/TransactionUtils.h | 42 + .../UpgradeDetector.cpp | 0 .../UpgradeDetector.h | 4 +- .../VerificationContext.h} | 0 .../CryptoNoteProtocolDefinitions.h} | 62 +- .../CryptoNoteProtocolHandler.cpp} | 278 ++-- .../CryptoNoteProtocolHandler.h} | 75 +- .../CryptoNoteProtocolHandlerCommon.h} | 0 .../ICryptoNoteProtocolObserver.h} | 6 +- .../ICryptoNoteProtocolQuery.h} | 10 +- src/{daemon/daemon.cpp => Daemon/Daemon.cpp} | 35 +- .../DaemonCommandsHandler.cpp} | 53 +- .../DaemonCommandsHandler.h | 8 +- src/HTTP/HttpParserErrorCodes.cpp | 2 +- src/HTTP/HttpParserErrorCodes.h | 2 +- src/HTTP/HttpResponse.cpp | 2 +- src/InProcessNode/InProcessNode.cpp | 611 ++++++-- src/InProcessNode/InProcessNode.h | 93 +- src/JsonRpcServer/JsonRpcServer.cpp | 190 +++ .../JsonRpcServer.h | 39 +- .../NodeErrors.cpp | 0 .../NodeErrors.h | 0 src/NodeRpcProxy/NodeRpcProxy.cpp | 637 ++++++++ src/NodeRpcProxy/NodeRpcProxy.h | 139 ++ .../ConnectionContext.h} | 26 +- src/P2p/IP2pNodeInternal.cpp | 18 + .../IP2pNodeInternal.h} | 27 +- src/{p2p => P2p}/LevinProtocol.cpp | 48 +- src/{p2p => P2p}/LevinProtocol.h | 53 +- src/{p2p/net_node.cpp => P2p/NetNode.cpp} | 722 +++++---- src/P2p/NetNode.h | 273 ++++ .../net_node_common.h => P2p/NetNodeCommon.h} | 25 +- src/{p2p => P2p}/NetNodeConfig.cpp | 126 +- src/P2p/NetNodeConfig.h | 76 + src/P2p/P2pConnectionProxy.cpp | 168 ++ src/P2p/P2pConnectionProxy.h | 59 + src/P2p/P2pContext.cpp | 193 +++ src/P2p/P2pContext.h | 104 ++ .../P2pContextOwner.cpp} | 26 +- .../MultiWallet.h => P2p/P2pContextOwner.h} | 34 +- .../LatchGuard.h => P2p/P2pInterfaces.cpp} | 16 +- .../P2pInterfaces.h} | 30 +- src/{p2p/p2p_networks.h => P2p/P2pNetworks.h} | 0 src/P2p/P2pNode.cpp | 553 +++++++ src/P2p/P2pNode.h | 122 ++ src/P2p/P2pNodeConfig.cpp | 130 ++ src/P2p/P2pNodeConfig.h | 63 + .../P2pProtocolDefinitions.h} | 52 +- .../P2pProtocolTypes.h} | 22 +- src/{p2p => P2p}/PeerListManager.cpp | 161 +- src/{p2p => P2p}/PeerListManager.h | 82 +- .../NodeFactory.cpp | 61 +- .../NodeFactory.h | 0 .../PaymentServiceJsonRpcMessages.cpp} | 55 +- .../PaymentServiceJsonRpcMessages.h} | 58 +- .../PaymentServiceJsonRpcServer.cpp} | 271 ++-- src/PaymentGate/PaymentServiceJsonRpcServer.h | 39 + .../WalletFactory.cpp | 10 +- .../WalletFactory.h | 3 +- .../WalletService.cpp | 415 ++--- .../WalletService.h | 62 +- .../ConfigurationManager.cpp | 8 +- .../ConfigurationManager.h | 4 +- src/PaymentGateService/PaymentGateService.cpp | 242 +++ src/PaymentGateService/PaymentGateService.h | 64 + .../PaymentServiceConfiguration.cpp | 17 +- .../PaymentServiceConfiguration.h | 4 +- .../RpcNodeConfiguration.cpp | 0 .../RpcNodeConfiguration.h | 0 src/PaymentGateService/main.cpp | 335 ++++ src/Platform/Linux/System/Dispatcher.cpp | 299 +++- src/Platform/Linux/System/Dispatcher.h | 75 +- src/Platform/Linux/System/Ipv4Resolver.cpp | 19 +- src/Platform/Linux/System/Ipv4Resolver.h | 3 - src/Platform/Linux/System/TcpConnection.cpp | 91 +- src/Platform/Linux/System/TcpConnection.h | 7 +- src/Platform/Linux/System/TcpConnector.cpp | 47 +- src/Platform/Linux/System/TcpConnector.h | 3 - src/Platform/Linux/System/TcpListener.cpp | 69 +- src/Platform/Linux/System/TcpListener.h | 3 - src/Platform/Linux/System/Timer.cpp | 78 +- src/Platform/Linux/System/Timer.h | 3 - .../OSX/System/{context.c => Context.c} | 0 .../OSX/System/{context.h => Context.h} | 0 src/Platform/OSX/System/Dispatcher.cpp | 297 +++- src/Platform/OSX/System/Dispatcher.h | 62 +- src/Platform/OSX/System/Ipv4Resolver.cpp | 19 +- src/Platform/OSX/System/Ipv4Resolver.h | 3 - src/Platform/OSX/System/TcpConnection.cpp | 96 +- src/Platform/OSX/System/TcpConnection.h | 5 +- src/Platform/OSX/System/TcpConnector.cpp | 47 +- src/Platform/OSX/System/TcpConnector.h | 3 - src/Platform/OSX/System/TcpListener.cpp | 58 +- src/Platform/OSX/System/TcpListener.h | 3 - src/Platform/OSX/System/Timer.cpp | 64 +- src/Platform/OSX/System/Timer.h | 3 - src/Platform/Windows/System/Dispatcher.cpp | 274 +++- src/Platform/Windows/System/Dispatcher.h | 51 +- src/Platform/Windows/System/Ipv4Resolver.cpp | 25 +- src/Platform/Windows/System/Ipv4Resolver.h | 5 +- src/Platform/Windows/System/TcpConnection.cpp | 106 +- src/Platform/Windows/System/TcpConnection.h | 13 +- src/Platform/Windows/System/TcpConnector.cpp | 59 +- src/Platform/Windows/System/TcpConnector.h | 3 - src/Platform/Windows/System/TcpListener.cpp | 56 +- src/Platform/Windows/System/TcpListener.h | 5 +- src/Platform/Windows/System/Timer.cpp | 46 +- src/Platform/Windows/System/Timer.h | 3 - .../CoreRpcServerCommandsDefinitions.h} | 99 +- .../CoreRpcServerErrorCodes.h} | 0 src/{rpc => Rpc}/HttpClient.cpp | 19 +- src/{rpc => Rpc}/HttpClient.h | 3 +- src/{rpc => Rpc}/HttpServer.cpp | 27 +- src/{rpc => Rpc}/HttpServer.h | 4 +- src/{rpc => Rpc}/JsonRpc.cpp | 2 +- src/{rpc => Rpc}/JsonRpc.h | 4 +- src/{rpc => Rpc}/RpcServer.cpp | 195 ++- src/{rpc => Rpc}/RpcServer.h | 12 +- src/{rpc => Rpc}/RpcServerConfig.cpp | 4 +- src/{rpc => Rpc}/RpcServerConfig.h | 0 .../BinaryInputStreamSerializer.cpp | 64 +- .../BinaryInputStreamSerializer.h | 13 +- .../BinaryOutputStreamSerializer.cpp | 38 +- .../BinaryOutputStreamSerializer.h | 13 +- src/Serialization/BinarySerializationTools.h | 89 ++ .../ISerializer.h | 13 +- .../IStream.h | 4 +- .../JsonInputStreamSerializer.cpp | 15 +- .../JsonInputStreamSerializer.h | 3 - .../JsonInputValueSerializer.cpp | 44 +- .../JsonInputValueSerializer.h | 11 +- .../JsonOutputStreamSerializer.cpp | 14 +- .../JsonOutputStreamSerializer.h | 6 +- .../KVBinaryCommon.h | 0 .../KVBinaryInputStreamSerializer.cpp | 82 +- .../KVBinaryInputStreamSerializer.h | 10 +- .../KVBinaryOutputStreamSerializer.cpp | 63 +- .../KVBinaryOutputStreamSerializer.h | 15 +- .../MemoryStream.cpp | 0 .../MemoryStream.h | 37 +- src/Serialization/SerializationOverloads.h | 252 +++ .../SerializationTools.h | 24 +- .../PasswordContainer.cpp} | 22 +- .../PasswordContainer.h} | 12 +- .../SimpleWallet.cpp} | 143 +- .../SimpleWallet.h} | 19 +- src/System/Context.h | 151 ++ src/System/ContextGroup.cpp | 99 ++ src/System/{Latch.h => ContextGroup.h} | 28 +- .../System/ContextGroupTimeout.cpp | 24 +- src/System/ContextGroupTimeout.h | 34 + src/System/Event.cpp | 39 +- src/System/EventLock.cpp | 0 src/System/EventLock.h | 1 + src/System/InterruptedException.cpp | 4 + src/System/InterruptedException.h | 4 + src/System/Ipv4Address.cpp | 10 +- src/System/Latch.cpp | 119 -- .../OperationTimeout.h} | 36 +- src/System/RemoteEventLock.cpp | 58 + src/System/RemoteEventLock.h | 35 + .../BlockchainSynchronizer.cpp | 82 +- .../BlockchainSynchronizer.h | 22 +- src/{transfers => Transfers}/CommonTypes.h | 6 +- .../IBlockchainSynchronizer.h | 14 +- .../IObservableImpl.h | 2 +- .../SynchronizationState.cpp | 43 +- .../SynchronizationState.h | 20 +- .../TransfersConsumer.cpp | 102 +- .../TransfersConsumer.h | 30 +- .../TransfersContainer.cpp | 80 +- .../TransfersContainer.h | 66 +- .../TransfersSubscription.cpp | 41 +- .../TransfersSubscription.h | 16 +- .../TransfersSynchronizer.cpp | 32 +- .../TransfersSynchronizer.h | 8 +- src/{transfers => Transfers}/TypeHelpers.h | 15 +- src/{wallet => Wallet}/LegacyKeysImporter.cpp | 198 +-- src/{wallet => Wallet}/LegacyKeysImporter.h | 0 .../WalletAsyncContextCounter.cpp | 0 .../WalletAsyncContextCounter.h | 0 src/{wallet => Wallet}/WalletErrors.cpp | 0 src/{wallet => Wallet}/WalletErrors.h | 4 +- src/Wallet/WalletGreen.cpp | 1218 +++++++++++++++ src/Wallet/WalletGreen.h | 211 +++ src/Wallet/WalletIndices.h | 124 ++ .../WalletRpcServer.cpp} | 61 +- .../WalletRpcServer.h} | 16 +- .../WalletRpcServerCommandsDefinitions.h} | 8 +- .../WalletRpcServerErrorCodes.h} | 0 src/Wallet/WalletSerialization.cpp | 873 +++++++++++ src/Wallet/WalletSerialization.h | 117 ++ src/{wallet => WalletLegacy}/KeysStorage.cpp | 8 +- src/{wallet => WalletLegacy}/KeysStorage.h | 9 +- src/{wallet => WalletLegacy}/WalletHelper.cpp | 24 +- src/{wallet => WalletLegacy}/WalletHelper.h | 22 +- .../WalletLegacy.cpp} | 218 ++- .../Wallet.h => WalletLegacy/WalletLegacy.h} | 60 +- .../WalletLegacyEvent.h} | 59 +- .../WalletLegacySerialization.cpp} | 36 +- .../WalletLegacySerialization.h} | 10 +- src/WalletLegacy/WalletLegacySerializer.cpp | 186 +++ .../WalletLegacySerializer.h} | 12 +- src/{wallet => WalletLegacy}/WalletRequest.h | 6 +- .../WalletSendTransactionContext.h | 4 +- .../WalletTransactionSender.cpp | 148 +- .../WalletTransactionSender.h | 45 +- .../WalletUnconfirmedTransactions.cpp | 27 +- .../WalletUnconfirmedTransactions.h | 34 +- .../WalletUserTransactionsCache.cpp | 104 +- .../WalletUserTransactionsCache.h | 30 +- src/{wallet => WalletLegacy}/WalletUtils.h | 8 +- src/crypto/chacha8.c | 0 src/crypto/chacha8.h | 13 +- src/crypto/crypto.cpp | 357 +++-- src/crypto/crypto.h | 213 +-- src/crypto/generic-ops.h | 16 +- src/crypto/groestl.c | 2 +- src/crypto/hash-ops.h | 0 src/crypto/hash.h | 30 +- src/crypto/skein.c | 4 +- src/crypto/skein_port.h | 0 src/crypto/slow-hash.c | 0 src/crypto/slow-hash.cpp | 2 +- src/cryptonote_core/BlockIndex.cpp | 87 -- src/cryptonote_core/ICore.h | 97 -- src/cryptonote_core/Transaction.cpp | 644 -------- src/cryptonote_core/blockchain_storage.h | 352 ----- .../blockchain_storage_boost_serialization.h | 47 - src/cryptonote_core/cryptonote_basic.cpp | 321 ---- src/cryptonote_core/cryptonote_basic.h | 451 ------ src/cryptonote_core/cryptonote_core.cpp | 622 -------- src/cryptonote_core/cryptonote_core.h | 166 -- .../cryptonote_format_utils.cpp | 754 --------- src/cryptonote_core/cryptonote_format_utils.h | 222 --- src/cryptonote_core/tx_extra.h | 164 -- src/cryptonote_protocol/blobdatatype.h | 39 - src/node_rpc_proxy/InitState.h | 88 -- src/node_rpc_proxy/NodeRpcProxy.cpp | 451 ------ src/node_rpc_proxy/NodeRpcProxy.h | 108 -- src/p2p/NetNodeConfig.h | 47 - src/p2p/net_node.h | 232 --- src/p2p/net_peerlist_boost_serialization.h | 41 - src/payment_service/WalletObservers.cpp | 73 - src/payment_service/WalletObservers.h | 72 - src/payment_service/WalletServiceErrorCodes.h | 60 - src/payment_service/main.cpp | 549 ------- src/serialization/SerializationOverloads.cpp | 81 - src/serialization/SerializationOverloads.h | 147 -- src/serialization/binary_archive.h | 173 --- src/serialization/binary_utils.h | 43 - src/serialization/crypto.h | 72 - src/serialization/json_archive.h | 154 -- src/serialization/serialization.h | 164 -- src/serialization/string.h | 48 - src/serialization/variant.h | 123 -- src/serialization/vector.h | 94 -- src/transfers/SerializationHelpers.h | 48 - src/version.h.in | 4 +- src/wallet/SyncWallet.cpp | 70 - src/wallet/SyncWallet.h | 49 - src/wallet/WalletSerializer.cpp | 179 --- tests/CMakeLists.txt | 39 +- .../CoreTests/AccountBoostSerialization.h | 16 +- .../BlockReward.cpp} | 27 +- .../BlockReward.h} | 2 +- .../BlockValidation.cpp} | 182 ++- .../BlockValidation.h} | 2 +- .../CoreTests/BoostSerializationHelper.h | 2 +- .../ChainSplit1.cpp} | 2 +- .../ChainSplit1.h} | 2 +- .../ChainSwitch1.cpp} | 14 +- .../ChainSwitch1.h} | 10 +- .../chaingen.cpp => CoreTests/Chaingen.cpp} | 122 +- .../chaingen.h => CoreTests/Chaingen.h} | 93 +- .../Chaingen001.cpp} | 6 +- .../chaingen001.h => CoreTests/Chaingen001.h} | 4 +- .../ChaingenMain.cpp} | 30 +- .../CoreTests/CryptoNoteBoostSerialization.h | 74 +- .../DoubleSpend.cpp} | 24 +- .../DoubleSpend.h} | 8 +- .../IntegerOverflow.cpp} | 60 +- .../IntegerOverflow.h} | 2 +- .../RandomOuts.cpp} | 4 +- .../random_outs.h => CoreTests/RandomOuts.h} | 2 +- .../RingSignature.cpp} | 22 +- .../RingSignature.h} | 14 +- .../{core_tests => CoreTests}/TestGenerator.h | 24 +- .../TransactionBuilder.cpp | 91 +- .../TransactionBuilder.h | 28 +- .../TransactionTests.cpp} | 95 +- .../TransactionTests.h} | 0 .../TransactionValidation.cpp} | 286 ++-- .../TransactionValidation.h} | 4 +- .../UnorderedContainersBoostSerialization.h | 0 .../upgrade.cpp => CoreTests/Upgrade.cpp} | 24 +- .../upgrade.h => CoreTests/Upgrade.h} | 6 +- .../double_spend.inl | 28 +- ...ec48bb477733d70ce5f9b85338a07cb10b849ad8fb | Bin ...73df5e5aaace00fe767c4f09de452838575357ca9f | Bin ...8fe4c0659f6959b2bebb15079cdaed07a442a78486 | Bin ...d2331715a631f5729db284eb1fc6f108aeb7a7f4fe | Bin .../Difficulty.cpp} | 6 +- tests/{difficulty => Difficulty}/data.txt | 0 .../{difficulty => Difficulty}/generate-data | 0 tests/{hash => Hash}/main.cpp | 17 +- tests/{hash => Hash}/tests-extra-blake.txt | 0 tests/{hash => Hash}/tests-extra-groestl.txt | 0 tests/{hash => Hash}/tests-extra-jh.txt | 0 tests/{hash => Hash}/tests-extra-skein.txt | 0 tests/{hash => Hash}/tests-fast.txt | 0 tests/{hash => Hash}/tests-slow.txt | 0 tests/{hash => Hash}/tests-tree.txt | 0 tests/{hash-target.cpp => HashTarget.cpp} | 8 +- .../BaseFunctionalTests.cpp} | 117 +- .../BaseFunctionalTests.h} | 30 +- .../InProcTestNode.cpp | 50 +- .../InProcTestNode.h | 14 +- .../Logger.cpp | 96 +- .../Logger.h | 144 +- .../NetworkConfiguration.h | 4 + tests/IntegrationTestLib/NodeCallback.h | 43 + .../NodeObserver.h | 14 +- tests/IntegrationTestLib/ObservableValue.h | 75 + .../Process.cpp | 0 .../Process.h | 0 .../RPCTestNode.cpp | 45 +- .../RPCTestNode.h | 106 +- .../TestNetwork.cpp | 48 +- .../TestNetwork.h | 2 + .../TestNode.h | 87 +- .../TestWalletLegacy.cpp} | 41 +- .../TestWalletLegacy.h} | 22 +- tests/IntegrationTests/BaseTests.h | 55 + .../IntegrationTests.cpp | 37 +- .../MultiVersion.cpp | 35 +- tests/IntegrationTests/Node.cpp | 380 +++++ .../WalletLegacyObserver.h} | 39 +- tests/IntegrationTests/WalletLegacyTests.cpp | 65 + .../main.cpp | 93 +- tests/{io.h => Io.h} | 0 .../NodeRpcProxyTests.cpp} | 6 +- .../CheckRingSignature.h} | 25 +- .../ConstructTransaction.h} | 16 +- .../CryptoNoteSlowHash.h} | 12 +- .../DerivePublicKey.h} | 14 +- .../DeriveSecretKey.h} | 14 +- .../GenerateKeyDerivation.h} | 8 +- .../GenerateKeyImage.h} | 18 +- .../GenerateKeyImageHelper.h} | 10 +- .../IsOutToAccount.h} | 10 +- .../MultiTransactionTestBase.h} | 36 +- .../PerformanceTests.h} | 0 .../PerformanceUtils.h} | 0 .../SingleTransactionTestBase.h} | 14 +- .../main.cpp | 23 +- tests/System/ContextGroupTests.cpp | 401 +++++ tests/System/ContextGroupTimeoutTests.cpp | 60 + tests/System/ContextTests.cpp | 121 ++ tests/System/DispatcherTests.cpp | 145 +- tests/System/EventLockTests.cpp | 9 +- tests/System/EventTests.cpp | 75 +- tests/System/Ipv4ResolverTests.cpp | 55 +- tests/System/OperationTimeoutTests.cpp | 56 + tests/System/TcpConnectionTests.cpp | 192 ++- tests/System/TcpConnectorTests.cpp | 73 +- tests/System/TcpListenerTests.cpp | 108 +- tests/System/TimerTests.cpp | 293 ++-- tests/TestGenerator/TestGenerator.cpp | 185 +-- tests/TestGenerator/TestGenerator.h | 48 +- .../globals.h => TransfersTests/Globals.h} | 10 +- tests/TransfersTests/TestNodeRpcProxy.cpp | 100 ++ .../TestTxPoolSync.cpp} | 352 ++--- .../tests.cpp => TransfersTests/Tests.cpp} | 232 +-- .../main.cpp | 4 +- .../ArrayRefTests.cpp | 2 +- .../ArrayViewTests.cpp | 2 +- .../base58.cpp => UnitTests/Base58.cpp} | 90 +- .../BinarySerializationCompatibility.cpp | 561 +++++++ .../BlockReward.cpp} | 4 +- .../BlockingQueue.cpp | 1 - .../chacha8.cpp => UnitTests/Chacha8.cpp} | 4 +- .../Checkpoints.cpp} | 8 +- .../DecomposeAmountIntoDigits.cpp} | 2 +- .../{unit_tests => UnitTests}/EventWaiter.cpp | 0 tests/{unit_tests => UnitTests}/EventWaiter.h | 0 tests/UnitTests/ICoreStub.cpp | 318 ++++ tests/UnitTests/ICoreStub.h | 125 ++ .../ICryptoNoteProtocolQueryStub.cpp} | 18 +- .../ICryptoNoteProtocolQueryStub.h} | 18 +- tests/UnitTests/INodeStubs.cpp | 693 +++++++++ tests/UnitTests/INodeStubs.h | 150 ++ .../mul_div.cpp => UnitTests/MulDiv.cpp} | 0 .../ParseAmount.cpp} | 4 +- tests/UnitTests/PaymentGateTests.cpp | 266 ++++ tests/UnitTests/Serialization.cpp | 468 ++++++ .../SerializationKV.cpp} | 26 +- .../shuffle.cpp => UnitTests/Shuffle.cpp} | 2 +- .../StringBufferTests.cpp | 0 .../StringViewTests.cpp | 2 +- .../test_BcS.cpp => UnitTests/TestBcS.cpp} | 409 +++-- tests/UnitTests/TestBlockchainExplorer.cpp | 1269 +++++++++++++++ .../TestBlockchainGenerator.cpp | 190 ++- tests/UnitTests/TestBlockchainGenerator.h | 101 ++ .../TestFormatUtils.cpp} | 107 +- .../TestInprocessNode.cpp} | 327 ++-- .../TestJsonValue.cpp} | 0 tests/UnitTests/TestMessageQueue.cpp | 292 ++++ .../test_path.cpp => UnitTests/TestPath.cpp} | 0 .../TestPeerlist.cpp} | 20 +- .../TestProtocolPack.cpp} | 8 +- .../TestTransactionPoolDetach.cpp} | 59 +- .../TestTransfers.cpp} | 36 +- .../TestTransfersConsumer.cpp} | 228 +-- .../TestTransfersContainer.cpp} | 569 +++---- .../TestTransfersContainerKeyImage.cpp} | 221 ++- .../TestTransfersSubscription.cpp} | 24 +- .../TestUpgradeDetector.cpp | 4 +- tests/UnitTests/TestWallet.cpp | 1141 ++++++++++++++ .../TestWalletLegacy.cpp} | 317 ++-- .../TransactionApi.cpp | 76 +- tests/UnitTests/TransactionApiHelpers.cpp | 227 +++ tests/UnitTests/TransactionApiHelpers.h | 178 +++ .../TransactionPool.cpp} | 80 +- .../TransfersObserver.h | 2 +- .../UnitTestsUtils.h} | 0 tests/{unit_tests => UnitTests}/main.cpp | 0 tests/crypto/crypto-tests.h | 10 +- tests/crypto/crypto.cpp | 28 +- tests/crypto/main.cpp | 105 +- tests/integration_tests/BlockchainInfo.h | 69 - tests/integration_tests/Node.cpp | 469 ------ tests/unit_tests/ICoreStub.cpp | 191 --- tests/unit_tests/ICoreStub.h | 100 -- tests/unit_tests/INodeStubs.cpp | 446 ------ tests/unit_tests/INodeStubs.h | 126 -- tests/unit_tests/TestBlockchainGenerator.h | 71 - tests/unit_tests/TransactionApiHelpers.h | 104 -- .../binary_serialization_compatibility.cpp | 535 ------- tests/unit_tests/serialization.cpp | 427 ----- .../serialization_structs_comparators.h | 138 -- tests/unit_tests/test_BlockchainExplorer.cpp | 922 ----------- 573 files changed, 30735 insertions(+), 21253 deletions(-) create mode 100644 include/CryptoNote.h rename src/serialization/json_utils.h => include/CryptoTypes.h (73%) delete mode 100755 include/IMultiWallet.h mode change 100644 => 100755 include/INode.h mode change 100644 => 100755 include/ITransaction.h mode change 100644 => 100755 include/IWallet.h create mode 100644 include/IWalletLegacy.h mode change 100644 => 100755 src/BlockchainExplorer/BlockchainExplorer.cpp mode change 100644 => 100755 src/BlockchainExplorer/BlockchainExplorer.h mode change 100644 => 100755 src/BlockchainExplorer/BlockchainExplorerDataBuilder.cpp mode change 100644 => 100755 src/BlockchainExplorer/BlockchainExplorerDataBuilder.h rename src/Common/{base58.cpp => Base58.cpp} (96%) rename src/Common/{base58.h => Base58.h} (96%) mode change 100644 => 100755 rename src/Common/{command_line.cpp => CommandLine.cpp} (97%) rename src/Common/{command_line.h => CommandLine.h} (100%) mode change 100644 => 100755 rename src/{CryptoNote/MultisignatureOutput.cpp => Common/MemoryInputStream.cpp} (53%) mode change 100755 => 100644 rename src/{CryptoNote/KeyOutput.h => Common/MemoryInputStream.h} (66%) mode change 100755 => 100644 mode change 100644 => 100755 src/Common/ObserverManager.h mode change 100644 => 100755 src/Common/SignalHandler.cpp mode change 100644 => 100755 src/Common/SignalHandler.h create mode 100644 src/Common/StdInputStream.cpp create mode 100644 src/Common/StdInputStream.h create mode 100644 src/Common/StdOutputStream.cpp create mode 100644 src/Common/StdOutputStream.h rename src/Common/{util.cpp => Util.cpp} (99%) rename src/Common/{util.h => Util.h} (94%) mode change 100644 => 100755 rename src/Common/{varint.h => Varint.h} (99%) mode change 100644 => 100755 create mode 100644 src/Common/VectorOutputStream.cpp create mode 100644 src/Common/VectorOutputStream.h mode change 100755 => 100644 src/Common/static_assert.h rename src/{connectivity_tool/conn_tool.cpp => ConnectivityTool/ConnectivityTool.cpp} (88%) mode change 100644 => 100755 delete mode 100755 src/CryptoNote/BaseTransaction.cpp delete mode 100755 src/CryptoNote/BaseTransaction.h delete mode 100755 src/CryptoNote/Block.cpp delete mode 100755 src/CryptoNote/Block.h delete mode 100755 src/CryptoNote/KeyInput.cpp delete mode 100755 src/CryptoNote/KeyInput.h delete mode 100755 src/CryptoNote/MultisignatureInput.cpp delete mode 100755 src/CryptoNote/MultisignatureInput.h delete mode 100755 src/CryptoNote/Transaction.cpp delete mode 100755 src/CryptoNote/Transaction.h delete mode 100755 src/CryptoNote/UnsignedMultisignatureInput.cpp delete mode 100755 src/CryptoNote/UnsignedTransaction.cpp delete mode 100755 src/CryptoNote/UnsignedTransaction.h rename src/{cryptonote_config.h => CryptoNoteConfig.h} (95%) rename src/{cryptonote_core/account.cpp => CryptoNoteCore/Account.cpp} (70%) mode change 100644 => 100755 rename src/{cryptonote_core/account.h => CryptoNoteCore/Account.h} (73%) mode change 100644 => 100755 create mode 100755 src/CryptoNoteCore/BlockIndex.cpp rename src/{cryptonote_core => CryptoNoteCore}/BlockIndex.h (69%) mode change 100644 => 100755 rename src/{cryptonote_core/blockchain_storage.cpp => CryptoNoteCore/Blockchain.cpp} (59%) create mode 100755 src/CryptoNoteCore/Blockchain.h create mode 100755 src/CryptoNoteCore/BlockchainIndices.cpp create mode 100755 src/CryptoNoteCore/BlockchainIndices.h create mode 100644 src/CryptoNoteCore/BlockchainMessages.cpp create mode 100644 src/CryptoNoteCore/BlockchainMessages.h rename src/{cryptonote_core/checkpoints.cpp => CryptoNoteCore/Checkpoints.cpp} (81%) rename src/{cryptonote_core/checkpoints.h => CryptoNoteCore/Checkpoints.h} (66%) mode change 100644 => 100755 create mode 100755 src/CryptoNoteCore/Core.cpp create mode 100755 src/CryptoNoteCore/Core.h rename src/{cryptonote_core => CryptoNoteCore}/CoreConfig.cpp (91%) mode change 100644 => 100755 rename src/{cryptonote_core => CryptoNoteCore}/CoreConfig.h (100%) rename src/{payment_service/WalletServiceErrorCodes.cpp => CryptoNoteCore/CryptoNoteBasic.cpp} (79%) mode change 100644 => 100755 create mode 100755 src/CryptoNoteCore/CryptoNoteBasic.h rename src/{cryptonote_core/cryptonote_basic_impl.cpp => CryptoNoteCore/CryptoNoteBasicImpl.cpp} (76%) rename src/{cryptonote_core/cryptonote_basic_impl.h => CryptoNoteCore/CryptoNoteBasicImpl.h} (68%) mode change 100644 => 100755 create mode 100644 src/CryptoNoteCore/CryptoNoteFormatUtils.cpp create mode 100755 src/CryptoNoteCore/CryptoNoteFormatUtils.h rename src/{cryptonote_core/cryptonote_serialization.cpp => CryptoNoteCore/CryptoNoteSerialization.cpp} (55%) rename src/{cryptonote_core/cryptonote_serialization.h => CryptoNoteCore/CryptoNoteSerialization.h} (55%) mode change 100644 => 100755 rename src/{cryptonote_core/cryptonote_stat_info.h => CryptoNoteCore/CryptoNoteStatInfo.h} (96%) mode change 100644 => 100755 create mode 100755 src/CryptoNoteCore/CryptoNoteTools.cpp create mode 100755 src/CryptoNoteCore/CryptoNoteTools.h rename src/{cryptonote_core => CryptoNoteCore}/Currency.cpp (86%) mode change 100644 => 100755 rename src/{cryptonote_core => CryptoNoteCore}/Currency.h (91%) mode change 100644 => 100755 rename src/{cryptonote_core/difficulty.cpp => CryptoNoteCore/Difficulty.cpp} (91%) mode change 100644 => 100755 rename src/{cryptonote_core/difficulty.h => CryptoNoteCore/Difficulty.h} (93%) mode change 100644 => 100755 rename src/{System/LatchGuard.cpp => CryptoNoteCore/IBlock.cpp} (79%) rename src/{CryptoNote/KeyOutput.cpp => CryptoNoteCore/IBlock.h} (75%) mode change 100755 => 100644 rename src/{cryptonote_core => CryptoNoteCore}/IBlockchainStorageObserver.h (100%) create mode 100755 src/CryptoNoteCore/ICore.h rename src/{cryptonote_core => CryptoNoteCore}/ICoreObserver.h (100%) rename src/{cryptonote_core/i_miner_handler.h => CryptoNoteCore/IMinerHandler.h} (86%) mode change 100644 => 100755 rename src/{cryptonote_core => CryptoNoteCore}/ITimeProvider.cpp (100%) rename src/{cryptonote_core => CryptoNoteCore}/ITimeProvider.h (100%) rename src/{cryptonote_core => CryptoNoteCore}/ITransactionValidator.h (85%) mode change 100644 => 100755 rename src/{cryptonote_core => CryptoNoteCore}/ITxPoolObserver.h (100%) create mode 100644 src/CryptoNoteCore/IntrusiveLinkedList.h create mode 100644 src/CryptoNoteCore/MessageQueue.h rename src/{cryptonote_core/miner.cpp => CryptoNoteCore/Miner.cpp} (90%) rename src/{cryptonote_core/miner.h => CryptoNoteCore/Miner.h} (79%) mode change 100644 => 100755 rename src/{cryptonote_core => CryptoNoteCore}/MinerConfig.cpp (97%) rename src/{cryptonote_core => CryptoNoteCore}/MinerConfig.h (97%) mode change 100644 => 100755 rename src/{cryptonote_core => CryptoNoteCore}/OnceInInterval.h (100%) rename src/{cryptonote_core => CryptoNoteCore}/SwappedMap.cpp (94%) rename src/{cryptonote_core => CryptoNoteCore}/SwappedMap.h (97%) rename src/{cryptonote_core => CryptoNoteCore}/SwappedVector.cpp (94%) rename src/{cryptonote_core => CryptoNoteCore}/SwappedVector.h (93%) create mode 100755 src/CryptoNoteCore/Transaction.cpp rename src/{cryptonote_core => CryptoNoteCore}/TransactionApi.h (69%) mode change 100644 => 100755 rename src/{cryptonote_core/TransactionExtra.h => CryptoNoteCore/TransactionApiExtra.h} (63%) mode change 100644 => 100755 create mode 100755 src/CryptoNoteCore/TransactionExtra.cpp create mode 100755 src/CryptoNoteCore/TransactionExtra.h rename src/{cryptonote_core/tx_pool.cpp => CryptoNoteCore/TransactionPool.cpp} (74%) rename src/{cryptonote_core/tx_pool.h => CryptoNoteCore/TransactionPool.h} (72%) mode change 100644 => 100755 create mode 100755 src/CryptoNoteCore/TransactionPrefixImpl.cpp create mode 100755 src/CryptoNoteCore/TransactionUtils.cpp create mode 100755 src/CryptoNoteCore/TransactionUtils.h rename src/{cryptonote_core => CryptoNoteCore}/UpgradeDetector.cpp (100%) rename src/{cryptonote_core => CryptoNoteCore}/UpgradeDetector.h (99%) mode change 100644 => 100755 rename src/{cryptonote_core/verification_context.h => CryptoNoteCore/VerificationContext.h} (100%) mode change 100644 => 100755 rename src/{cryptonote_protocol/cryptonote_protocol_defs.h => CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h} (77%) mode change 100644 => 100755 rename src/{cryptonote_protocol/cryptonote_protocol_handler.cpp => CryptoNoteProtocol/CryptoNoteProtocolHandler.cpp} (64%) rename src/{cryptonote_protocol/cryptonote_protocol_handler.h => CryptoNoteProtocol/CryptoNoteProtocolHandler.h} (51%) mode change 100644 => 100755 rename src/{cryptonote_protocol/cryptonote_protocol_handler_common.h => CryptoNoteProtocol/CryptoNoteProtocolHandlerCommon.h} (100%) mode change 100644 => 100755 rename src/{cryptonote_protocol/ICryptonoteProtocolObserver.h => CryptoNoteProtocol/ICryptoNoteProtocolObserver.h} (85%) mode change 100644 => 100755 rename src/{cryptonote_protocol/ICryptonoteProtocolQuery.h => CryptoNoteProtocol/ICryptoNoteProtocolQuery.h} (80%) mode change 100644 => 100755 rename src/{daemon/daemon.cpp => Daemon/Daemon.cpp} (92%) mode change 100644 => 100755 rename src/{daemon/DeamonCommandsHandler.cpp => Daemon/DaemonCommandsHandler.cpp} (90%) mode change 100644 => 100755 rename src/{daemon => Daemon}/DaemonCommandsHandler.h (91%) mode change 100644 => 100755 mode change 100644 => 100755 src/HTTP/HttpParserErrorCodes.cpp mode change 100644 => 100755 src/HTTP/HttpParserErrorCodes.h create mode 100755 src/JsonRpcServer/JsonRpcServer.cpp rename src/{payment_service => JsonRpcServer}/JsonRpcServer.h (61%) mode change 100644 => 100755 rename src/{node_rpc_proxy => NodeRpcProxy}/NodeErrors.cpp (100%) rename src/{node_rpc_proxy => NodeRpcProxy}/NodeErrors.h (100%) create mode 100644 src/NodeRpcProxy/NodeRpcProxy.cpp create mode 100644 src/NodeRpcProxy/NodeRpcProxy.h rename src/{p2p/connection_context.h => P2p/ConnectionContext.h} (72%) mode change 100644 => 100755 create mode 100644 src/P2p/IP2pNodeInternal.cpp rename src/{CryptoNote/UnsignedKeyInput.h => P2p/IP2pNodeInternal.h} (60%) mode change 100755 => 100644 rename src/{p2p => P2p}/LevinProtocol.cpp (73%) rename src/{p2p => P2p}/LevinProtocol.h (60%) mode change 100644 => 100755 rename src/{p2p/net_node.cpp => P2p/NetNode.cpp} (64%) create mode 100644 src/P2p/NetNode.h rename src/{p2p/net_node_common.h => P2p/NetNodeCommon.h} (52%) mode change 100644 => 100755 rename src/{p2p => P2p}/NetNodeConfig.cpp (60%) mode change 100644 => 100755 create mode 100755 src/P2p/NetNodeConfig.h create mode 100644 src/P2p/P2pConnectionProxy.cpp create mode 100644 src/P2p/P2pConnectionProxy.h create mode 100755 src/P2p/P2pContext.cpp create mode 100755 src/P2p/P2pContext.h rename src/{CryptoNote/UnsignedKeyInput.cpp => P2p/P2pContextOwner.cpp} (52%) mode change 100755 => 100644 rename src/{wallet/MultiWallet.h => P2p/P2pContextOwner.h} (65%) rename src/{System/LatchGuard.h => P2p/P2pInterfaces.cpp} (83%) rename src/{CryptoNote/UnsignedMultisignatureInput.h => P2p/P2pInterfaces.h} (66%) mode change 100755 => 100644 rename src/{p2p/p2p_networks.h => P2p/P2pNetworks.h} (100%) mode change 100644 => 100755 create mode 100755 src/P2p/P2pNode.cpp create mode 100755 src/P2p/P2pNode.h create mode 100644 src/P2p/P2pNodeConfig.cpp create mode 100644 src/P2p/P2pNodeConfig.h rename src/{p2p/p2p_protocol_defs.h => P2p/P2pProtocolDefinitions.h} (85%) mode change 100644 => 100755 rename src/{p2p/p2p_protocol_types.h => P2p/P2pProtocolTypes.h} (78%) mode change 100644 => 100755 rename src/{p2p => P2p}/PeerListManager.cpp (59%) mode change 100644 => 100755 rename src/{p2p => P2p}/PeerListManager.h (53%) rename src/{payment_service => PaymentGate}/NodeFactory.cpp (51%) rename src/{payment_service => PaymentGate}/NodeFactory.h (100%) rename src/{payment_service/JsonRpcMessages.cpp => PaymentGate/PaymentServiceJsonRpcMessages.cpp} (79%) mode change 100644 => 100755 rename src/{payment_service/JsonRpcMessages.h => PaymentGate/PaymentServiceJsonRpcMessages.h} (82%) rename src/{payment_service/JsonRpcServer.cpp => PaymentGate/PaymentServiceJsonRpcServer.cpp} (56%) mode change 100644 => 100755 create mode 100644 src/PaymentGate/PaymentServiceJsonRpcServer.h rename src/{payment_service => PaymentGate}/WalletFactory.cpp (79%) mode change 100644 => 100755 rename src/{payment_service => PaymentGate}/WalletFactory.h (91%) rename src/{payment_service => PaymentGate}/WalletService.cpp (52%) mode change 100644 => 100755 rename src/{payment_service => PaymentGate}/WalletService.h (58%) mode change 100644 => 100755 rename src/{payment_service => PaymentGateService}/ConfigurationManager.cpp (96%) mode change 100644 => 100755 rename src/{payment_service => PaymentGateService}/ConfigurationManager.h (94%) mode change 100644 => 100755 create mode 100755 src/PaymentGateService/PaymentGateService.cpp create mode 100644 src/PaymentGateService/PaymentGateService.h rename src/{payment_service => PaymentGateService}/PaymentServiceConfiguration.cpp (89%) rename src/{payment_service => PaymentGateService}/PaymentServiceConfiguration.h (96%) rename src/{payment_service => PaymentGateService}/RpcNodeConfiguration.cpp (100%) rename src/{payment_service => PaymentGateService}/RpcNodeConfiguration.h (100%) create mode 100644 src/PaymentGateService/main.cpp rename src/Platform/OSX/System/{context.c => Context.c} (100%) mode change 100644 => 100755 rename src/Platform/OSX/System/{context.h => Context.h} (100%) mode change 100644 => 100755 rename src/{rpc/core_rpc_server_commands_defs.h => Rpc/CoreRpcServerCommandsDefinitions.h} (77%) mode change 100644 => 100755 rename src/{rpc/core_rpc_server_error_codes.h => Rpc/CoreRpcServerErrorCodes.h} (100%) mode change 100644 => 100755 rename src/{rpc => Rpc}/HttpClient.cpp (84%) rename src/{rpc => Rpc}/HttpClient.h (97%) mode change 100644 => 100755 rename src/{rpc => Rpc}/HttpServer.cpp (83%) mode change 100644 => 100755 rename src/{rpc => Rpc}/HttpServer.h (95%) mode change 100644 => 100755 rename src/{rpc => Rpc}/JsonRpc.cpp (98%) mode change 100644 => 100755 rename src/{rpc => Rpc}/JsonRpc.h (98%) mode change 100644 => 100755 rename src/{rpc => Rpc}/RpcServer.cpp (78%) mode change 100644 => 100755 rename src/{rpc => Rpc}/RpcServer.h (88%) mode change 100644 => 100755 rename src/{rpc => Rpc}/RpcServerConfig.cpp (96%) mode change 100644 => 100755 rename src/{rpc => Rpc}/RpcServerConfig.h (100%) rename src/{serialization => Serialization}/BinaryInputStreamSerializer.cpp (72%) rename src/{serialization => Serialization}/BinaryInputStreamSerializer.h (81%) rename src/{serialization => Serialization}/BinaryOutputStreamSerializer.cpp (82%) rename src/{serialization => Serialization}/BinaryOutputStreamSerializer.h (81%) create mode 100644 src/Serialization/BinarySerializationTools.h rename src/{serialization => Serialization}/ISerializer.h (83%) rename src/{serialization => Serialization}/IStream.h (88%) rename src/{serialization => Serialization}/JsonInputStreamSerializer.cpp (77%) rename src/{serialization => Serialization}/JsonInputStreamSerializer.h (96%) rename src/{serialization => Serialization}/JsonInputValueSerializer.cpp (79%) rename src/{serialization => Serialization}/JsonInputValueSerializer.h (84%) rename src/{serialization => Serialization}/JsonOutputStreamSerializer.cpp (88%) rename src/{serialization => Serialization}/JsonOutputStreamSerializer.h (87%) rename src/{serialization => Serialization}/KVBinaryCommon.h (100%) rename src/{serialization => Serialization}/KVBinaryInputStreamSerializer.cpp (69%) rename src/{serialization => Serialization}/KVBinaryInputStreamSerializer.h (84%) rename src/{serialization => Serialization}/KVBinaryOutputStreamSerializer.cpp (84%) rename src/{serialization => Serialization}/KVBinaryOutputStreamSerializer.h (87%) rename src/{serialization => Serialization}/MemoryStream.cpp (100%) rename src/{serialization => Serialization}/MemoryStream.h (68%) create mode 100644 src/Serialization/SerializationOverloads.h rename src/{serialization => Serialization}/SerializationTools.h (91%) rename src/{simplewallet/password_container.cpp => SimpleWallet/PasswordContainer.cpp} (90%) rename src/{simplewallet/password_container.h => SimpleWallet/PasswordContainer.h} (86%) mode change 100644 => 100755 rename src/{simplewallet/simplewallet.cpp => SimpleWallet/SimpleWallet.cpp} (91%) rename src/{simplewallet/simplewallet.h => SimpleWallet/SimpleWallet.h} (94%) mode change 100644 => 100755 create mode 100755 src/System/Context.h create mode 100755 src/System/ContextGroup.cpp rename src/System/{Latch.h => ContextGroup.h} (67%) rename tests/System/LatchTests.cpp => src/System/ContextGroupTimeout.cpp (64%) create mode 100755 src/System/ContextGroupTimeout.h mode change 100644 => 100755 src/System/EventLock.cpp mode change 100644 => 100755 src/System/EventLock.h delete mode 100755 src/System/Latch.cpp rename src/{CryptoNote/MultisignatureOutput.h => System/OperationTimeout.h} (55%) create mode 100755 src/System/RemoteEventLock.cpp create mode 100755 src/System/RemoteEventLock.h rename src/{transfers => Transfers}/BlockchainSynchronizer.cpp (89%) mode change 100644 => 100755 rename src/{transfers => Transfers}/BlockchainSynchronizer.h (88%) mode change 100644 => 100755 rename src/{transfers => Transfers}/CommonTypes.h (93%) mode change 100644 => 100755 rename src/{transfers => Transfers}/IBlockchainSynchronizer.h (74%) mode change 100644 => 100755 rename src/{transfers => Transfers}/IObservableImpl.h (95%) mode change 100644 => 100755 rename src/{transfers => Transfers}/SynchronizationState.cpp (67%) mode change 100644 => 100755 rename src/{transfers => Transfers}/SynchronizationState.h (75%) mode change 100644 => 100755 rename src/{transfers => Transfers}/TransfersConsumer.cpp (81%) mode change 100644 => 100755 rename src/{transfers => Transfers}/TransfersConsumer.h (66%) mode change 100644 => 100755 rename src/{transfers => Transfers}/TransfersContainer.cpp (92%) mode change 100644 => 100755 rename src/{transfers => Transfers}/TransfersContainer.h (80%) mode change 100644 => 100755 rename src/{transfers => Transfers}/TransfersSubscription.cpp (69%) mode change 100644 => 100755 rename src/{transfers => Transfers}/TransfersSubscription.h (75%) mode change 100644 => 100755 rename src/{transfers => Transfers}/TransfersSynchronizer.cpp (88%) mode change 100644 => 100755 rename src/{transfers => Transfers}/TransfersSynchronizer.h (83%) rename src/{transfers => Transfers}/TypeHelpers.h (71%) rename src/{wallet => Wallet}/LegacyKeysImporter.cpp (60%) rename src/{wallet => Wallet}/LegacyKeysImporter.h (100%) rename src/{wallet => Wallet}/WalletAsyncContextCounter.cpp (100%) rename src/{wallet => Wallet}/WalletAsyncContextCounter.h (100%) rename src/{wallet => Wallet}/WalletErrors.cpp (100%) rename src/{wallet => Wallet}/WalletErrors.h (96%) create mode 100755 src/Wallet/WalletGreen.cpp create mode 100755 src/Wallet/WalletGreen.h create mode 100644 src/Wallet/WalletIndices.h rename src/{wallet/wallet_rpc_server.cpp => Wallet/WalletRpcServer.cpp} (87%) rename src/{wallet/wallet_rpc_server.h => Wallet/WalletRpcServer.h} (92%) mode change 100644 => 100755 rename src/{wallet/wallet_rpc_server_commans_defs.h => Wallet/WalletRpcServerCommandsDefinitions.h} (95%) mode change 100644 => 100755 rename src/{wallet/wallet_rpc_server_error_codes.h => Wallet/WalletRpcServerErrorCodes.h} (100%) mode change 100644 => 100755 create mode 100755 src/Wallet/WalletSerialization.cpp create mode 100755 src/Wallet/WalletSerialization.h rename src/{wallet => WalletLegacy}/KeysStorage.cpp (86%) mode change 100644 => 100755 rename src/{wallet => WalletLegacy}/KeysStorage.h (84%) rename src/{wallet => WalletLegacy}/WalletHelper.cpp (86%) rename src/{wallet => WalletLegacy}/WalletHelper.h (75%) rename src/{wallet/Wallet.cpp => WalletLegacy/WalletLegacy.cpp} (59%) rename src/{wallet/Wallet.h => WalletLegacy/WalletLegacy.h} (62%) mode change 100644 => 100755 rename src/{wallet/WalletEvent.h => WalletLegacy/WalletLegacyEvent.h} (50%) mode change 100644 => 100755 rename src/{wallet/WalletSerialization.cpp => WalletLegacy/WalletLegacySerialization.cpp} (67%) mode change 100644 => 100755 rename src/{wallet/WalletSerialization.h => WalletLegacy/WalletLegacySerialization.h} (81%) create mode 100755 src/WalletLegacy/WalletLegacySerializer.cpp rename src/{wallet/WalletSerializer.h => WalletLegacy/WalletLegacySerializer.h} (82%) mode change 100644 => 100755 rename src/{wallet => WalletLegacy}/WalletRequest.h (89%) rename src/{wallet => WalletLegacy}/WalletSendTransactionContext.h (95%) mode change 100644 => 100755 rename src/{wallet => WalletLegacy}/WalletTransactionSender.cpp (62%) rename src/{wallet => WalletLegacy}/WalletTransactionSender.h (56%) mode change 100644 => 100755 rename src/{wallet => WalletLegacy}/WalletUnconfirmedTransactions.cpp (77%) mode change 100644 => 100755 rename src/{wallet => WalletLegacy}/WalletUnconfirmedTransactions.h (67%) mode change 100644 => 100755 rename src/{wallet => WalletLegacy}/WalletUserTransactionsCache.cpp (70%) mode change 100644 => 100755 rename src/{wallet => WalletLegacy}/WalletUserTransactionsCache.h (66%) mode change 100644 => 100755 rename src/{wallet => WalletLegacy}/WalletUtils.h (88%) mode change 100644 => 100755 mode change 100644 => 100755 src/crypto/chacha8.c mode change 100644 => 100755 src/crypto/crypto.cpp mode change 100644 => 100755 src/crypto/crypto.h mode change 100644 => 100755 src/crypto/hash-ops.h mode change 100644 => 100755 src/crypto/skein_port.h mode change 100644 => 100755 src/crypto/slow-hash.c delete mode 100644 src/cryptonote_core/BlockIndex.cpp delete mode 100755 src/cryptonote_core/ICore.h delete mode 100644 src/cryptonote_core/Transaction.cpp delete mode 100644 src/cryptonote_core/blockchain_storage.h delete mode 100644 src/cryptonote_core/blockchain_storage_boost_serialization.h delete mode 100755 src/cryptonote_core/cryptonote_basic.cpp delete mode 100644 src/cryptonote_core/cryptonote_basic.h delete mode 100644 src/cryptonote_core/cryptonote_core.cpp delete mode 100644 src/cryptonote_core/cryptonote_core.h delete mode 100644 src/cryptonote_core/cryptonote_format_utils.cpp delete mode 100644 src/cryptonote_core/cryptonote_format_utils.h delete mode 100644 src/cryptonote_core/tx_extra.h delete mode 100644 src/cryptonote_protocol/blobdatatype.h delete mode 100644 src/node_rpc_proxy/InitState.h delete mode 100644 src/node_rpc_proxy/NodeRpcProxy.cpp delete mode 100644 src/node_rpc_proxy/NodeRpcProxy.h delete mode 100644 src/p2p/NetNodeConfig.h delete mode 100644 src/p2p/net_node.h delete mode 100644 src/p2p/net_peerlist_boost_serialization.h delete mode 100644 src/payment_service/WalletObservers.cpp delete mode 100644 src/payment_service/WalletObservers.h delete mode 100644 src/payment_service/WalletServiceErrorCodes.h delete mode 100644 src/payment_service/main.cpp delete mode 100644 src/serialization/SerializationOverloads.cpp delete mode 100644 src/serialization/SerializationOverloads.h delete mode 100644 src/serialization/binary_archive.h delete mode 100644 src/serialization/binary_utils.h delete mode 100644 src/serialization/crypto.h delete mode 100644 src/serialization/json_archive.h delete mode 100644 src/serialization/serialization.h delete mode 100644 src/serialization/string.h delete mode 100644 src/serialization/variant.h delete mode 100644 src/serialization/vector.h delete mode 100644 src/transfers/SerializationHelpers.h delete mode 100644 src/wallet/SyncWallet.cpp delete mode 100644 src/wallet/SyncWallet.h delete mode 100644 src/wallet/WalletSerializer.cpp rename src/cryptonote_core/account_boost_serialization.h => tests/CoreTests/AccountBoostSerialization.h (75%) mode change 100644 => 100755 rename tests/{core_tests/block_reward.cpp => CoreTests/BlockReward.cpp} (91%) rename tests/{core_tests/block_reward.h => CoreTests/BlockReward.h} (98%) mode change 100644 => 100755 rename tests/{core_tests/block_validation.cpp => CoreTests/BlockValidation.cpp} (80%) rename tests/{core_tests/block_validation.h => CoreTests/BlockValidation.h} (99%) mode change 100644 => 100755 rename src/Common/boost_serialization_helper.h => tests/CoreTests/BoostSerializationHelper.h (99%) mode change 100644 => 100755 rename tests/{core_tests/chain_split_1.cpp => CoreTests/ChainSplit1.cpp} (99%) rename tests/{core_tests/chain_split_1.h => CoreTests/ChainSplit1.h} (99%) mode change 100644 => 100755 rename tests/{core_tests/chain_switch_1.cpp => CoreTests/ChainSwitch1.cpp} (94%) rename tests/{core_tests/chain_switch_1.h => CoreTests/ChainSwitch1.h} (87%) mode change 100644 => 100755 rename tests/{core_tests/chaingen.cpp => CoreTests/Chaingen.cpp} (72%) rename tests/{core_tests/chaingen.h => CoreTests/Chaingen.h} (91%) mode change 100644 => 100755 rename tests/{core_tests/chaingen001.cpp => CoreTests/Chaingen001.cpp} (97%) rename tests/{core_tests/chaingen001.h => CoreTests/Chaingen001.h} (96%) mode change 100644 => 100755 rename tests/{core_tests/chaingen_main.cpp => CoreTests/ChaingenMain.cpp} (96%) rename src/cryptonote_core/cryptonote_boost_serialization.h => tests/CoreTests/CryptoNoteBoostSerialization.h (53%) mode change 100644 => 100755 rename tests/{core_tests/double_spend.cpp => CoreTests/DoubleSpend.cpp} (94%) rename tests/{core_tests/double_spend.h => CoreTests/DoubleSpend.h} (98%) mode change 100644 => 100755 rename tests/{core_tests/integer_overflow.cpp => CoreTests/IntegerOverflow.cpp} (74%) rename tests/{core_tests/integer_overflow.h => CoreTests/IntegerOverflow.h} (98%) mode change 100644 => 100755 rename tests/{core_tests/random_outs.cpp => CoreTests/RandomOuts.cpp} (98%) rename tests/{core_tests/random_outs.h => CoreTests/RandomOuts.h} (98%) mode change 100644 => 100755 rename tests/{core_tests/ring_signature_1.cpp => CoreTests/RingSignature.cpp} (94%) rename tests/{core_tests/ring_signature_1.h => CoreTests/RingSignature.h} (90%) mode change 100644 => 100755 rename tests/{core_tests => CoreTests}/TestGenerator.h (80%) rename tests/{core_tests => CoreTests}/TransactionBuilder.cpp (57%) rename tests/{core_tests => CoreTests}/TransactionBuilder.h (69%) rename tests/{core_tests/transaction_tests.cpp => CoreTests/TransactionTests.cpp} (54%) rename tests/{core_tests/transaction_tests.h => CoreTests/TransactionTests.h} (100%) mode change 100644 => 100755 rename tests/{core_tests/tx_validation.cpp => CoreTests/TransactionValidation.cpp} (72%) rename tests/{core_tests/tx_validation.h => CoreTests/TransactionValidation.h} (98%) mode change 100644 => 100755 rename src/Common/unordered_containers_boost_serialization.h => tests/CoreTests/UnorderedContainersBoostSerialization.h (100%) mode change 100644 => 100755 rename tests/{core_tests/upgrade.cpp => CoreTests/Upgrade.cpp} (91%) rename tests/{core_tests/upgrade.h => CoreTests/Upgrade.h} (94%) mode change 100644 => 100755 rename tests/{core_tests => CoreTests}/double_spend.inl (91%) rename tests/{data => Data}/account-002bee2f8e16f5de4db0d3b8ce9227c8c0b7f9688348b028e022cb43f210968b40a69cdc8531fd4a2e7c9e144eec48bb477733d70ce5f9b85338a07cb10b849ad8fb (100%) rename tests/{data => Data}/account-007af2d7c5ffd8f69005debae820207820805e28c7d7a16714591143f56fb51e2b91ad0c1a535567e6292b321773df5e5aaace00fe767c4f09de452838575357ca9f (100%) rename tests/{data => Data}/account-009b82d66dfaaba55a581913fa09d6c5bebe179cd73731781265c96e9e630dcd27fd5d20e7f1d0fa42619de9ca8fe4c0659f6959b2bebb15079cdaed07a442a78486 (100%) rename tests/{data => Data}/account-00aff84db50d6a54dd56051379f6c336fdd330d1cb11e7523bbf71f30b1ae760fa47ace8679b6486f79429980fd2331715a631f5729db284eb1fc6f108aeb7a7f4fe (100%) rename tests/{difficulty/difficulty.cpp => Difficulty/Difficulty.cpp} (96%) mode change 100644 => 100755 rename tests/{difficulty => Difficulty}/data.txt (100%) rename tests/{difficulty => Difficulty}/generate-data (100%) rename tests/{hash => Hash}/main.cpp (87%) rename tests/{hash => Hash}/tests-extra-blake.txt (100%) rename tests/{hash => Hash}/tests-extra-groestl.txt (100%) rename tests/{hash => Hash}/tests-extra-jh.txt (100%) rename tests/{hash => Hash}/tests-extra-skein.txt (100%) rename tests/{hash => Hash}/tests-fast.txt (100%) rename tests/{hash => Hash}/tests-slow.txt (100%) rename tests/{hash => Hash}/tests-tree.txt (100%) rename tests/{hash-target.cpp => HashTarget.cpp} (93%) mode change 100644 => 100755 rename tests/{integration_test_lib/BaseFunctionalTest.cpp => IntegrationTestLib/BaseFunctionalTests.cpp} (81%) rename tests/{integration_test_lib/BaseFunctionalTest.h => IntegrationTestLib/BaseFunctionalTests.h} (84%) rename tests/{integration_test_lib => IntegrationTestLib}/InProcTestNode.cpp (78%) rename tests/{integration_test_lib => IntegrationTestLib}/InProcTestNode.h (82%) rename tests/{integration_test_lib => IntegrationTestLib}/Logger.cpp (97%) rename tests/{integration_test_lib => IntegrationTestLib}/Logger.h (96%) rename tests/{integration_test_lib => IntegrationTestLib}/NetworkConfiguration.h (93%) create mode 100644 tests/IntegrationTestLib/NodeCallback.h rename tests/{integration_test_lib => IntegrationTestLib}/NodeObserver.h (89%) create mode 100644 tests/IntegrationTestLib/ObservableValue.h rename tests/{integration_test_lib => IntegrationTestLib}/Process.cpp (100%) rename tests/{integration_test_lib => IntegrationTestLib}/Process.h (100%) rename tests/{integration_test_lib => IntegrationTestLib}/RPCTestNode.cpp (83%) rename tests/{integration_test_lib => IntegrationTestLib}/RPCTestNode.h (53%) rename tests/{integration_test_lib => IntegrationTestLib}/TestNetwork.cpp (89%) rename tests/{integration_test_lib => IntegrationTestLib}/TestNetwork.h (95%) rename tests/{integration_test_lib => IntegrationTestLib}/TestNode.h (66%) rename tests/{integration_test_lib/TestWallet.cpp => IntegrationTestLib/TestWalletLegacy.cpp} (70%) rename tests/{integration_test_lib/TestWallet.h => IntegrationTestLib/TestWalletLegacy.h} (71%) create mode 100644 tests/IntegrationTests/BaseTests.h rename tests/{integration_tests => IntegrationTests}/IntegrationTests.cpp (85%) rename tests/{integration_tests => IntegrationTests}/MultiVersion.cpp (88%) create mode 100644 tests/IntegrationTests/Node.cpp rename tests/{integration_tests/WalletObserver.h => IntegrationTests/WalletLegacyObserver.h} (84%) create mode 100644 tests/IntegrationTests/WalletLegacyTests.cpp rename tests/{integration_tests => IntegrationTests}/main.cpp (94%) rename tests/{io.h => Io.h} (100%) mode change 100644 => 100755 rename tests/{node_rpc_proxy_test/node_rpc_proxy_test.cpp => NodeRpcProxyTests/NodeRpcProxyTests.cpp} (96%) mode change 100644 => 100755 rename tests/{performance_tests/check_ring_signature.h => PerformanceTests/CheckRingSignature.h} (63%) mode change 100644 => 100755 rename tests/{performance_tests/construct_tx.h => PerformanceTests/ConstructTransaction.h} (72%) mode change 100644 => 100755 rename tests/{performance_tests/cn_slow_hash.h => PerformanceTests/CryptoNoteSlowHash.h} (87%) mode change 100644 => 100755 rename tests/{performance_tests/derive_public_key.h => PerformanceTests/DerivePublicKey.h} (71%) mode change 100644 => 100755 rename tests/{performance_tests/derive_secret_key.h => PerformanceTests/DeriveSecretKey.h} (72%) mode change 100644 => 100755 rename tests/{performance_tests/generate_key_derivation.h => PerformanceTests/GenerateKeyDerivation.h} (80%) mode change 100644 => 100755 rename tests/{performance_tests/generate_key_image.h => PerformanceTests/GenerateKeyImage.h} (66%) mode change 100644 => 100755 rename tests/{performance_tests/generate_key_image_helper.h => PerformanceTests/GenerateKeyImageHelper.h} (78%) mode change 100644 => 100755 rename tests/{performance_tests/is_out_to_acc.h => PerformanceTests/IsOutToAccount.h} (73%) mode change 100644 => 100755 rename tests/{performance_tests/multi_tx_test_base.h => PerformanceTests/MultiTransactionTestBase.h} (64%) mode change 100644 => 100755 rename tests/{performance_tests/performance_tests.h => PerformanceTests/PerformanceTests.h} (100%) mode change 100644 => 100755 rename tests/{performance_tests/performance_utils.h => PerformanceTests/PerformanceUtils.h} (100%) mode change 100644 => 100755 rename tests/{performance_tests/single_tx_test_base.h => PerformanceTests/SingleTransactionTestBase.h} (76%) mode change 100644 => 100755 rename tests/{performance_tests => PerformanceTests}/main.cpp (86%) create mode 100755 tests/System/ContextGroupTests.cpp create mode 100644 tests/System/ContextGroupTimeoutTests.cpp create mode 100755 tests/System/ContextTests.cpp create mode 100644 tests/System/OperationTimeoutTests.cpp rename tests/{transfers_tests/globals.h => TransfersTests/Globals.h} (77%) mode change 100644 => 100755 create mode 100755 tests/TransfersTests/TestNodeRpcProxy.cpp rename tests/{transfers_tests/test_TxPoolSync.cpp => TransfersTests/TestTxPoolSync.cpp} (56%) mode change 100644 => 100755 rename tests/{transfers_tests/tests.cpp => TransfersTests/Tests.cpp} (68%) rename tests/{transfers_tests => TransfersTests}/main.cpp (95%) rename tests/{unit_tests => UnitTests}/ArrayRefTests.cpp (99%) rename tests/{unit_tests => UnitTests}/ArrayViewTests.cpp (99%) rename tests/{unit_tests/base58.cpp => UnitTests/Base58.cpp} (90%) mode change 100644 => 100755 create mode 100755 tests/UnitTests/BinarySerializationCompatibility.cpp rename tests/{unit_tests/block_reward.cpp => UnitTests/BlockReward.cpp} (99%) mode change 100644 => 100755 rename tests/{unit_tests => UnitTests}/BlockingQueue.cpp (99%) rename tests/{unit_tests/chacha8.cpp => UnitTests/Chacha8.cpp} (97%) mode change 100644 => 100755 rename tests/{unit_tests/checkpoints.cpp => UnitTests/Checkpoints.cpp} (98%) mode change 100644 => 100755 rename tests/{unit_tests/decompose_amount_into_digits.cpp => UnitTests/DecomposeAmountIntoDigits.cpp} (98%) mode change 100644 => 100755 rename tests/{unit_tests => UnitTests}/EventWaiter.cpp (100%) rename tests/{unit_tests => UnitTests}/EventWaiter.h (100%) create mode 100755 tests/UnitTests/ICoreStub.cpp create mode 100644 tests/UnitTests/ICoreStub.h rename tests/{unit_tests/ICryptonoteProtocolQueryStub.cpp => UnitTests/ICryptoNoteProtocolQueryStub.cpp} (61%) rename tests/{unit_tests/ICryptonoteProtocolQueryStub.h => UnitTests/ICryptoNoteProtocolQueryStub.h} (69%) create mode 100644 tests/UnitTests/INodeStubs.cpp create mode 100644 tests/UnitTests/INodeStubs.h rename tests/{unit_tests/mul_div.cpp => UnitTests/MulDiv.cpp} (100%) mode change 100644 => 100755 rename tests/{unit_tests/parse_amount.cpp => UnitTests/ParseAmount.cpp} (97%) mode change 100644 => 100755 create mode 100644 tests/UnitTests/PaymentGateTests.cpp create mode 100755 tests/UnitTests/Serialization.cpp rename tests/{unit_tests/serialization_kv.cpp => UnitTests/SerializationKV.cpp} (81%) mode change 100644 => 100755 rename tests/{unit_tests/shuffle.cpp => UnitTests/Shuffle.cpp} (97%) mode change 100644 => 100755 rename tests/{unit_tests => UnitTests}/StringBufferTests.cpp (100%) rename tests/{unit_tests => UnitTests}/StringViewTests.cpp (99%) rename tests/{unit_tests/test_BcS.cpp => UnitTests/TestBcS.cpp} (65%) create mode 100755 tests/UnitTests/TestBlockchainExplorer.cpp rename tests/{unit_tests => UnitTests}/TestBlockchainGenerator.cpp (50%) create mode 100644 tests/UnitTests/TestBlockchainGenerator.h rename tests/{unit_tests/test_format_utils.cpp => UnitTests/TestFormatUtils.cpp} (55%) mode change 100644 => 100755 rename tests/{unit_tests/test_inprocess_node.cpp => UnitTests/TestInprocessNode.cpp} (73%) rename tests/{unit_tests/test_JsonValue.cpp => UnitTests/TestJsonValue.cpp} (100%) mode change 100644 => 100755 create mode 100644 tests/UnitTests/TestMessageQueue.cpp rename tests/{unit_tests/test_path.cpp => UnitTests/TestPath.cpp} (100%) mode change 100644 => 100755 rename tests/{unit_tests/test_peerlist.cpp => UnitTests/TestPeerlist.cpp} (73%) mode change 100644 => 100755 rename tests/{unit_tests/test_protocol_pack.cpp => UnitTests/TestProtocolPack.cpp} (84%) mode change 100644 => 100755 rename tests/{unit_tests/test_tx_pool_detach.cpp => UnitTests/TestTransactionPoolDetach.cpp} (88%) rename tests/{unit_tests/test_transfers.cpp => UnitTests/TestTransfers.cpp} (92%) mode change 100644 => 100755 rename tests/{unit_tests/test_TransfersConsumer.cpp => UnitTests/TestTransfersConsumer.cpp} (83%) mode change 100644 => 100755 rename tests/{unit_tests/test_TransfersContainer.cpp => UnitTests/TestTransfersContainer.cpp} (67%) mode change 100644 => 100755 rename tests/{unit_tests/test_TransfersContainerKeyImage.cpp => UnitTests/TestTransfersContainerKeyImage.cpp} (79%) mode change 100644 => 100755 rename tests/{unit_tests/test_TransfersSubscription.cpp => UnitTests/TestTransfersSubscription.cpp} (83%) mode change 100644 => 100755 rename tests/{unit_tests => UnitTests}/TestUpgradeDetector.cpp (99%) create mode 100755 tests/UnitTests/TestWallet.cpp rename tests/{unit_tests/test_wallet.cpp => UnitTests/TestWalletLegacy.cpp} (84%) rename tests/{unit_tests => UnitTests}/TransactionApi.cpp (83%) create mode 100755 tests/UnitTests/TransactionApiHelpers.cpp create mode 100644 tests/UnitTests/TransactionApiHelpers.h rename tests/{unit_tests/tx_pool.cpp => UnitTests/TransactionPool.cpp} (89%) mode change 100644 => 100755 rename tests/{unit_tests => UnitTests}/TransfersObserver.h (95%) rename tests/{unit_tests/unit_tests_utils.h => UnitTests/UnitTestsUtils.h} (100%) mode change 100644 => 100755 rename tests/{unit_tests => UnitTests}/main.cpp (100%) delete mode 100644 tests/integration_tests/BlockchainInfo.h delete mode 100644 tests/integration_tests/Node.cpp delete mode 100755 tests/unit_tests/ICoreStub.cpp delete mode 100644 tests/unit_tests/ICoreStub.h delete mode 100644 tests/unit_tests/INodeStubs.cpp delete mode 100644 tests/unit_tests/INodeStubs.h delete mode 100644 tests/unit_tests/TestBlockchainGenerator.h delete mode 100644 tests/unit_tests/TransactionApiHelpers.h delete mode 100644 tests/unit_tests/binary_serialization_compatibility.cpp delete mode 100644 tests/unit_tests/serialization.cpp delete mode 100644 tests/unit_tests/serialization_structs_comparators.h delete mode 100644 tests/unit_tests/test_BlockchainExplorer.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 70e78739..8aa392ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ set(CMAKE_CONFIGURATION_TYPES Debug RelWithDebInfo Release CACHE TYPE INTERNAL) set(CMAKE_SKIP_INSTALL_RULES ON) set(CMAKE_SKIP_PACKAGE_ALL_DEPENDENCY ON) set(CMAKE_SUPPRESS_REGENERATION ON) -#enable_testing() +enable_testing() project(Bytecoin) @@ -62,11 +62,14 @@ else() else() set(MINGW_FLAG "") endif() + if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT (CMAKE_C_COMPILER_VERSION VERSION_LESS 5.1)) + set(WARNINGS "${WARNINGS} -Wno-error=odr") + endif() set(C_WARNINGS "-Waggregate-return -Wnested-externs -Wold-style-definition -Wstrict-prototypes") set(CXX_WARNINGS "-Wno-reorder -Wno-missing-field-initializers") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 ${MINGW_FLAG} ${WARNINGS} ${C_WARNINGS} ${ARCH_FLAG} -maes") if(NOT APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${MINGW_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${ARCH_FLAG} -maes") if(APPLE) diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index e3a4f0b2..fa94c070 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -1,3 +1,9 @@ +Release notes 1.0.6 + +- High-level API update +- Aggregate multi-addresses for Bytecoin RPC Wallet +- Wallet synchronization speed increase + Release notes 1.0.5 - High-level API for blockchain explorer diff --git a/include/BlockchainExplorerData.h b/include/BlockchainExplorerData.h index 6e476da6..6ad6d80a 100644 --- a/include/BlockchainExplorerData.h +++ b/include/BlockchainExplorerData.h @@ -21,6 +21,8 @@ #include #include +#include "CryptoTypes.h" + #include namespace CryptoNote { @@ -32,17 +34,17 @@ enum class TransactionRemoveReason : uint8_t }; struct TransactionOutputToKeyDetails { - std::array txOutKey; + Crypto::PublicKey txOutKey; }; struct TransactionOutputMultisignatureDetails { - std::vector> keys; + std::vector keys; uint32_t requiredSignatures; }; struct TransactionOutputDetails { uint64_t amount; - uint64_t globalIndex; + uint32_t globalIndex; boost::variant< TransactionOutputToKeyDetails, @@ -50,17 +52,17 @@ struct TransactionOutputDetails { }; struct TransactionOutputReferenceDetails { - std::array transactionHash; + Crypto::Hash transactionHash; size_t number; }; struct TransactionInputGenerateDetails { - uint64_t height; + uint32_t height; }; struct TransactionInputToKeyDetails { - std::vector keyOffsets; - std::array keyImage; + std::vector outputIndexes; + Crypto::KeyImage keyImage; uint64_t mixin; TransactionOutputReferenceDetails output; }; @@ -81,13 +83,13 @@ struct TransactionInputDetails { struct TransactionExtraDetails { std::vector padding; - std::vector> publicKey; + std::vector publicKey; std::vector nonce; std::vector raw; }; struct TransactionDetails { - std::array hash; + Crypto::Hash hash; uint64_t size; uint64_t fee; uint64_t totalInputsAmount; @@ -95,12 +97,12 @@ struct TransactionDetails { uint64_t mixin; uint64_t unlockTime; uint64_t timestamp; - std::array paymentId; + Crypto::Hash paymentId; bool inBlockchain; - std::array blockHash; - uint64_t blockHeight; + Crypto::Hash blockHash; + uint32_t blockHeight; TransactionExtraDetails extra; - std::vector>> signatures; + std::vector> signatures; std::vector inputs; std::vector outputs; }; @@ -109,11 +111,11 @@ struct BlockDetails { uint8_t majorVersion; uint8_t minorVersion; uint64_t timestamp; - std::array prevBlockHash; + Crypto::Hash prevBlockHash; uint32_t nonce; bool isOrphaned; - uint64_t height; - std::array hash; + uint32_t height; + Crypto::Hash hash; uint64_t difficulty; uint64_t reward; uint64_t baseReward; diff --git a/include/CryptoNote.h b/include/CryptoNote.h new file mode 100644 index 00000000..2eecebdf --- /dev/null +++ b/include/CryptoNote.h @@ -0,0 +1,114 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include "CryptoTypes.h" + +namespace CryptoNote { + +struct BaseInput { + uint32_t blockIndex; +}; + +struct KeyInput { + uint64_t amount; + std::vector outputIndexes; + Crypto::KeyImage keyImage; +}; + +struct MultisignatureInput { + uint64_t amount; + uint8_t signatureCount; + uint32_t outputIndex; +}; + +struct KeyOutput { + Crypto::PublicKey key; +}; + +struct MultisignatureOutput { + std::vector keys; + uint8_t requiredSignatureCount; +}; + +typedef boost::variant TransactionInput; + +typedef boost::variant TransactionOutputTarget; + +struct TransactionOutput { + uint64_t amount; + TransactionOutputTarget target; +}; + +struct TransactionPrefix { + uint8_t version; + uint64_t unlockTime; + std::vector inputs; + std::vector outputs; + std::vector extra; +}; + +struct Transaction : public TransactionPrefix { + std::vector> signatures; +}; + +struct ParentBlock { + uint8_t majorVersion; + uint8_t minorVersion; + Crypto::Hash previousBlockHash; + uint16_t transactionCount; + std::vector baseTransactionBranch; + Transaction baseTransaction; + std::vector blockchainBranch; +}; + +struct BlockHeader { + uint8_t majorVersion; + uint8_t minorVersion; + uint32_t nonce; + uint64_t timestamp; + Crypto::Hash previousBlockHash; +}; + +struct Block : public BlockHeader { + ParentBlock parentBlock; + Transaction baseTransaction; + std::vector transactionHashes; +}; + +struct AccountPublicAddress { + Crypto::PublicKey spendPublicKey; + Crypto::PublicKey viewPublicKey; +}; + +struct AccountKeys { + AccountPublicAddress address; + Crypto::SecretKey spendSecretKey; + Crypto::SecretKey viewSecretKey; +}; + +struct KeyPair { + Crypto::PublicKey publicKey; + Crypto::SecretKey secretKey; +}; + +using BinaryArray = std::vector; + +} diff --git a/src/serialization/json_utils.h b/include/CryptoTypes.h similarity index 73% rename from src/serialization/json_utils.h rename to include/CryptoTypes.h index b469e515..e9b18fb6 100644 --- a/src/serialization/json_utils.h +++ b/include/CryptoTypes.h @@ -17,18 +17,32 @@ #pragma once -#include -#include "json_archive.h" +#include -namespace serialization { +namespace Crypto { -template -std::string dump_json(T &v) -{ - std::stringstream ostr; - json_archive oar(ostr); - assert(serialization::serialize(oar, v)); - return ostr.str(); +struct Hash { + uint8_t data[32]; }; -} // namespace serialization +struct PublicKey { + uint8_t data[32]; +}; + +struct SecretKey { + uint8_t data[32]; +}; + +struct KeyDerivation { + uint8_t data[32]; +}; + +struct KeyImage { + uint8_t data[32]; +}; + +struct Signature { + uint8_t data[64]; +}; + +} diff --git a/include/IBlockchainExplorer.h b/include/IBlockchainExplorer.h index 3fa65681..b2c81261 100644 --- a/include/IBlockchainExplorer.h +++ b/include/IBlockchainExplorer.h @@ -29,7 +29,7 @@ public: virtual ~IBlockchainObserver() {} virtual void blockchainUpdated(const std::vector& newBlocks, const std::vector& orphanedBlocks) {} - virtual void poolUpdated(const std::vector& newTransactions, const std::vector, TransactionRemoveReason>>& removedTransactions) {} + virtual void poolUpdated(const std::vector& newTransactions, const std::vector>& removedTransactions) {} virtual void blockchainSynchronized(const BlockDetails& topBlock) {} }; @@ -44,13 +44,16 @@ public: virtual void init() = 0; virtual void shutdown() = 0; - virtual bool getBlocks(const std::vector& blockHeights, std::vector>& blocks) = 0; - virtual bool getBlocks(const std::vector>& blockHashes, std::vector& blocks) = 0; + virtual bool getBlocks(const std::vector& blockHeights, std::vector>& blocks) = 0; + virtual bool getBlocks(const std::vector& blockHashes, std::vector& blocks) = 0; + virtual bool getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps) = 0; virtual bool getBlockchainTop(BlockDetails& topBlock) = 0; - virtual bool getTransactions(const std::vector>& transactionHashes, std::vector& transactions) = 0; - virtual bool getPoolState(const std::vector>& knownPoolTransactionHashes, std::array knownBlockchainTop, bool& isBlockchainActual, std::vector& newTransactions, std::vector>& removedTransactions) = 0; + virtual bool getTransactions(const std::vector& transactionHashes, std::vector& transactions) = 0; + virtual bool getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions) = 0; + virtual bool getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps) = 0; + virtual bool getPoolState(const std::vector& knownPoolTransactionHashes, Crypto::Hash knownBlockchainTop, bool& isBlockchainActual, std::vector& newTransactions, std::vector& removedTransactions) = 0; virtual uint64_t getRewardBlocksWindow() = 0; virtual uint64_t getFullRewardMaxBlockSize(uint8_t majorVersion) = 0; diff --git a/include/IMultiWallet.h b/include/IMultiWallet.h deleted file mode 100755 index a6eb2c87..00000000 --- a/include/IMultiWallet.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include -#include - -namespace CryptoNote { - -enum class MultiWalletTransactionState : uint8_t { - FAILED -}; - -struct MultiWalletTransaction { - MultiWalletTransactionState state; - uint64_t timestamp; - uint64_t blockHeight; - std::array hash; - bool isBase; - int64_t totalAmount; - uint64_t fee; - uint64_t creationTime; - uint64_t unlockTime; - std::string extra; -}; - -struct MultiWalletTransfer { - std::string address; - uint64_t amount; -}; - -class IMultiWallet { -public: - virtual ~IMultiWallet() {} - - virtual void initialize(const std::string& password) = 0; - virtual void load(std::istream& source, const std::string& password) = 0; - virtual void shutdown() = 0; - - virtual void changePassword(const std::string& oldPassword, const std::string& newPassword) = 0; - virtual void save(std::ostream& destination, bool saveDetails = true, bool saveCache = true) = 0; - - virtual std::size_t getAddressCount() const = 0; - virtual std::string getAddress(std::size_t index) const = 0; - virtual std::string createAddress() = 0; - virtual std::string createAddress(const std::array& spendPublicKey, const std::array& spendSecretKey) = 0; - virtual void deleteAddress(const std::string& address) = 0; - - virtual uint64_t getActualBalance() const = 0; - virtual uint64_t getActualBalance(const std::string& address) const = 0; - virtual uint64_t getPendingBalance() const = 0; - virtual uint64_t getPendingBalance(const std::string& address) const = 0; - - virtual std::size_t getTransactionCount() const = 0; - virtual MultiWalletTransaction getTransaction(std::size_t transactionIndex) const = 0; - virtual std::size_t getTransactionTransferCount(std::size_t transactionIndex) const = 0; - virtual MultiWalletTransfer getTransactionTransfer(std::size_t transactionIndex, std::size_t transferIndex) const = 0; - - virtual std::size_t transfer(const MultiWalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; - virtual std::size_t transfer(const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; - virtual std::size_t transfer(const std::string& sourceAddress, const MultiWalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; - virtual std::size_t transfer(const std::string& sourceAddress, const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; - - virtual void start() = 0; - virtual void stop() = 0; - virtual void refresh() = 0; -}; - -} diff --git a/include/INode.h b/include/INode.h old mode 100644 new mode 100755 index 9c53317f..a8d1d5e6 --- a/include/INode.h +++ b/include/INode.h @@ -23,11 +23,12 @@ #include #include "crypto/crypto.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "rpc/core_rpc_server_commands_defs.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h" +#include "Rpc/CoreRpcServerCommandsDefinitions.h" #include "BlockchainExplorerData.h" +#include "ITransaction.h" namespace CryptoNote { @@ -35,15 +36,15 @@ class INodeObserver { public: virtual ~INodeObserver() {} virtual void peerCountUpdated(size_t count) {} - virtual void localBlockchainUpdated(uint64_t height) {} - virtual void lastKnownBlockHeightUpdated(uint64_t height) {} + virtual void localBlockchainUpdated(uint32_t height) {} + virtual void lastKnownBlockHeightUpdated(uint32_t height) {} virtual void poolChanged() {} - virtual void blockchainSynchronized(uint64_t topHeight) {} + virtual void blockchainSynchronized(uint32_t topHeight) {} }; struct OutEntry { - uint64_t outGlobalIndex; - crypto::public_key outKey; + uint32_t outGlobalIndex; + Crypto::PublicKey outKey; }; struct OutsForAmount { @@ -51,10 +52,16 @@ struct OutsForAmount { std::vector outs; }; -struct BlockCompleteEntry { - crypto::hash blockHash; - CryptoNote::blobdata block; - std::list txs; +struct TransactionShortInfo { + Crypto::Hash txId; + TransactionPrefix txPrefix; +}; + +struct BlockShortEntry { + Crypto::Hash blockHash; + bool hasBlock; + CryptoNote::Block block; + std::vector txsShortInfo; }; class INode { @@ -69,22 +76,26 @@ public: virtual bool shutdown() = 0; virtual size_t getPeerCount() const = 0; - virtual uint64_t getLastLocalBlockHeight() const = 0; - virtual uint64_t getLastKnownBlockHeight() const = 0; - virtual uint64_t getLocalBlockCount() const = 0; - virtual uint64_t getKnownBlockCount() const = 0; + virtual uint32_t getLastLocalBlockHeight() const = 0; + virtual uint32_t getLastKnownBlockHeight() const = 0; + virtual uint32_t getLocalBlockCount() const = 0; + virtual uint32_t getKnownBlockCount() const = 0; virtual uint64_t getLastLocalBlockTimestamp() const = 0; virtual void relayTransaction(const Transaction& transaction, const Callback& callback) = 0; virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) = 0; - virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) = 0; - virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) = 0; - virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) = 0; - virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) = 0; + virtual void getNewBlocks(std::vector&& knownBlockIds, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback) = 0; + virtual void getTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) = 0; + virtual void queryBlocks(std::vector&& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback) = 0; + virtual void getPoolSymmetricDifference(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, std::vector>& newTxs, std::vector& deletedTxIds, const Callback& callback) = 0; + virtual void getMultisignatureOutputByGlobalIndex(uint64_t amount, uint32_t gindex, MultisignatureOutput& out, const Callback& callback) = 0; - virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) = 0; - virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) = 0; - virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) = 0; + virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) = 0; + virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) = 0; + virtual void getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps, const Callback& callback) = 0; + virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) = 0; + virtual void getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions, const Callback& callback) = 0; + virtual void getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps, const Callback& callback) = 0; virtual void isSynchronized(bool& syncStatus, const Callback& callback) = 0; }; diff --git a/include/ITransaction.h b/include/ITransaction.h old mode 100644 new mode 100755 index 9c9fe773..e65f4222 --- a/include/ITransaction.h +++ b/include/ITransaction.h @@ -19,64 +19,27 @@ #include #include +#include #include +#include "CryptoNote.h" + namespace CryptoNote { -typedef std::array PublicKey; -typedef std::array SecretKey; -typedef std::array KeyImage; -typedef std::array Hash; -typedef std::vector Blob; - -struct AccountAddress { - PublicKey spendPublicKey; - PublicKey viewPublicKey; -}; - -struct AccountKeys { - AccountAddress address; - SecretKey spendSecretKey; - SecretKey viewSecretKey; -}; - namespace TransactionTypes { enum class InputType : uint8_t { Invalid, Key, Multisignature, Generating }; enum class OutputType : uint8_t { Invalid, Key, Multisignature }; - struct InputKey { - uint64_t amount; - std::vector keyOffsets; - KeyImage keyImage; // double spending protection - }; - - struct InputMultisignature { - uint64_t amount; - uint32_t signatures; - uint64_t outputIndex; - }; - - struct OutputKey { - uint64_t amount; - PublicKey key; - }; - - struct OutputMultisignature { - uint64_t amount; - std::vector keys; - uint32_t requiredSignatures; - }; - struct GlobalOutput { - PublicKey targetKey; - uint64_t outputIndex; + Crypto::PublicKey targetKey; + uint32_t outputIndex; }; typedef std::vector GlobalOutputsContainer; struct OutputKeyInfo { - PublicKey transactionPublicKey; + Crypto::PublicKey transactionPublicKey; size_t transactionIndex; size_t outputInTransaction; }; @@ -86,12 +49,6 @@ namespace TransactionTypes { GlobalOutputsContainer outputs; OutputKeyInfo realOutput; }; - - struct KeyPair { - PublicKey publicKey; - SecretKey secretKey; - }; - } // @@ -101,33 +58,34 @@ class ITransactionReader { public: virtual ~ITransactionReader() { } - virtual Hash getTransactionHash() const = 0; - virtual Hash getTransactionPrefixHash() const = 0; - virtual PublicKey getTransactionPublicKey() const = 0; + virtual Crypto::Hash getTransactionHash() const = 0; + virtual Crypto::Hash getTransactionPrefixHash() const = 0; + virtual Crypto::PublicKey getTransactionPublicKey() const = 0; + virtual bool getTransactionSecretKey(Crypto::SecretKey& key) const = 0; virtual uint64_t getUnlockTime() const = 0; // extra - virtual bool getPaymentId(Hash& paymentId) const = 0; - virtual bool getExtraNonce(std::string& nonce) const = 0; - virtual Blob getExtra() const = 0; + virtual bool getPaymentId(Crypto::Hash& paymentId) const = 0; + virtual bool getExtraNonce(BinaryArray& nonce) const = 0; + virtual BinaryArray getExtra() const = 0; // inputs virtual size_t getInputCount() const = 0; virtual uint64_t getInputTotalAmount() const = 0; virtual TransactionTypes::InputType getInputType(size_t index) const = 0; - virtual void getInput(size_t index, TransactionTypes::InputKey& input) const = 0; - virtual void getInput(size_t index, TransactionTypes::InputMultisignature& input) const = 0; + virtual void getInput(size_t index, KeyInput& input) const = 0; + virtual void getInput(size_t index, MultisignatureInput& input) const = 0; // outputs virtual size_t getOutputCount() const = 0; virtual uint64_t getOutputTotalAmount() const = 0; virtual TransactionTypes::OutputType getOutputType(size_t index) const = 0; - virtual void getOutput(size_t index, TransactionTypes::OutputKey& output) const = 0; - virtual void getOutput(size_t index, TransactionTypes::OutputMultisignature& output) const = 0; + virtual void getOutput(size_t index, KeyOutput& output, uint64_t& amount) const = 0; + virtual void getOutput(size_t index, MultisignatureOutput& output, uint64_t& amount) const = 0; // signatures virtual size_t getRequiredSignaturesCount(size_t inputIndex) const = 0; - virtual bool findOutputsToAccount(const AccountAddress& addr, const SecretKey& viewSecretKey, std::vector& outs, uint64_t& outputAmount) const = 0; + virtual bool findOutputsToAccount(const AccountPublicAddress& addr, const Crypto::SecretKey& viewSecretKey, std::vector& outs, uint64_t& outputAmount) const = 0; // various checks virtual bool validateInputs() const = 0; @@ -135,7 +93,7 @@ public: virtual bool validateSignatures() const = 0; // serialized transaction - virtual Blob getTransactionData() const = 0; + virtual BinaryArray getTransactionData() const = 0; }; // @@ -150,25 +108,27 @@ public: virtual void setUnlockTime(uint64_t unlockTime) = 0; // extra - virtual void setPaymentId(const Hash& paymentId) = 0; - virtual void setExtraNonce(const std::string& nonce) = 0; - virtual void appendExtra(const Blob& extraData) = 0; + virtual void setPaymentId(const Crypto::Hash& paymentId) = 0; + virtual void setExtraNonce(const BinaryArray& nonce) = 0; + virtual void appendExtra(const BinaryArray& extraData) = 0; // Inputs/Outputs - virtual size_t addInput(const TransactionTypes::InputKey& input) = 0; - virtual size_t addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, TransactionTypes::KeyPair& ephKeys) = 0; - virtual size_t addInput(const TransactionTypes::InputMultisignature& input) = 0; + virtual size_t addInput(const KeyInput& input) = 0; + virtual size_t addInput(const MultisignatureInput& input) = 0; + virtual size_t addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, KeyPair& ephKeys) = 0; - virtual size_t addOutput(uint64_t amount, const AccountAddress& to) = 0; - virtual size_t addOutput(uint64_t amount, const std::vector& to, uint32_t requiredSignatures) = 0; + virtual size_t addOutput(uint64_t amount, const AccountPublicAddress& to) = 0; + virtual size_t addOutput(uint64_t amount, const std::vector& to, uint32_t requiredSignatures) = 0; + virtual size_t addOutput(uint64_t amount, const KeyOutput& out) = 0; + virtual size_t addOutput(uint64_t amount, const MultisignatureOutput& out) = 0; // transaction info - virtual bool getTransactionSecretKey(SecretKey& key) const = 0; - virtual void setTransactionSecretKey(const SecretKey& key) = 0; + virtual void setTransactionSecretKey(const Crypto::SecretKey& key) = 0; // signing - virtual void signInputKey(size_t input, const TransactionTypes::InputKeyInfo& info, const TransactionTypes::KeyPair& ephKeys) = 0; - virtual void signInputMultisignature(size_t input, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) = 0; + virtual void signInputKey(size_t input, const TransactionTypes::InputKeyInfo& info, const KeyPair& ephKeys) = 0; + virtual void signInputMultisignature(size_t input, const Crypto::PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) = 0; + virtual void signInputMultisignature(size_t input, const KeyPair& ephemeralKeys) = 0; }; class ITransaction : diff --git a/include/ITransfersContainer.h b/include/ITransfersContainer.h index 21e553da..96cbd12c 100644 --- a/include/ITransfersContainer.h +++ b/include/ITransfersContainer.h @@ -27,19 +27,19 @@ namespace CryptoNote { -const uint64_t UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX = std::numeric_limits::max(); +const uint32_t UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX = std::numeric_limits::max(); struct TransactionInformation { // transaction info - Hash transactionHash; - PublicKey publicKey; - uint64_t blockHeight; + Crypto::Hash transactionHash; + Crypto::PublicKey publicKey; + uint32_t blockHeight; uint64_t timestamp; uint64_t unlockTime; uint64_t totalAmountIn; uint64_t totalAmountOut; std::vector extra; - Hash paymentId; + Crypto::Hash paymentId; }; @@ -47,24 +47,24 @@ struct TransactionOutputInformation { // output info TransactionTypes::OutputType type; uint64_t amount; - uint64_t globalOutputIndex; + uint32_t globalOutputIndex; uint32_t outputInTransaction; // transaction info - Hash transactionHash; - PublicKey transactionPublicKey; + Crypto::Hash transactionHash; + Crypto::PublicKey transactionPublicKey; union { - PublicKey outputKey; // Type: Key + Crypto::PublicKey outputKey; // Type: Key uint32_t requiredSignatures; // Type: Multisignature }; }; struct TransactionSpentOutputInformation: public TransactionOutputInformation { - uint64_t spendingBlockHeight; + uint32_t spendingBlockHeight; uint64_t timestamp; - Hash spendingTransactionHash; - KeyImage keyImage; //!< \attention Used only for TransactionTypes::OutputType::Key + Crypto::Hash spendingTransactionHash; + Crypto::KeyImage keyImage; //!< \attention Used only for TransactionTypes::OutputType::Key uint32_t inputInTransaction; }; @@ -96,9 +96,9 @@ public: virtual size_t transactionsCount() = 0; virtual uint64_t balance(uint32_t flags = IncludeDefault) = 0; virtual void getOutputs(std::vector& transfers, uint32_t flags = IncludeDefault) = 0; - virtual bool getTransactionInformation(const Hash& transactionHash, TransactionInformation& info, int64_t& txBalance) = 0; - virtual std::vector getTransactionOutputs(const Hash& transactionHash, uint32_t flags = IncludeDefault) = 0; - virtual void getUnconfirmedTransactions(std::vector& transactions) = 0; + virtual bool getTransactionInformation(const Crypto::Hash& transactionHash, TransactionInformation& info, int64_t& txBalance) = 0; + virtual std::vector getTransactionOutputs(const Crypto::Hash& transactionHash, uint32_t flags = IncludeDefault) = 0; + virtual void getUnconfirmedTransactions(std::vector& transactions) = 0; virtual std::vector getSpentOutputs() = 0; }; diff --git a/include/ITransfersSynchronizer.h b/include/ITransfersSynchronizer.h index f68d7399..d49da55e 100644 --- a/include/ITransfersSynchronizer.h +++ b/include/ITransfersSynchronizer.h @@ -42,22 +42,23 @@ class ITransfersSubscription; class ITransfersObserver { public: virtual void onError(ITransfersSubscription* object, - uint64_t height, std::error_code ec) {} + uint32_t height, std::error_code ec) { + } - virtual void onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) {} + virtual void onTransactionUpdated(ITransfersSubscription* object, const Crypto::Hash& transactionHash) {} /** * \note The sender must guarantee that onTransactionDeleted() is called only after onTransactionUpdated() is called * for the same \a transactionHash. */ - virtual void onTransactionDeleted(ITransfersSubscription* object, const Hash& transactionHash) { } + virtual void onTransactionDeleted(ITransfersSubscription* object, const Crypto::Hash& transactionHash) {} }; class ITransfersSubscription : public IObservable < ITransfersObserver > { public: virtual ~ITransfersSubscription() {} - virtual AccountAddress getAddress() = 0; + virtual AccountPublicAddress getAddress() = 0; virtual ITransfersContainer& getContainer() = 0; }; @@ -66,10 +67,10 @@ public: virtual ~ITransfersSynchronizer() {} virtual ITransfersSubscription& addSubscription(const AccountSubscription& acc) = 0; - virtual bool removeSubscription(const AccountAddress& acc) = 0; - virtual void getSubscriptions(std::vector& subscriptions) = 0; + virtual bool removeSubscription(const AccountPublicAddress& acc) = 0; + virtual void getSubscriptions(std::vector& subscriptions) = 0; // returns nullptr if address is not found - virtual ITransfersSubscription* getSubscription(const AccountAddress& acc) = 0; + virtual ITransfersSubscription* getSubscription(const AccountPublicAddress& acc) = 0; }; } diff --git a/include/IWallet.h b/include/IWallet.h old mode 100644 new mode 100755 index c64961d7..854f0dfd --- a/include/IWallet.h +++ b/include/IWallet.h @@ -17,113 +17,99 @@ #pragma once -#include -#include -#include #include -#include #include -#include #include +#include "CryptoNote.h" namespace CryptoNote { -typedef size_t TransactionId; -typedef size_t TransferId; -typedef std::array TransactionHash; +const size_t WALLET_INVALID_TRANSACTION_ID = std::numeric_limits::max(); +const size_t WALLET_INVALID_TRANSFER_ID = std::numeric_limits::max(); +const uint32_t WALLET_UNCONFIRMED_TRANSACTION_HEIGHT = std::numeric_limits::max(); -struct Transfer { +enum class WalletTransactionState : uint8_t { + SUCCEEDED = 0, + FAILED, + CANCELLED +}; + +enum WalletEventType { + TRANSACTION_CREATED, + TRANSACTION_UPDATED, + BALANCE_UNLOCKED +}; + +struct WalletTransactionCreatedData { + size_t transactionIndex; +}; + +struct WalletTransactionUpdatedData { + size_t transactionIndex; +}; + +struct WalletEvent { + WalletEventType type; + union { + WalletTransactionCreatedData transactionCreated; + WalletTransactionUpdatedData transactionUpdated; + }; +}; + +struct WalletTransaction { + WalletTransactionState state; + uint64_t timestamp; + uint32_t blockHeight; + Crypto::Hash hash; + int64_t totalAmount; + uint64_t fee; + uint64_t creationTime; + uint64_t unlockTime; + std::string extra; +}; + +struct WalletTransfer { std::string address; int64_t amount; }; -const TransactionId INVALID_TRANSACTION_ID = std::numeric_limits::max(); -const TransferId INVALID_TRANSFER_ID = std::numeric_limits::max(); -const uint64_t UNCONFIRMED_TRANSACTION_HEIGHT = std::numeric_limits::max(); - -enum class TransactionState : uint8_t { - Active, // --> {Deleted} - Deleted, // --> {Active} - - Sending, // --> {Active, Cancelled, Failed} - Cancelled, // --> {} - Failed // --> {} -}; - -struct TransactionInfo { - TransferId firstTransferId; - size_t transferCount; - int64_t totalAmount; - uint64_t fee; - uint64_t sentTime; - uint64_t unlockTime; - TransactionHash hash; - bool isCoinbase; - uint64_t blockHeight; - uint64_t timestamp; - std::string extra; - TransactionState state; -}; - -typedef std::array WalletPublicKey; -typedef std::array WalletSecretKey; - -struct WalletAccountKeys { - WalletPublicKey viewPublicKey; - WalletSecretKey viewSecretKey; - WalletPublicKey spendPublicKey; - WalletSecretKey spendSecretKey; -}; - -class IWalletObserver { -public: - virtual ~IWalletObserver() {} - - virtual void initCompleted(std::error_code result) {} - virtual void saveCompleted(std::error_code result) {} - virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total) {} - virtual void synchronizationCompleted(std::error_code result) {} - virtual void actualBalanceUpdated(uint64_t actualBalance) {} - virtual void pendingBalanceUpdated(uint64_t pendingBalance) {} - virtual void externalTransactionCreated(TransactionId transactionId) {} - virtual void sendTransactionCompleted(TransactionId transactionId, std::error_code result) {} - virtual void transactionUpdated(TransactionId transactionId) {} -}; - class IWallet { public: - virtual ~IWallet() {} ; - virtual void addObserver(IWalletObserver* observer) = 0; - virtual void removeObserver(IWalletObserver* observer) = 0; + virtual ~IWallet() {} - virtual void initAndGenerate(const std::string& password) = 0; - virtual void initAndLoad(std::istream& source, const std::string& password) = 0; - virtual void initWithKeys(const WalletAccountKeys& accountKeys, const std::string& password) = 0; + virtual void initialize(const std::string& password) = 0; + virtual void load(std::istream& source, const std::string& password) = 0; virtual void shutdown() = 0; - virtual void reset() = 0; - virtual void save(std::ostream& destination, bool saveDetailed = true, bool saveCache = true) = 0; + virtual void changePassword(const std::string& oldPassword, const std::string& newPassword) = 0; + virtual void save(std::ostream& destination, bool saveDetails = true, bool saveCache = true) = 0; - virtual std::error_code changePassword(const std::string& oldPassword, const std::string& newPassword) = 0; + virtual size_t getAddressCount() const = 0; + virtual std::string getAddress(size_t index) const = 0; + virtual std::string createAddress() = 0; + virtual std::string createAddress(const KeyPair& spendKey) = 0; + virtual void deleteAddress(const std::string& address) = 0; - virtual std::string getAddress() = 0; + virtual uint64_t getActualBalance() const = 0; + virtual uint64_t getActualBalance(const std::string& address) const = 0; + virtual uint64_t getPendingBalance() const = 0; + virtual uint64_t getPendingBalance(const std::string& address) const = 0; - virtual uint64_t actualBalance() = 0; - virtual uint64_t pendingBalance() = 0; + virtual size_t getTransactionCount() const = 0; + virtual WalletTransaction getTransaction(size_t transactionIndex) const = 0; + virtual size_t getTransactionTransferCount(size_t transactionIndex) const = 0; + virtual WalletTransfer getTransactionTransfer(size_t transactionIndex, size_t transferIndex) const = 0; - virtual size_t getTransactionCount() = 0; - virtual size_t getTransferCount() = 0; + virtual size_t transfer(const WalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; + virtual size_t transfer(const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; + virtual size_t transfer(const std::string& sourceAddress, const WalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; + virtual size_t transfer(const std::string& sourceAddress, const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; - virtual TransactionId findTransactionByTransferId(TransferId transferId) = 0; - - virtual bool getTransaction(TransactionId transactionId, TransactionInfo& transaction) = 0; - virtual bool getTransfer(TransferId transferId, Transfer& transfer) = 0; + virtual void start() = 0; + virtual void stop() = 0; - virtual TransactionId sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0) = 0; - virtual TransactionId sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0) = 0; - virtual std::error_code cancelTransaction(size_t transferId) = 0; - - virtual void getAccountKeys(WalletAccountKeys& keys) = 0; + //blocks until an event occured + virtual WalletEvent getEvent() = 0; }; } diff --git a/include/IWalletLegacy.h b/include/IWalletLegacy.h new file mode 100644 index 00000000..a0686a5f --- /dev/null +++ b/include/IWalletLegacy.h @@ -0,0 +1,116 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include +#include +#include +#include "CryptoNote.h" + +namespace CryptoNote { + +typedef size_t TransactionId; +typedef size_t TransferId; + +struct WalletLegacyTransfer { + std::string address; + int64_t amount; +}; + +const TransactionId WALLET_LEGACY_INVALID_TRANSACTION_ID = std::numeric_limits::max(); +const TransferId WALLET_LEGACY_INVALID_TRANSFER_ID = std::numeric_limits::max(); +const uint32_t WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT = std::numeric_limits::max(); + +enum class WalletLegacyTransactionState : uint8_t { + Active, // --> {Deleted} + Deleted, // --> {Active} + + Sending, // --> {Active, Cancelled, Failed} + Cancelled, // --> {} + Failed // --> {} +}; + +struct WalletLegacyTransaction { + TransferId firstTransferId; + size_t transferCount; + int64_t totalAmount; + uint64_t fee; + uint64_t sentTime; + uint64_t unlockTime; + Crypto::Hash hash; + bool isCoinbase; + uint32_t blockHeight; + uint64_t timestamp; + std::string extra; + WalletLegacyTransactionState state; +}; + +class IWalletLegacyObserver { +public: + virtual ~IWalletLegacyObserver() {} + + virtual void initCompleted(std::error_code result) {} + virtual void saveCompleted(std::error_code result) {} + virtual void synchronizationProgressUpdated(uint32_t current, uint32_t total) {} + virtual void synchronizationCompleted(std::error_code result) {} + virtual void actualBalanceUpdated(uint64_t actualBalance) {} + virtual void pendingBalanceUpdated(uint64_t pendingBalance) {} + virtual void externalTransactionCreated(TransactionId transactionId) {} + virtual void sendTransactionCompleted(TransactionId transactionId, std::error_code result) {} + virtual void transactionUpdated(TransactionId transactionId) {} +}; + +class IWalletLegacy { +public: + virtual ~IWalletLegacy() {} ; + virtual void addObserver(IWalletLegacyObserver* observer) = 0; + virtual void removeObserver(IWalletLegacyObserver* observer) = 0; + + virtual void initAndGenerate(const std::string& password) = 0; + virtual void initAndLoad(std::istream& source, const std::string& password) = 0; + virtual void initWithKeys(const AccountKeys& accountKeys, const std::string& password) = 0; + virtual void shutdown() = 0; + virtual void reset() = 0; + + virtual void save(std::ostream& destination, bool saveDetailed = true, bool saveCache = true) = 0; + + virtual std::error_code changePassword(const std::string& oldPassword, const std::string& newPassword) = 0; + + virtual std::string getAddress() = 0; + + virtual uint64_t actualBalance() = 0; + virtual uint64_t pendingBalance() = 0; + + virtual size_t getTransactionCount() = 0; + virtual size_t getTransferCount() = 0; + + virtual TransactionId findTransactionByTransferId(TransferId transferId) = 0; + + virtual bool getTransaction(TransactionId transactionId, WalletLegacyTransaction& transaction) = 0; + virtual bool getTransfer(TransferId transferId, WalletLegacyTransfer& transfer) = 0; + + virtual TransactionId sendTransaction(const WalletLegacyTransfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0) = 0; + virtual TransactionId sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0) = 0; + virtual std::error_code cancelTransaction(size_t transferId) = 0; + + virtual void getAccountKeys(AccountKeys& keys) = 0; +}; + +} diff --git a/src/BlockchainExplorer/BlockchainExplorer.cpp b/src/BlockchainExplorer/BlockchainExplorer.cpp old mode 100644 new mode 100755 index e3ec4b5b..bdfe4ee1 --- a/src/BlockchainExplorer/BlockchainExplorer.cpp +++ b/src/BlockchainExplorer/BlockchainExplorer.cpp @@ -21,37 +21,69 @@ #include #include -#include "cryptonote_core/cryptonote_format_utils.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteConfig.h" + #include "BlockchainExplorerErrors.h" +#include "ITransaction.h" using namespace Logging; +using namespace Crypto; namespace CryptoNote { +class ContextCounterHolder +{ +public: + ContextCounterHolder(BlockchainExplorer::AsyncContextCounter& counter) : counter(counter) {} + ~ContextCounterHolder() { counter.delAsyncContext(); } + +private: + BlockchainExplorer::AsyncContextCounter& counter; +}; + class NodeRequest { public: NodeRequest(const std::function& request) : requestFunc(request) {} std::error_code performBlocking() { - requestFunc(std::bind(&NodeRequest::completeionCallback, this, std::placeholders::_1)); - return promise.get_future().get(); + std::promise promise; + std::future future = promise.get_future(); + requestFunc([&](std::error_code c){ + blockingCompleteionCallback(std::move(promise), c); + }); + return future.get(); } - void performAsync(const INode::Callback& callback) { - requestFunc(callback); + void performAsync(BlockchainExplorer::AsyncContextCounter& asyncContextCounter, const INode::Callback& callback) { + asyncContextCounter.addAsyncContext(); + requestFunc(std::bind(&NodeRequest::asyncCompleteionCallback, callback, std::ref(asyncContextCounter), std::placeholders::_1)); } private: - void completeionCallback(std::error_code ec) { + void blockingCompleteionCallback(std::promise promise, std::error_code ec) { promise.set_value(ec); } - std::promise promise; + static void asyncCompleteionCallback(const INode::Callback& callback, BlockchainExplorer::AsyncContextCounter& asyncContextCounter, std::error_code ec) { + ContextCounterHolder counterHolder(asyncContextCounter); + try { + callback(ec); + } catch (...) { + return; + } + } + const std::function requestFunc; }; -BlockchainExplorer::BlockchainExplorer(INode& node, Logging::ILogger& logger) : node(node), logger(logger, "BlockchainExplorer"), state(NOT_INITIALIZED) {} +BlockchainExplorer::BlockchainExplorer(INode& node, Logging::ILogger& logger) : + node(node), + logger(logger, "BlockchainExplorer"), + state(NOT_INITIALIZED), + synchronized(false), + observersCounter(0) {} BlockchainExplorer::~BlockchainExplorer() {} @@ -59,7 +91,7 @@ bool BlockchainExplorer::addObserver(IBlockchainObserver* observer) { if (state.load() != INITIALIZED) { throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); } - + observersCounter.fetch_add(1); return observerManager.add(observer); } @@ -67,7 +99,9 @@ bool BlockchainExplorer::removeObserver(IBlockchainObserver* observer) { if (state.load() != INITIALIZED) { throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); } - + if (observersCounter.load() != 0) { + observersCounter.fetch_sub(1); + } return observerManager.remove(observer); } @@ -98,10 +132,11 @@ void BlockchainExplorer::shutdown() { throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); } node.removeObserver(this); + asyncContextCounter.waitAsyncContextsFinish(); state.store(NOT_INITIALIZED); } -bool BlockchainExplorer::getBlocks(const std::vector& blockHeights, std::vector>& blocks) { +bool BlockchainExplorer::getBlocks(const std::vector& blockHeights, std::vector>& blocks) { if (state.load() != INITIALIZED) { throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); } @@ -111,7 +146,7 @@ bool BlockchainExplorer::getBlocks(const std::vector& blockHeights, st std::bind( static_cast< void(INode::*)( - const std::vector&, + const std::vector&, std::vector>&, const INode::Callback& ) @@ -131,7 +166,7 @@ bool BlockchainExplorer::getBlocks(const std::vector& blockHeights, st return true; } -bool BlockchainExplorer::getBlocks(const std::vector>& blockHashes, std::vector& blocks) { +bool BlockchainExplorer::getBlocks(const std::vector& blockHashes, std::vector& blocks) { if (state.load() != INITIALIZED) { throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); } @@ -141,13 +176,13 @@ bool BlockchainExplorer::getBlocks(const std::vector>& b std::bind( static_cast< void(INode::*)( - const std::vector&, + const std::vector&, std::vector&, const INode::Callback& ) >(&INode::getBlocks), std::ref(node), - std::cref(reinterpret_cast&>(blockHashes)), + std::cref(reinterpret_cast&>(blockHashes)), std::ref(blocks), std::placeholders::_1 ) @@ -161,15 +196,50 @@ bool BlockchainExplorer::getBlocks(const std::vector>& b return true; } +bool BlockchainExplorer::getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps) { + if (state.load() != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); + } + + logger(DEBUGGING) << "Get blocks by timestamp request came."; + NodeRequest request( + std::bind( + static_cast< + void(INode::*)( + uint64_t, + uint64_t, + uint32_t, + std::vector&, + uint32_t&, + const INode::Callback& + ) + >(&INode::getBlocks), + std::ref(node), + timestampBegin, + timestampEnd, + blocksNumberLimit, + std::ref(blocks), + std::ref(blocksNumberWithinTimestamps), + std::placeholders::_1 + ) + ); + std::error_code ec = request.performBlocking(); + if (ec) { + logger(ERROR) << "Can't get blocks by timestamp: " << ec.message(); + throw std::system_error(ec); + } + return true; +} + bool BlockchainExplorer::getBlockchainTop(BlockDetails& topBlock) { if (state.load() != INITIALIZED) { throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); } logger(DEBUGGING) << "Get blockchain top request came."; - uint64_t lastHeight = node.getLastLocalBlockHeight(); + uint32_t lastHeight = node.getLastLocalBlockHeight(); - std::vector heights; + std::vector heights; heights.push_back(std::move(lastHeight)); std::vector> blocks; @@ -195,49 +265,105 @@ bool BlockchainExplorer::getBlockchainTop(BlockDetails& topBlock) { return true; } -bool BlockchainExplorer::getTransactions(const std::vector>& transactionHashes, std::vector& transactions) { +bool BlockchainExplorer::getTransactions(const std::vector& transactionHashes, std::vector& transactions) { if (state.load() != INITIALIZED) { throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); } - logger(DEBUGGING) << "Get transactions request came."; + logger(DEBUGGING) << "Get transactions by hash request came."; NodeRequest request( std::bind( - &INode::getTransactions, + static_cast< + void(INode::*)( + const std::vector&, + std::vector&, + const INode::Callback& + ) + >(&INode::getTransactions), std::ref(node), - std::cref(reinterpret_cast&>(transactionHashes)), + std::cref(reinterpret_cast&>(transactionHashes)), std::ref(transactions), std::placeholders::_1 ) ); std::error_code ec = request.performBlocking(); if (ec) { - logger(ERROR) << "Can't get transactions: " << ec.message(); + logger(ERROR) << "Can't get transactions by hash: " << ec.message(); throw std::system_error(ec); } return true; } -bool BlockchainExplorer::getPoolState(const std::vector>& knownPoolTransactionHashes, std::array knownBlockchainTopHash, bool& isBlockchainActual, std::vector& newTransactions, std::vector>& removedTransactions) { +bool BlockchainExplorer::getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps) { + if (state.load() != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); + } + + logger(DEBUGGING) << "Get transactions by timestamp request came."; + NodeRequest request( + std::bind( + &INode::getPoolTransactions, + std::ref(node), + timestampBegin, + timestampEnd, + transactionsNumberLimit, + std::ref(transactions), + std::ref(transactionsNumberWithinTimestamps), + std::placeholders::_1 + ) + ); + std::error_code ec = request.performBlocking(); + if (ec) { + logger(ERROR) << "Can't get transactions by timestamp: " << ec.message(); + throw std::system_error(ec); + } + return true; +} + +bool BlockchainExplorer::getTransactionsByPaymentId(const Hash& paymentId, std::vector& transactions) { + if (state.load() != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); + } + + logger(DEBUGGING) << "Get transactions by payment id request came."; + NodeRequest request( + std::bind( + &INode::getTransactionsByPaymentId, + std::ref(node), + std::cref(reinterpret_cast(paymentId)), + std::ref(transactions), + std::placeholders::_1 + ) + ); + std::error_code ec = request.performBlocking(); + if (ec) { + logger(ERROR) << "Can't get transactions by payment id: " << ec.message(); + throw std::system_error(ec); + } + return true; +} + +bool BlockchainExplorer::getPoolState(const std::vector& knownPoolTransactionHashes, Hash knownBlockchainTopHash, bool& isBlockchainActual, std::vector& newTransactions, std::vector& removedTransactions) { if (state.load() != INITIALIZED) { throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); } logger(DEBUGGING) << "Get pool state request came."; - std::vector rawNewTransactions; + std::vector> rawNewTransactions; NodeRequest request( [&](const INode::Callback& callback) { - std::vector hashes; - for (const std::array& hash : knownPoolTransactionHashes) { - hashes.push_back(std::move(reinterpret_cast(hash))); + std::vector hashes; + for (Hash hash : knownPoolTransactionHashes) { + hashes.push_back(std::move(hash)); } + node.getPoolSymmetricDifference( std::move(hashes), - reinterpret_cast(knownBlockchainTopHash), + reinterpret_cast(knownBlockchainTopHash), isBlockchainActual, rawNewTransactions, - reinterpret_cast&>(removedTransactions), + removedTransactions, callback ); } @@ -248,10 +374,10 @@ bool BlockchainExplorer::getPoolState(const std::vector> throw std::system_error(ec); } - std::vector> newTransactionsHashes; - for (const Transaction& rawTransaction : rawNewTransactions) { - crypto::hash transactionHash = get_transaction_hash(rawTransaction); - newTransactionsHashes.push_back(std::move(reinterpret_cast&>(transactionHash))); + std::vector newTransactionsHashes; + for (const auto& rawTransaction : rawNewTransactions) { + Hash transactionHash = rawTransaction->getTransactionHash(); + newTransactionsHashes.push_back(std::move(transactionHash)); } return getTransactions(newTransactionsHashes, newTransactions); @@ -295,27 +421,32 @@ bool BlockchainExplorer::isSynchronized() { logger(ERROR) << "Can't get synchronization status: " << ec.message(); throw std::system_error(ec); } + synchronized.store(syncStatus); return syncStatus; } void BlockchainExplorer::poolChanged() { logger(DEBUGGING) << "Got poolChanged notification."; + if (!synchronized.load() || observersCounter.load() == 0) { + return; + } + std::unique_lock lock(mutex); - std::shared_ptr> rawNewTransactionsPtr = std::make_shared>(); - std::shared_ptr> removedTransactionsPtr = std::make_shared>(); + std::shared_ptr>> rawNewTransactionsPtr = std::make_shared>>(); + std::shared_ptr> removedTransactionsPtr = std::make_shared>(); std::shared_ptr isBlockchainActualPtr = std::make_shared(false); NodeRequest request( [this, rawNewTransactionsPtr, removedTransactionsPtr, isBlockchainActualPtr](const INode::Callback& callback) { - std::vector hashes; - for (const crypto::hash& hash : knownPoolState) { + std::vector hashes; + for (const Hash& hash : knownPoolState) { hashes.push_back(std::move(hash)); } node.getPoolSymmetricDifference( std::move(hashes), - reinterpret_cast(knownBlockchainTop.hash), + reinterpret_cast(knownBlockchainTop.hash), *isBlockchainActualPtr, *rawNewTransactionsPtr, *removedTransactionsPtr, @@ -323,7 +454,7 @@ void BlockchainExplorer::poolChanged() { ); } ); - request.performAsync( + request.performAsync(asyncContextCounter, [this, rawNewTransactionsPtr, removedTransactionsPtr, isBlockchainActualPtr](std::error_code ec) { if (ec) { logger(ERROR) << "Can't send poolChanged notification because can't get pool symmetric difference: " << ec.message(); @@ -336,22 +467,23 @@ void BlockchainExplorer::poolChanged() { std::unique_lock lock(mutex); - std::shared_ptr> newTransactionsHashesPtr = std::make_shared>(); - for (const Transaction& rawTransaction : *rawNewTransactionsPtr) { - crypto::hash transactionHash = get_transaction_hash(rawTransaction); + std::shared_ptr> newTransactionsHashesPtr = std::make_shared>(); + for (const auto& rawTransaction : *rawNewTransactionsPtr) { + auto hash = rawTransaction->getTransactionHash(); + Hash transactionHash = reinterpret_cast(hash); bool inserted = knownPoolState.emplace(transactionHash).second; if (inserted) { newTransactionsHashesPtr->push_back(std::move(transactionHash)); } } - std::shared_ptr, TransactionRemoveReason>>> removedTransactionsHashesPtr = std::make_shared, TransactionRemoveReason>>>(); - for (const crypto::hash hash : *removedTransactionsPtr) { + std::shared_ptr>> removedTransactionsHashesPtr = std::make_shared>>(); + for (const Hash hash : *removedTransactionsPtr) { auto iter = knownPoolState.find(hash); if (iter != knownPoolState.end()) { removedTransactionsHashesPtr->push_back( std::move(std::make_pair( - reinterpret_cast&>(hash), + hash, TransactionRemoveReason::INCLUDED_IN_BLOCK //Can't have real reason here. )) ); @@ -362,14 +494,20 @@ void BlockchainExplorer::poolChanged() { std::shared_ptr> newTransactionsPtr = std::make_shared>(); NodeRequest request( std::bind( - &INode::getTransactions, + static_cast< + void(INode::*)( + const std::vector&, + std::vector&, + const INode::Callback& + ) + >(&INode::getTransactions), std::ref(node), std::cref(*newTransactionsHashesPtr), std::ref(*newTransactionsPtr), std::placeholders::_1 ) ); - request.performAsync( + request.performAsync(asyncContextCounter, [this, newTransactionsHashesPtr, newTransactionsPtr, removedTransactionsHashesPtr](std::error_code ec) { if (ec) { logger(ERROR) << "Can't send poolChanged notification because can't get transactions: " << ec.message(); @@ -386,10 +524,16 @@ void BlockchainExplorer::poolChanged() { } -void BlockchainExplorer::blockchainSynchronized(uint64_t topHeight) { +void BlockchainExplorer::blockchainSynchronized(uint32_t topHeight) { logger(DEBUGGING) << "Got blockchainSynchronized notification."; - std::shared_ptr> blockHeightsPtr = std::make_shared>(); + synchronized.store(true); + + if (observersCounter.load() == 0) { + return; + } + + std::shared_ptr> blockHeightsPtr = std::make_shared>(); std::shared_ptr>> blocksPtr = std::make_shared>>(); blockHeightsPtr->push_back(topHeight); @@ -398,7 +542,7 @@ void BlockchainExplorer::blockchainSynchronized(uint64_t topHeight) { std::bind( static_cast< void(INode::*)( - const std::vector&, + const std::vector&, std::vector>&, const INode::Callback& ) @@ -410,7 +554,7 @@ void BlockchainExplorer::blockchainSynchronized(uint64_t topHeight) { ) ); - request.performAsync( + request.performAsync(asyncContextCounter, [this, blockHeightsPtr, blocksPtr, topHeight](std::error_code ec) { if (ec) { logger(ERROR) << "Can't send blockchainSynchronized notification because can't get blocks by height: " << ec.message(); @@ -439,17 +583,22 @@ void BlockchainExplorer::blockchainSynchronized(uint64_t topHeight) { ); } -void BlockchainExplorer::localBlockchainUpdated(uint64_t height) { +void BlockchainExplorer::localBlockchainUpdated(uint32_t height) { logger(DEBUGGING) << "Got localBlockchainUpdated notification."; + if (observersCounter.load() == 0) { + knownBlockchainTopHeight = height; + return; + } + std::unique_lock lock(mutex); assert(height >= knownBlockchainTopHeight); - std::shared_ptr> blockHeightsPtr = std::make_shared>(); + std::shared_ptr> blockHeightsPtr = std::make_shared>(); std::shared_ptr>> blocksPtr = std::make_shared>>(); - for (size_t i = knownBlockchainTopHeight; i <= height; ++i) { + for (uint32_t i = knownBlockchainTopHeight; i <= height; ++i) { blockHeightsPtr->push_back(i); } @@ -459,7 +608,7 @@ void BlockchainExplorer::localBlockchainUpdated(uint64_t height) { std::bind( static_cast< void(INode::*)( - const std::vector&, + const std::vector&, std::vector>&, const INode::Callback& ) @@ -471,7 +620,7 @@ void BlockchainExplorer::localBlockchainUpdated(uint64_t height) { ) ); - request.performAsync( + request.performAsync(asyncContextCounter, [this, blockHeightsPtr, blocksPtr](std::error_code ec) { if (ec) { logger(ERROR) << "Can't send blockchainUpdated notification because can't get blocks by height: " << ec.message(); diff --git a/src/BlockchainExplorer/BlockchainExplorer.h b/src/BlockchainExplorer/BlockchainExplorer.h old mode 100644 new mode 100755 index d3c644d3..8ab27420 --- a/src/BlockchainExplorer/BlockchainExplorer.h +++ b/src/BlockchainExplorer/BlockchainExplorer.h @@ -27,6 +27,8 @@ #include "Common/ObserverManager.h" #include "BlockchainExplorerErrors.h" +#include "Wallet/WalletAsyncContextCounter.h" + #include "Logging/LoggerRef.h" namespace CryptoNote { @@ -46,13 +48,16 @@ public: virtual bool addObserver(IBlockchainObserver* observer) override; virtual bool removeObserver(IBlockchainObserver* observer) override; - virtual bool getBlocks(const std::vector& blockHeights, std::vector>& blocks) override; - virtual bool getBlocks(const std::vector>& blockHashes, std::vector& blocks) override; + virtual bool getBlocks(const std::vector& blockHeights, std::vector>& blocks) override; + virtual bool getBlocks(const std::vector& blockHashes, std::vector& blocks) override; + virtual bool getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps) override; virtual bool getBlockchainTop(BlockDetails& topBlock) override; - virtual bool getTransactions(const std::vector>& transactionHashes, std::vector& transactions) override; - virtual bool getPoolState(const std::vector>& knownPoolTransactionHashes, std::array knownBlockchainTop, bool& isBlockchainActual, std::vector& newTransactions, std::vector>& removedTransactions) override; + virtual bool getTransactions(const std::vector& transactionHashes, std::vector& transactions) override; + virtual bool getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions) override; + virtual bool getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps) override; + virtual bool getPoolState(const std::vector& knownPoolTransactionHashes, Crypto::Hash knownBlockchainTop, bool& isBlockchainActual, std::vector& newTransactions, std::vector& removedTransactions) override; virtual uint64_t getRewardBlocksWindow() override; virtual uint64_t getFullRewardMaxBlockSize(uint8_t majorVersion) override; @@ -63,8 +68,10 @@ public: virtual void shutdown() override; virtual void poolChanged() override; - virtual void blockchainSynchronized(uint64_t topHeight) override; - virtual void localBlockchainUpdated(uint64_t height) override; + virtual void blockchainSynchronized(uint32_t topHeight) override; + virtual void localBlockchainUpdated(uint32_t height) override; + + typedef WalletAsyncContextCounter AsyncContextCounter; private: enum State { @@ -73,16 +80,20 @@ private: }; BlockDetails knownBlockchainTop; - uint64_t knownBlockchainTopHeight; - std::unordered_set knownPoolState; + uint32_t knownBlockchainTopHeight; + std::unordered_set knownPoolState; std::atomic state; - tools::ObserverManager observerManager; + std::atomic synchronized; + std::atomic observersCounter; + Tools::ObserverManager observerManager; std::mutex mutex; INode& node; Logging::LoggerRef logger; + + AsyncContextCounter asyncContextCounter; }; } diff --git a/src/BlockchainExplorer/BlockchainExplorerDataBuilder.cpp b/src/BlockchainExplorer/BlockchainExplorerDataBuilder.cpp old mode 100644 new mode 100755 index 336b9342..f0b7b640 --- a/src/BlockchainExplorer/BlockchainExplorerDataBuilder.cpp +++ b/src/BlockchainExplorer/BlockchainExplorerDataBuilder.cpp @@ -21,23 +21,25 @@ #include #include "Common/StringTools.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteCore/CryptoNoteTools.h" +#include "CryptoNoteCore/TransactionExtra.h" +#include "CryptoNoteConfig.h" namespace CryptoNote { -BlockchainExplorerDataBuilder::BlockchainExplorerDataBuilder(CryptoNote::ICore& core, CryptoNote::ICryptonoteProtocolQuery& protocol) : - core(core), - protocol(protocol) -{ +BlockchainExplorerDataBuilder::BlockchainExplorerDataBuilder(CryptoNote::ICore& core, CryptoNote::ICryptoNoteProtocolQuery& protocol) : +core(core), +protocol(protocol) { } bool BlockchainExplorerDataBuilder::getMixin(const Transaction& transaction, uint64_t& mixin) { mixin = 0; - for (const TransactionInput& txin : transaction.vin) { - if (txin.type() != typeid(TransactionInputToKey)) { + for (const TransactionInput& txin : transaction.inputs) { + if (txin.type() != typeid(KeyInput)) { continue; } - uint64_t currentMixin = boost::get(txin).keyOffsets.size(); + uint64_t currentMixin = boost::get(txin).outputIndexes.size(); if (currentMixin > mixin) { mixin = currentMixin; } @@ -45,75 +47,71 @@ bool BlockchainExplorerDataBuilder::getMixin(const Transaction& transaction, uin return true; } -bool BlockchainExplorerDataBuilder::getPaymentId(const Transaction& transaction, crypto::hash& paymentId) { - std::vector txExtraFields; - parse_tx_extra(transaction.extra, txExtraFields); - tx_extra_nonce extraNonce; - if (!find_tx_extra_field_by_type(txExtraFields, extraNonce)) { +bool BlockchainExplorerDataBuilder::getPaymentId(const Transaction& transaction, Crypto::Hash& paymentId) { + std::vector txExtraFields; + parseTransactionExtra(transaction.extra, txExtraFields); + TransactionExtraNonce extraNonce; + if (!findTransactionExtraFieldByType(txExtraFields, extraNonce)) { return false; } - return get_payment_id_from_tx_extra_nonce(extraNonce.nonce, paymentId); + return getPaymentIdFromTransactionExtraNonce(extraNonce.nonce, paymentId); } bool BlockchainExplorerDataBuilder::fillTxExtra(const std::vector& rawExtra, TransactionExtraDetails& extraDetails) { extraDetails.raw = rawExtra; - std::vector txExtraFields; - parse_tx_extra(rawExtra, txExtraFields); - for (const tx_extra_field& field : txExtraFields) { - if (typeid(tx_extra_padding) == field.type()) { - extraDetails.padding.push_back(std::move(boost::get(field).size)); - } - else if (typeid(tx_extra_pub_key) == field.type()) { - extraDetails.publicKey.push_back(std::move(reinterpret_cast&>(boost::get(field).pub_key))); - } - else if (typeid(tx_extra_nonce) == field.type()) { - extraDetails.nonce.push_back(std::move(Common::toHex(boost::get(field).nonce.data(), boost::get(field).nonce.size()))); + std::vector txExtraFields; + parseTransactionExtra(rawExtra, txExtraFields); + for (const TransactionExtraField& field : txExtraFields) { + if (typeid(TransactionExtraPadding) == field.type()) { + extraDetails.padding.push_back(std::move(boost::get(field).size)); + } else if (typeid(TransactionExtraPublicKey) == field.type()) { + extraDetails.publicKey.push_back(std::move(boost::get(field).publicKey)); + } else if (typeid(TransactionExtraNonce) == field.type()) { + extraDetails.nonce.push_back(std::move(Common::toHex(boost::get(field).nonce.data(), boost::get(field).nonce.size()))); } } return true; } size_t BlockchainExplorerDataBuilder::median(std::vector& v) { - if(v.empty()) + if (v.empty()) return boost::value_initialized(); - if(v.size() == 1) + if (v.size() == 1) return v[0]; size_t n = (v.size()) / 2; std::sort(v.begin(), v.end()); //nth_element(v.begin(), v.begin()+n-1, v.end()); - if(v.size()%2) - {//1, 3, 5... + if (v.size() % 2) {//1, 3, 5... return v[n]; - }else - {//2, 4, 6... - return (v[n-1] + v[n])/2; + } else {//2, 4, 6... + return (v[n - 1] + v[n]) / 2; } } bool BlockchainExplorerDataBuilder::fillBlockDetails(const Block &block, BlockDetails& blockDetails) { - crypto::hash hash = get_block_hash(block); - + Crypto::Hash hash = get_block_hash(block); + blockDetails.majorVersion = block.majorVersion; blockDetails.minorVersion = block.minorVersion; blockDetails.timestamp = block.timestamp; - blockDetails.prevBlockHash = reinterpret_cast&>(block.prevId); + blockDetails.prevBlockHash = block.previousBlockHash; blockDetails.nonce = block.nonce; - blockDetails.hash = reinterpret_cast&>(hash); + blockDetails.hash = hash; blockDetails.reward = 0; - for (const TransactionOutput& out : block.minerTx.vout) { + for (const TransactionOutput& out : block.baseTransaction.outputs) { blockDetails.reward += out.amount; } - if (block.minerTx.vin.front().type() != typeid(TransactionInputGenerate)) + if (block.baseTransaction.inputs.front().type() != typeid(BaseInput)) return false; - blockDetails.height = boost::get(block.minerTx.vin.front()).height; - - crypto::hash tmpHash = core.getBlockIdByHeight(blockDetails.height); + blockDetails.height = boost::get(block.baseTransaction.inputs.front()).blockIndex; + + Crypto::Hash tmpHash = core.getBlockIdByHeight(blockDetails.height); blockDetails.isOrphaned = hash != tmpHash; - + if (!core.getBlockDifficulty(blockDetails.height, blockDetails.difficulty)) { return false; } @@ -130,20 +128,21 @@ bool BlockchainExplorerDataBuilder::fillBlockDetails(const Block &block, BlockDe } blockDetails.transactionsCumulativeSize = blockSize; - size_t blokBlobSize = get_object_blobsize(block); - size_t minerTxBlobSize = get_object_blobsize(block.minerTx); + size_t blokBlobSize = getObjectBinarySize(block); + size_t minerTxBlobSize = getObjectBinarySize(block.baseTransaction); blockDetails.blockSize = blokBlobSize + blockDetails.transactionsCumulativeSize - minerTxBlobSize; - + if (!core.getAlreadyGeneratedCoins(hash, blockDetails.alreadyGeneratedCoins)) { return false; } - - blockDetails.alreadyGeneratedTransactions = 0; //TODO + + if (!core.getGeneratedTransactionsNumber(blockDetails.height, blockDetails.alreadyGeneratedTransactions)) { + return false; + } uint64_t prevBlockGeneratedCoins = 0; - if (blockDetails.height > 0) - { - if (!core.getAlreadyGeneratedCoins(block.prevId, prevBlockGeneratedCoins)) { + if (blockDetails.height > 0) { + if (!core.getAlreadyGeneratedCoins(block.previousBlockHash, prevBlockGeneratedCoins)) { return false; } } @@ -151,45 +150,40 @@ bool BlockchainExplorerDataBuilder::fillBlockDetails(const Block &block, BlockDe uint64_t currentReward = 0; int64_t emissionChange = 0; bool penalizeFee = block.majorVersion >= 2; - if(!core.getBlockReward(blockDetails.sizeMedian, 0, prevBlockGeneratedCoins, 0, penalizeFee, maxReward, emissionChange)) - { + if (!core.getBlockReward(blockDetails.sizeMedian, 0, prevBlockGeneratedCoins, 0, penalizeFee, maxReward, emissionChange)) { return false; } - if(!core.getBlockReward(blockDetails.sizeMedian, blockDetails.transactionsCumulativeSize, prevBlockGeneratedCoins, 0, penalizeFee, currentReward, emissionChange)) - { + if (!core.getBlockReward(blockDetails.sizeMedian, blockDetails.transactionsCumulativeSize, prevBlockGeneratedCoins, 0, penalizeFee, currentReward, emissionChange)) { return false; } blockDetails.baseReward = maxReward; - if (maxReward == 0 && currentReward == 0) - { + if (maxReward == 0 && currentReward == 0) { blockDetails.penalty = static_cast(0); - } - else - { + } else { if (maxReward < currentReward) { return false; } blockDetails.penalty = static_cast(maxReward - currentReward) / static_cast(maxReward); } - - blockDetails.transactions.reserve(block.txHashes.size() + 1); + + blockDetails.transactions.reserve(block.transactionHashes.size() + 1); TransactionDetails transactionDetails; - if (!fillTransactionDetails(block.minerTx, transactionDetails, block.timestamp)) { + if (!fillTransactionDetails(block.baseTransaction, transactionDetails, block.timestamp)) { return false; } blockDetails.transactions.push_back(std::move(transactionDetails)); - + std::list found; - std::list missed; - core.getTransactions(block.txHashes, found, missed); - if (found.size() != block.txHashes.size()) { + std::list missed; + core.getTransactions(block.transactionHashes, found, missed, blockDetails.isOrphaned); + if (found.size() != block.transactionHashes.size()) { return false; } - + blockDetails.totalFeeAmount = 0; - + for (const Transaction& tx : found) { TransactionDetails transactionDetails; if (!fillTransactionDetails(tx, transactionDetails, block.timestamp)) { @@ -202,21 +196,21 @@ bool BlockchainExplorerDataBuilder::fillBlockDetails(const Block &block, BlockDe } bool BlockchainExplorerDataBuilder::fillTransactionDetails(const Transaction& transaction, TransactionDetails& transactionDetails, uint64_t timestamp) { - crypto::hash hash = get_transaction_hash(transaction); - transactionDetails.hash = reinterpret_cast&>(hash); - + Crypto::Hash hash = getObjectHash(transaction); + transactionDetails.hash = hash; + transactionDetails.timestamp = timestamp; - - crypto::hash blockHash; - uint64_t blockHeight; + + Crypto::Hash blockHash; + uint32_t blockHeight; if (!core.getBlockContainingTx(hash, blockHash, blockHeight)) { transactionDetails.inBlockchain = false; - transactionDetails.blockHeight = boost::value_initialized(); - transactionDetails.blockHash = boost::value_initialized>(); + transactionDetails.blockHeight = boost::value_initialized(); + transactionDetails.blockHash = boost::value_initialized(); } else { transactionDetails.inBlockchain = true; transactionDetails.blockHeight = blockHeight; - transactionDetails.blockHash = reinterpret_cast&>(blockHash); + transactionDetails.blockHash = blockHash; if (timestamp == 0) { Block block; if (!core.getBlockByHash(blockHash, block)) { @@ -225,8 +219,8 @@ bool BlockchainExplorerDataBuilder::fillTransactionDetails(const Transaction& tr transactionDetails.timestamp = block.timestamp; } } - - transactionDetails.size = get_object_blobsize(transaction); + + transactionDetails.size = getObjectBinarySize(transaction); transactionDetails.unlockTime = transaction.unlockTime; transactionDetails.totalOutputsAmount = get_outs_money_amount(transaction); @@ -236,7 +230,7 @@ bool BlockchainExplorerDataBuilder::fillTransactionDetails(const Transaction& tr } transactionDetails.totalInputsAmount = inputsAmount; - if (transaction.vin.size() > 0 && transaction.vin.front().type() == typeid(TransactionInputGenerate)) { + if (transaction.inputs.size() > 0 && transaction.inputs.front().type() == typeid(BaseInput)) { //It's gen transaction transactionDetails.fee = 0; transactionDetails.mixin = 0; @@ -252,106 +246,105 @@ bool BlockchainExplorerDataBuilder::fillTransactionDetails(const Transaction& tr } transactionDetails.mixin = mixin; } - - crypto::hash paymentId; + + Crypto::Hash paymentId; if (getPaymentId(transaction, paymentId)) { - transactionDetails.paymentId = reinterpret_cast&>(paymentId); + transactionDetails.paymentId = paymentId; + } else { + transactionDetails.paymentId = boost::value_initialized(); } - else { - transactionDetails.paymentId = boost::value_initialized>(); - } - + fillTxExtra(transaction.extra, transactionDetails.extra); - + transactionDetails.signatures.reserve(transaction.signatures.size()); - for (const std::vector& signatures : transaction.signatures) { - std::vector> signaturesDetails; + for (const std::vector& signatures : transaction.signatures) { + std::vector signaturesDetails; signaturesDetails.reserve(signatures.size()); - for (const crypto::signature& signature : signatures) { - signaturesDetails.push_back(std::move(reinterpret_cast&>(signature))); + for (const Crypto::Signature& signature : signatures) { + signaturesDetails.push_back(std::move(signature)); } transactionDetails.signatures.push_back(std::move(signaturesDetails)); } - - transactionDetails.inputs.reserve(transaction.vin.size()); - for (const TransactionInput& txIn : transaction.vin) { + + transactionDetails.inputs.reserve(transaction.inputs.size()); + for (const TransactionInput& txIn : transaction.inputs) { TransactionInputDetails txInDetails; - if (txIn.type() == typeid(TransactionInputGenerate)) { + if (txIn.type() == typeid(BaseInput)) { TransactionInputGenerateDetails txInGenDetails; - txInGenDetails.height = boost::get(txIn).height; + txInGenDetails.height = boost::get(txIn).blockIndex; txInDetails.amount = 0; - for (const TransactionOutput& out : transaction.vout) { + for (const TransactionOutput& out : transaction.outputs) { txInDetails.amount += out.amount; } txInDetails.input = txInGenDetails; - } else if (txIn.type() == typeid(TransactionInputToKey)) { + } else if (txIn.type() == typeid(KeyInput)) { TransactionInputToKeyDetails txInToKeyDetails; - const TransactionInputToKey& txInToKey = boost::get(txIn); - std::list> outputReferences; + const KeyInput& txInToKey = boost::get(txIn); + std::list> outputReferences; if (!core.scanOutputkeysForIndices(txInToKey, outputReferences)) { return false; } txInDetails.amount = txInToKey.amount; - txInToKeyDetails.keyOffsets = txInToKey.keyOffsets; - txInToKeyDetails.keyImage = reinterpret_cast&>(txInToKey.keyImage); - txInToKeyDetails.mixin = txInToKey.keyOffsets.size(); + txInToKeyDetails.outputIndexes = txInToKey.outputIndexes; + txInToKeyDetails.keyImage = txInToKey.keyImage; + txInToKeyDetails.mixin = txInToKey.outputIndexes.size(); txInToKeyDetails.output.number = outputReferences.back().second; - txInToKeyDetails.output.transactionHash = reinterpret_cast&>(outputReferences.back().first); + txInToKeyDetails.output.transactionHash = outputReferences.back().first; txInDetails.input = txInToKeyDetails; - } else if (txIn.type() == typeid(TransactionInputMultisignature)) { + } else if (txIn.type() == typeid(MultisignatureInput)) { TransactionInputMultisignatureDetails txInMultisigDetails; - const TransactionInputMultisignature& txInMultisig = boost::get(txIn); + const MultisignatureInput& txInMultisig = boost::get(txIn); txInDetails.amount = txInMultisig.amount; - txInMultisigDetails.signatures = txInMultisig.signatures; - std::pair outputReference; + txInMultisigDetails.signatures = txInMultisig.signatureCount; + std::pair outputReference; if (!core.getMultisigOutputReference(txInMultisig, outputReference)) { return false; } txInMultisigDetails.output.number = outputReference.second; - txInMultisigDetails.output.transactionHash = reinterpret_cast&>(outputReference.first); + txInMultisigDetails.output.transactionHash = outputReference.first; txInDetails.input = txInMultisigDetails; } else { return false; } transactionDetails.inputs.push_back(std::move(txInDetails)); } - - transactionDetails.outputs.reserve(transaction.vout.size()); - std::vector globalIndices; - globalIndices.reserve(transaction.vout.size()); - if (!core.get_tx_outputs_gindexs(hash, globalIndices)) { - for (size_t i = 0; i < transaction.vout.size(); ++i) { + + transactionDetails.outputs.reserve(transaction.outputs.size()); + std::vector globalIndices; + globalIndices.reserve(transaction.outputs.size()); + if (!transactionDetails.inBlockchain || !core.get_tx_outputs_gindexs(hash, globalIndices)) { + for (size_t i = 0; i < transaction.outputs.size(); ++i) { globalIndices.push_back(0); } } - typedef boost::tuple outputWithIndex; - auto range = boost::combine(transaction.vout, globalIndices); + typedef boost::tuple outputWithIndex; + auto range = boost::combine(transaction.outputs, globalIndices); for (const outputWithIndex& txOutput : range) { TransactionOutputDetails txOutDetails; txOutDetails.amount = txOutput.get<0>().amount; txOutDetails.globalIndex = txOutput.get<1>(); - if (txOutput.get<0>().target.type() == typeid(TransactionOutputToKey)) { + if (txOutput.get<0>().target.type() == typeid(KeyOutput)) { TransactionOutputToKeyDetails txOutToKeyDetails; - txOutToKeyDetails.txOutKey = reinterpret_cast&>(boost::get(txOutput.get<0>().target).key); + txOutToKeyDetails.txOutKey = boost::get(txOutput.get<0>().target).key; txOutDetails.output = txOutToKeyDetails; - } else if (txOutput.get<0>().target.type() == typeid(TransactionOutputMultisignature)) { + } else if (txOutput.get<0>().target.type() == typeid(MultisignatureOutput)) { TransactionOutputMultisignatureDetails txOutMultisigDetails; - TransactionOutputMultisignature txOutMultisig = boost::get(txOutput.get<0>().target); + MultisignatureOutput txOutMultisig = boost::get(txOutput.get<0>().target); txOutMultisigDetails.keys.reserve(txOutMultisig.keys.size()); - for (const crypto::public_key& key : txOutMultisig.keys) { - txOutMultisigDetails.keys.push_back(std::move(reinterpret_cast&>(key))); + for (const Crypto::PublicKey& key : txOutMultisig.keys) { + txOutMultisigDetails.keys.push_back(std::move(key)); } - txOutMultisigDetails.requiredSignatures = txOutMultisig.requiredSignatures; + txOutMultisigDetails.requiredSignatures = txOutMultisig.requiredSignatureCount; txOutDetails.output = txOutMultisigDetails; } else { return false; } transactionDetails.outputs.push_back(std::move(txOutDetails)); } - + return true; } diff --git a/src/BlockchainExplorer/BlockchainExplorerDataBuilder.h b/src/BlockchainExplorer/BlockchainExplorerDataBuilder.h old mode 100644 new mode 100755 index c7a3ea65..33c3ab71 --- a/src/BlockchainExplorer/BlockchainExplorerDataBuilder.h +++ b/src/BlockchainExplorer/BlockchainExplorerDataBuilder.h @@ -20,8 +20,8 @@ #include #include -#include "cryptonote_protocol/ICryptonoteProtocolQuery.h" -#include "cryptonote_core/ICore.h" +#include "CryptoNoteProtocol/ICryptoNoteProtocolQuery.h" +#include "CryptoNoteCore/ICore.h" #include "BlockchainExplorerData.h" namespace CryptoNote { @@ -29,7 +29,7 @@ namespace CryptoNote { class BlockchainExplorerDataBuilder { public: - BlockchainExplorerDataBuilder(CryptoNote::ICore& core, CryptoNote::ICryptonoteProtocolQuery& protocol); + BlockchainExplorerDataBuilder(CryptoNote::ICore& core, CryptoNote::ICryptoNoteProtocolQuery& protocol); BlockchainExplorerDataBuilder(const BlockchainExplorerDataBuilder&) = delete; BlockchainExplorerDataBuilder(BlockchainExplorerDataBuilder&&) = delete; @@ -40,13 +40,14 @@ public: bool fillBlockDetails(const Block& block, BlockDetails& blockDetails); bool fillTransactionDetails(const Transaction &tx, TransactionDetails& txRpcInfo, uint64_t timestamp = 0); + static bool getPaymentId(const Transaction& transaction, Crypto::Hash& paymentId); + private: bool getMixin(const Transaction& transaction, uint64_t& mixin); - bool getPaymentId(const Transaction& transaction, crypto::hash& paymentId); bool fillTxExtra(const std::vector& rawExtra, TransactionExtraDetails& extraDetails); size_t median(std::vector& v); CryptoNote::ICore& core; - CryptoNote::ICryptonoteProtocolQuery& protocol; + CryptoNote::ICryptoNoteProtocolQuery& protocol; }; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1ebd2854..16e89dfe 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,20 +2,19 @@ add_definitions(-DSTATICLIB) file(GLOB_RECURSE BlockchainExplorer BlockchainExplorer/*) file(GLOB_RECURSE Common Common/*) -file(GLOB_RECURSE ConnectivityTool connectivity_tool/*) +file(GLOB_RECURSE ConnectivityTool ConnectivityTool/*) file(GLOB_RECURSE Crypto crypto/*) -file(GLOB_RECURSE CryptoNote CryptoNote/*) -file(GLOB_RECURSE CryptoNoteCore cryptonote_core/* cryptonote_config.h) -file(GLOB_RECURSE CryptoNoteProtocol cryptonote_protocol/*) -file(GLOB_RECURSE Daemon daemon/*) +file(GLOB_RECURSE CryptoNoteCore CryptoNoteCore/* CryptoNoteConfig.h) +file(GLOB_RECURSE CryptoNoteProtocol CryptoNoteProtocol/*) +file(GLOB_RECURSE Daemon Daemon/*) file(GLOB_RECURSE Http HTTP/*) file(GLOB_RECURSE InProcessNode InProcessNode/*) file(GLOB_RECURSE Logging Logging/*) -file(GLOB_RECURSE NodeRpcProxy node_rpc_proxy/*) -file(GLOB_RECURSE P2p p2p/*) -file(GLOB_RECURSE Rpc rpc/*) -file(GLOB_RECURSE Serialization serialization/*) -file(GLOB_RECURSE SimpleWallet simplewallet/*) +file(GLOB_RECURSE NodeRpcProxy NodeRpcProxy/*) +file(GLOB_RECURSE P2p P2p/*) +file(GLOB_RECURSE Rpc Rpc/*) +file(GLOB_RECURSE Serialization Serialization/*) +file(GLOB_RECURSE SimpleWallet SimpleWallet/*) if(MSVC) file(GLOB_RECURSE System System/* Platform/Windows/System/*) elseif(APPLE) @@ -23,17 +22,20 @@ file(GLOB_RECURSE System System/* Platform/OSX/System/*) else() file(GLOB_RECURSE System System/* Platform/Linux/System/*) endif() -file(GLOB_RECURSE Transfers transfers/*) -file(GLOB_RECURSE Wallet wallet/*) -file(GLOB_RECURSE PaymentService payment_service/*) +file(GLOB_RECURSE Transfers Transfers/*) +file(GLOB_RECURSE Wallet Wallet/*) +file(GLOB_RECURSE WalletLegacy WalletLegacy/*) +file(GLOB_RECURSE JsonRpcServer JsonRpcServer/*) -source_group("" FILES ${Common} ${ConnectivityTool} ${Crypto} ${CryptoNote} ${CryptoNoteCore} ${CryptoNoteProtocol} ${Daemon} ${Http} ${Logging} ${NodeRpcProxy} ${P2p} ${Rpc} ${Serialization} ${SimpleWallet} ${System} ${Transfers} ${Wallet}) +file(GLOB_RECURSE PaymentGate PaymentGate/*) +file(GLOB_RECURSE PaymentGateService PaymentGateService/*) + +source_group("" FILES $${Common} ${ConnectivityTool} ${Crypto} ${CryptoNoteCore} ${CryptoNoteProtocol} ${Daemon} ${JsonRpcServer} ${Http} ${Logging} ${NodeRpcProxy} ${P2p} ${Rpc} ${Serialization} ${SimpleWallet} ${System} ${Transfers} ${Wallet} ${WalletLegacy}) add_library(BlockchainExplorer ${BlockchainExplorer}) add_library(Common ${Common}) add_library(Crypto ${Crypto}) -add_library(CryptoNote ${CryptoNote}) add_library(CryptoNoteCore ${CryptoNoteCore}) add_library(Http ${Http}) add_library(InProcessNode ${InProcessNode}) @@ -44,27 +46,35 @@ add_library(P2P ${CryptoNoteProtocol} ${P2p}) add_library(Serialization ${Serialization}) add_library(System ${System}) add_library(Transfers ${Transfers}) -add_library(Wallet ${Wallet}) +add_library(Wallet ${Wallet} ${WalletLegacy}) +add_library(PaymentGate ${PaymentGate}) +add_library(JsonRpcServer ${JsonRpcServer}) add_executable(ConnectivityTool ${ConnectivityTool}) add_executable(Daemon ${Daemon}) add_executable(SimpleWallet ${SimpleWallet}) -add_executable(PaymentGate ${PaymentService}) +add_executable(PaymentGateService ${PaymentGateService}) + target_link_libraries(ConnectivityTool CryptoNoteCore Common Logging Crypto P2P Rpc Http Serialization System ${Boost_LIBRARIES}) -target_link_libraries(Daemon CryptoNoteCore P2P Rpc Serialization System Http Logging Common Crypto upnpc-static ${Boost_LIBRARIES}) +target_link_libraries(Daemon CryptoNoteCore P2P Rpc Serialization System Http Logging Common Crypto upnpc-static BlockchainExplorer ${Boost_LIBRARIES}) target_link_libraries(SimpleWallet Wallet NodeRpcProxy Transfers Rpc Http Serialization CryptoNoteCore System Logging Common Crypto ${Boost_LIBRARIES}) -target_link_libraries(PaymentGate Wallet NodeRpcProxy Transfers CryptoNoteCore Crypto P2P Rpc Http Serialization System Logging Common InProcessNode BlockchainExplorer upnpc-static ${Boost_LIBRARIES}) +target_link_libraries(PaymentGateService PaymentGate JsonRpcServer Wallet NodeRpcProxy Transfers CryptoNoteCore Crypto P2P Rpc Http Serialization System Logging Common InProcessNode upnpc-static BlockchainExplorer ${Boost_LIBRARIES}) + +if (MSVC) + target_link_libraries(ConnectivityTool ws2_32) + target_link_libraries(SimpleWallet ws2_32) +endif () add_dependencies(Rpc version) add_dependencies(ConnectivityTool version) add_dependencies(Daemon version) add_dependencies(SimpleWallet version) -add_dependencies(PaymentGate version) +add_dependencies(PaymentGateService version) add_dependencies(P2P version) set_property(TARGET ConnectivityTool PROPERTY OUTPUT_NAME "connectivity_tool") set_property(TARGET Daemon PROPERTY OUTPUT_NAME "bytecoind") set_property(TARGET SimpleWallet PROPERTY OUTPUT_NAME "simplewallet") -set_property(TARGET PaymentGate PROPERTY OUTPUT_NAME "walletd") +set_property(TARGET PaymentGateService PROPERTY OUTPUT_NAME "walletd") diff --git a/src/Common/ArrayRef.h b/src/Common/ArrayRef.h index 8e6501fa..9d5a391e 100755 --- a/src/Common/ArrayRef.h +++ b/src/Common/ArrayRef.h @@ -30,7 +30,7 @@ namespace Common { // 'data' == 'nullptr' && 'size' > 0 - Undefined // 'data' != 'nullptr' && 'size' > 0 - NOTEMPTY NOTNIL // For signed integer 'Size', 'ArrayRef' with 'size' < 0 is undefined. -template class ArrayRef { +template class ArrayRef { public: typedef ObjectType Object; typedef SizeType Size; diff --git a/src/Common/ArrayView.h b/src/Common/ArrayView.h index 2fcc22ba..44423767 100755 --- a/src/Common/ArrayView.h +++ b/src/Common/ArrayView.h @@ -32,7 +32,7 @@ namespace Common { // 'data' == 'nullptr' && 'size' > 0 - Undefined // 'data' != 'nullptr' && 'size' > 0 - NOTEMPTY NOTNIL // For signed integer 'Size', 'ArrayView' with 'size' < 0 is undefined. -template class ArrayView { +template class ArrayView { public: typedef Object ObjectType; typedef Size SizeType; diff --git a/src/Common/base58.cpp b/src/Common/Base58.cpp similarity index 96% rename from src/Common/base58.cpp rename to src/Common/Base58.cpp index cc1a8f4b..538d38f6 100644 --- a/src/Common/base58.cpp +++ b/src/Common/Base58.cpp @@ -15,7 +15,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "base58.h" +#include "Base58.h" #include #include @@ -23,11 +23,11 @@ #include "crypto/hash.h" #include "int-util.h" -#include "varint.h" +#include "Varint.h" -namespace tools +namespace Tools { - namespace base58 + namespace Base58 { namespace { @@ -227,7 +227,7 @@ namespace tools { std::string buf = get_varint_data(tag); buf += data; - crypto::hash hash = crypto::cn_fast_hash(buf.data(), buf.size()); + Crypto::Hash hash = Crypto::cn_fast_hash(buf.data(), buf.size()); const char* hash_data = reinterpret_cast(&hash); buf.append(hash_data, addr_checksum_size); return encode(buf); @@ -244,11 +244,11 @@ namespace tools checksum = addr_data.substr(addr_data.size() - addr_checksum_size); addr_data.resize(addr_data.size() - addr_checksum_size); - crypto::hash hash = crypto::cn_fast_hash(addr_data.data(), addr_data.size()); + Crypto::Hash hash = Crypto::cn_fast_hash(addr_data.data(), addr_data.size()); std::string expected_checksum(reinterpret_cast(&hash), addr_checksum_size); if (expected_checksum != checksum) return false; - int read = tools::read_varint(addr_data.begin(), addr_data.end(), tag); + int read = Tools::read_varint(addr_data.begin(), addr_data.end(), tag); if (read <= 0) return false; data = addr_data.substr(read); diff --git a/src/Common/base58.h b/src/Common/Base58.h old mode 100644 new mode 100755 similarity index 96% rename from src/Common/base58.h rename to src/Common/Base58.h index b4e95901..f358e245 --- a/src/Common/base58.h +++ b/src/Common/Base58.h @@ -20,9 +20,9 @@ #include #include -namespace tools +namespace Tools { - namespace base58 + namespace Base58 { std::string encode(const std::string& data); bool decode(const std::string& enc, std::string& data); diff --git a/src/Common/BlockingQueue.cpp b/src/Common/BlockingQueue.cpp index 3460308b..af636d15 100644 --- a/src/Common/BlockingQueue.cpp +++ b/src/Common/BlockingQueue.cpp @@ -16,3 +16,7 @@ // along with Bytecoin. If not, see . #include "BlockingQueue.h" + +namespace { +char suppressMSVCWarningLNK4221; +} diff --git a/src/Common/command_line.cpp b/src/Common/CommandLine.cpp similarity index 97% rename from src/Common/command_line.cpp rename to src/Common/CommandLine.cpp index dd887a65..af4b7144 100644 --- a/src/Common/command_line.cpp +++ b/src/Common/CommandLine.cpp @@ -15,7 +15,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "command_line.h" +#include "CommandLine.h" namespace command_line { diff --git a/src/Common/command_line.h b/src/Common/CommandLine.h old mode 100644 new mode 100755 similarity index 100% rename from src/Common/command_line.h rename to src/Common/CommandLine.h diff --git a/src/Common/IInputStream.h b/src/Common/IInputStream.h index 2bb867cf..ef8aea62 100755 --- a/src/Common/IInputStream.h +++ b/src/Common/IInputStream.h @@ -24,7 +24,7 @@ namespace Common { class IInputStream { public: virtual ~IInputStream() { } - virtual std::size_t readSome(void* data, std::size_t size) = 0; + virtual size_t readSome(void* data, size_t size) = 0; }; } diff --git a/src/Common/IOutputStream.h b/src/Common/IOutputStream.h index 1f5ef4f9..5361c4aa 100755 --- a/src/Common/IOutputStream.h +++ b/src/Common/IOutputStream.h @@ -24,7 +24,7 @@ namespace Common { class IOutputStream { public: virtual ~IOutputStream() { } - virtual std::size_t writeSome(const void* data, std::size_t size) = 0; + virtual size_t writeSome(const void* data, size_t size) = 0; }; } diff --git a/src/Common/JsonValue.cpp b/src/Common/JsonValue.cpp index be656f75..132ed100 100644 --- a/src/Common/JsonValue.cpp +++ b/src/Common/JsonValue.cpp @@ -492,7 +492,7 @@ const JsonValue::String& JsonValue::getString() const { return *reinterpret_cast(valueString); } -std::size_t JsonValue::size() const { +size_t JsonValue::size() const { switch (type) { case ARRAY: return reinterpret_cast(valueArray)->size(); @@ -503,7 +503,7 @@ std::size_t JsonValue::size() const { } } -JsonValue& JsonValue::operator[](std::size_t index) { +JsonValue& JsonValue::operator[](size_t index) { if (type != ARRAY) { throw std::runtime_error("JsonValue type is not ARRAY"); } @@ -511,7 +511,7 @@ JsonValue& JsonValue::operator[](std::size_t index) { return reinterpret_cast(valueArray)->at(index); } -const JsonValue& JsonValue::operator[](std::size_t index) const { +const JsonValue& JsonValue::operator[](size_t index) const { if (type != ARRAY) { throw std::runtime_error("JsonValue type is not ARRAY"); } @@ -567,7 +567,7 @@ JsonValue& JsonValue::set(const Key& key, JsonValue&& value) { return *this; } -std::size_t JsonValue::erase(const Key& key) { +size_t JsonValue::erase(const Key& key) { return getObject().erase(key); } @@ -595,7 +595,7 @@ std::ostream& operator<<(std::ostream& out, const JsonValue& jsonValue) { out << '['; if (array.size() > 0) { out << array[0]; - for (std::size_t i = 1; i < array.size(); ++i) { + for (size_t i = 1; i < array.size(); ++i) { out << ',' << array[i]; } } @@ -811,7 +811,7 @@ void JsonValue::readNull(std::istream& in) { void JsonValue::readNumber(std::istream& in, char c) { std::string text; text += c; - std::size_t dots = 0; + size_t dots = 0; for (;;) { int i = in.peek(); if (i >= '0' && i <= '9') { diff --git a/src/Common/JsonValue.h b/src/Common/JsonValue.h index 0fc6d2b5..5efe0077 100644 --- a/src/Common/JsonValue.h +++ b/src/Common/JsonValue.h @@ -61,7 +61,7 @@ public: JsonValue(Real value); JsonValue(const String& value); JsonValue(String&& value); - template JsonValue(const char(&value)[size]) { + template JsonValue(const char(&value)[size]) { new(valueString)String(value, size - 1); type = STRING; } @@ -80,7 +80,7 @@ public: JsonValue& operator=(Real value); JsonValue& operator=(const String& value); JsonValue& operator=(String&& value); - template JsonValue& operator=(const char(&value)[size]) { + template JsonValue& operator=(const char(&value)[size]) { if (type != STRING) { destructValue(); type = NIL; @@ -112,10 +112,10 @@ public: String& getString(); const String& getString() const; - std::size_t size() const; + size_t size() const; - JsonValue& operator[](std::size_t index); - const JsonValue& operator[](std::size_t index) const; + JsonValue& operator[](size_t index); + const JsonValue& operator[](size_t index) const; JsonValue& pushBack(const JsonValue& value); JsonValue& pushBack(JsonValue&& value); @@ -129,7 +129,7 @@ public: JsonValue& set(const Key& key, const JsonValue& value); JsonValue& set(const Key& key, JsonValue&& value); - std::size_t erase(const Key& key); + size_t erase(const Key& key); static JsonValue fromString(const std::string& source); std::string toString() const; diff --git a/src/Common/Math.cpp b/src/Common/Math.cpp index 922a231d..495e0ef1 100644 --- a/src/Common/Math.cpp +++ b/src/Common/Math.cpp @@ -16,3 +16,7 @@ // along with Bytecoin. If not, see . #include "Math.h" + +namespace { +char suppressMSVCWarningLNK4221; +} diff --git a/src/CryptoNote/MultisignatureOutput.cpp b/src/Common/MemoryInputStream.cpp old mode 100755 new mode 100644 similarity index 53% rename from src/CryptoNote/MultisignatureOutput.cpp rename to src/Common/MemoryInputStream.cpp index 2e346e2f..88efbef8 --- a/src/CryptoNote/MultisignatureOutput.cpp +++ b/src/Common/MemoryInputStream.cpp @@ -15,29 +15,34 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "MultisignatureOutput.h" +#include "MemoryInputStream.h" +#include #include +#include // memcpy -namespace CryptoNote { +namespace Common { -MultisignatureOutput::MultisignatureOutput(uint64_t amount, std::vector&& keys, uint32_t requiredSignatureCount) : amount(amount), keys(std::move(keys)), requiredSignatureCount(requiredSignatureCount) { - assert(requiredSignatureCount <= keys.size()); +MemoryInputStream::MemoryInputStream(const void* buffer, size_t bufferSize) : +buffer(static_cast(buffer)), bufferSize(bufferSize), position(0) {} + +size_t MemoryInputStream::getPosition() const { + return position; } -uint64_t MultisignatureOutput::getAmount() const { - return amount; +bool MemoryInputStream::endOfStream() const { + return position == bufferSize; } -uint32_t MultisignatureOutput::getKeyCount() const { - return static_cast(keys.size()); -} +size_t MemoryInputStream::readSome(void* data, size_t size) { + assert(position <= bufferSize); + size_t readSize = std::min(size, bufferSize - position); -const crypto::public_key& MultisignatureOutput::getKey(uint32_t index) const { - return keys[index]; -} - -uint32_t MultisignatureOutput::getRequiredSignatureCount() const { - return requiredSignatureCount; + if (readSize > 0) { + memcpy(data, buffer + position, readSize); + position += readSize; + } + + return readSize; } } diff --git a/src/CryptoNote/KeyOutput.h b/src/Common/MemoryInputStream.h old mode 100755 new mode 100644 similarity index 66% rename from src/CryptoNote/KeyOutput.h rename to src/Common/MemoryInputStream.h index 7f53420c..017dc655 --- a/src/CryptoNote/KeyOutput.h +++ b/src/Common/MemoryInputStream.h @@ -17,21 +17,22 @@ #pragma once -#include "../crypto/crypto.h" +#include "IInputStream.h" -namespace CryptoNote { +namespace Common { -class KeyOutput { -public: - KeyOutput(uint64_t amount, const crypto::public_key& key); - KeyOutput(const KeyOutput& other) = delete; - KeyOutput& operator=(const KeyOutput& other) = delete; - uint64_t getAmount() const; - const crypto::public_key& getKey() const; - -private: - uint64_t amount; - crypto::public_key key; -}; + class MemoryInputStream : public IInputStream { + public: + MemoryInputStream(const void* buffer, size_t bufferSize); + size_t getPosition() const; + bool endOfStream() const; + + // IInputStream + virtual size_t readSome(void* data, size_t size) override; + private: + const char* buffer; + size_t bufferSize; + size_t position; + }; } diff --git a/src/Common/ObserverManager.h b/src/Common/ObserverManager.h old mode 100644 new mode 100755 index c383113b..585041db --- a/src/Common/ObserverManager.h +++ b/src/Common/ObserverManager.h @@ -21,7 +21,7 @@ #include #include -namespace tools { +namespace Tools { template class ObserverManager { @@ -39,6 +39,7 @@ public: bool remove(T* observer) { std::unique_lock lock(m_observersMutex); + auto it = std::find(m_observers.begin(), m_observers.end(), observer); if (m_observers.end() == it) { return false; diff --git a/src/Common/SignalHandler.cpp b/src/Common/SignalHandler.cpp old mode 100644 new mode 100755 index 3795df3d..c78bf3e3 --- a/src/Common/SignalHandler.cpp +++ b/src/Common/SignalHandler.cpp @@ -27,6 +27,7 @@ #include #else #include +#include #endif namespace { @@ -65,7 +66,7 @@ void posixHandler(int /*type*/) { } -namespace tools { +namespace Tools { bool SignalHandler::install(std::function t) { @@ -76,9 +77,23 @@ namespace tools { } return r; #else - signal(SIGINT, posixHandler); - signal(SIGTERM, posixHandler); - signal(SIGPIPE, SIG_IGN); + struct sigaction newMask; + std::memset(&newMask, 0, sizeof(struct sigaction)); + newMask.sa_handler = posixHandler; + if (sigaction(SIGINT, &newMask, nullptr) != 0) { + return false; + } + + if (sigaction(SIGTERM, &newMask, nullptr) != 0) { + return false; + } + + std::memset(&newMask, 0, sizeof(struct sigaction)); + newMask.sa_handler = SIG_IGN; + if (sigaction(SIGPIPE, &newMask, nullptr) != 0) { + return false; + } + m_handler = t; return true; #endif diff --git a/src/Common/SignalHandler.h b/src/Common/SignalHandler.h old mode 100644 new mode 100755 index 14033c9a..1807a633 --- a/src/Common/SignalHandler.h +++ b/src/Common/SignalHandler.h @@ -19,7 +19,7 @@ #include -namespace tools { +namespace Tools { class SignalHandler { diff --git a/src/Common/StdInputStream.cpp b/src/Common/StdInputStream.cpp new file mode 100644 index 00000000..3e5dcc08 --- /dev/null +++ b/src/Common/StdInputStream.cpp @@ -0,0 +1,30 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "StdInputStream.h" + +namespace Common { + +StdInputStream::StdInputStream(std::istream& in) : in(in) { +} + +size_t StdInputStream::readSome(void* data, size_t size) { + in.read(static_cast(data), size); + return in.gcount(); +} + +} diff --git a/src/Common/StdInputStream.h b/src/Common/StdInputStream.h new file mode 100644 index 00000000..4efb5ea7 --- /dev/null +++ b/src/Common/StdInputStream.h @@ -0,0 +1,35 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include "IInputStream.h" + +namespace Common { + +class StdInputStream : public IInputStream { +public: + StdInputStream(std::istream& in); + StdInputStream& operator=(const StdInputStream&) = delete; + size_t readSome(void* data, size_t size) override; + +private: + std::istream& in; +}; + +} diff --git a/src/Common/StdOutputStream.cpp b/src/Common/StdOutputStream.cpp new file mode 100644 index 00000000..2788c5a0 --- /dev/null +++ b/src/Common/StdOutputStream.cpp @@ -0,0 +1,34 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "StdOutputStream.h" + +namespace Common { + +StdOutputStream::StdOutputStream(std::ostream& out) : out(out) { +} + +size_t StdOutputStream::writeSome(const void* data, size_t size) { + out.write(static_cast(data), size); + if (out.bad()) { + return 0; + } + + return size; +} + +} diff --git a/src/Common/StdOutputStream.h b/src/Common/StdOutputStream.h new file mode 100644 index 00000000..2c0dbc51 --- /dev/null +++ b/src/Common/StdOutputStream.h @@ -0,0 +1,35 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include "IOutputStream.h" + +namespace Common { + +class StdOutputStream : public IOutputStream { +public: + StdOutputStream(std::ostream& out); + StdOutputStream& operator=(const StdOutputStream&) = delete; + size_t writeSome(const void* data, size_t size) override; + +private: + std::ostream& out; +}; + +} diff --git a/src/Common/StreamTools.cpp b/src/Common/StreamTools.cpp index 304c458c..821d7cc2 100755 --- a/src/Common/StreamTools.cpp +++ b/src/Common/StreamTools.cpp @@ -22,9 +22,9 @@ namespace Common { -void read(IInputStream& in, void* data, std::size_t size) { +void read(IInputStream& in, void* data, size_t size) { while (size > 0) { - std::size_t readSize = in.readSome(data, size); + size_t readSize = in.readSome(data, size); if (readSize == 0) { throw std::runtime_error("Failed to read from IInputStream"); } @@ -72,12 +72,12 @@ void read(IInputStream& in, uint64_t& value) { read(in, &value, sizeof(value)); } -void read(IInputStream& in, std::vector& data, std::size_t size) { +void read(IInputStream& in, std::vector& data, size_t size) { data.resize(size); read(in, data.data(), size); } -void read(IInputStream& in, std::string& data, std::size_t size) { +void read(IInputStream& in, std::string& data, size_t size) { std::vector temp(size); read(in, temp.data(), size); data.assign(temp.data(), size); @@ -92,7 +92,7 @@ void readVarint(IInputStream& in, uint8_t& value) { throw std::runtime_error("readVarint, value overflow"); } - temp |= static_cast(piece & 0x7f) << shift; + temp |= static_cast(piece & 0x7f) << shift; if ((piece & 0x80) == 0) { if (piece == 0 && shift != 0) { throw std::runtime_error("readVarint, invalid value representation"); @@ -114,7 +114,7 @@ void readVarint(IInputStream& in, uint16_t& value) { throw std::runtime_error("readVarint, value overflow"); } - temp |= static_cast(piece & 0x7f) << shift; + temp |= static_cast(piece & 0x7f) << shift; if ((piece & 0x80) == 0) { if (piece == 0 && shift != 0) { throw std::runtime_error("readVarint, invalid value representation"); @@ -136,7 +136,7 @@ void readVarint(IInputStream& in, uint32_t& value) { throw std::runtime_error("readVarint, value overflow"); } - temp |= static_cast(piece & 0x7f) << shift; + temp |= static_cast(piece & 0x7f) << shift; if ((piece & 0x80) == 0) { if (piece == 0 && shift != 0) { throw std::runtime_error("readVarint, invalid value representation"); @@ -158,7 +158,7 @@ void readVarint(IInputStream& in, uint64_t& value) { throw std::runtime_error("readVarint, value overflow"); } - temp |= static_cast(piece & 0x7f) << shift; + temp |= static_cast(piece & 0x7f) << shift; if ((piece & 0x80) == 0) { if (piece == 0 && shift != 0) { throw std::runtime_error("readVarint, invalid value representation"); @@ -171,9 +171,9 @@ void readVarint(IInputStream& in, uint64_t& value) { value = temp; } -void write(IOutputStream& out, const void* data, std::size_t size) { +void write(IOutputStream& out, const void* data, size_t size) { while (size > 0) { - std::size_t writtenSize = out.writeSome(data, size); + size_t writtenSize = out.writeSome(data, size); if (writtenSize == 0) { throw std::runtime_error("Failed to write to IOutputStream"); } diff --git a/src/Common/StreamTools.h b/src/Common/StreamTools.h index 578ef33d..44e25a70 100755 --- a/src/Common/StreamTools.h +++ b/src/Common/StreamTools.h @@ -26,7 +26,7 @@ namespace Common { class IInputStream; class IOutputStream; -void read(IInputStream& in, void* data, std::size_t size); +void read(IInputStream& in, void* data, size_t size); void read(IInputStream& in, int8_t& value); void read(IInputStream& in, int16_t& value); void read(IInputStream& in, int32_t& value); @@ -35,14 +35,14 @@ void read(IInputStream& in, uint8_t& value); void read(IInputStream& in, uint16_t& value); void read(IInputStream& in, uint32_t& value); void read(IInputStream& in, uint64_t& value); -void read(IInputStream& in, std::vector& data, std::size_t size); -void read(IInputStream& in, std::string& data, std::size_t size); +void read(IInputStream& in, std::vector& data, size_t size); +void read(IInputStream& in, std::string& data, size_t size); void readVarint(IInputStream& in, uint8_t& value); void readVarint(IInputStream& in, uint16_t& value); void readVarint(IInputStream& in, uint32_t& value); void readVarint(IInputStream& in, uint64_t& value); -void write(IOutputStream& out, const void* data, std::size_t size); +void write(IOutputStream& out, const void* data, size_t size); void write(IOutputStream& out, int8_t value); void write(IOutputStream& out, int16_t value); void write(IOutputStream& out, int32_t value); @@ -61,7 +61,7 @@ template T read(IInputStream& in) { return value; } -template T read(IInputStream& in, std::size_t size) { +template T read(IInputStream& in, size_t size) { T value; read(in, value, size); return value; diff --git a/src/Common/StringBuffer.h b/src/Common/StringBuffer.h index 0b7053fb..4b293180 100755 --- a/src/Common/StringBuffer.h +++ b/src/Common/StringBuffer.h @@ -24,10 +24,10 @@ namespace Common { // 'StringBuffer' is a string of fixed maximum size. -template class StringBuffer { +template class StringBuffer { public: typedef char Object; - typedef std::size_t Size; + typedef size_t Size; const static Size MAXIMUM_SIZE = MAXIMUM_SIZE_VALUE; const static Size INVALID; @@ -549,6 +549,6 @@ protected: Size size; }; -template const typename StringBuffer::Size StringBuffer::INVALID = std::numeric_limits::Size>::max(); +template const typename StringBuffer::Size StringBuffer::INVALID = std::numeric_limits::Size>::max(); } diff --git a/src/Common/StringInputStream.cpp b/src/Common/StringInputStream.cpp index 9ec09498..0aefe353 100755 --- a/src/Common/StringInputStream.cpp +++ b/src/Common/StringInputStream.cpp @@ -23,7 +23,7 @@ namespace Common { StringInputStream::StringInputStream(const std::string& in) : in(in), offset(0) { } -std::size_t StringInputStream::readSome(void* data, std::size_t size) { +size_t StringInputStream::readSome(void* data, size_t size) { if (size > in.size() - offset) { size = in.size() - offset; } diff --git a/src/Common/StringInputStream.h b/src/Common/StringInputStream.h index af6cf78e..cb25c685 100755 --- a/src/Common/StringInputStream.h +++ b/src/Common/StringInputStream.h @@ -25,11 +25,11 @@ namespace Common { class StringInputStream : public IInputStream { public: StringInputStream(const std::string& in); - std::size_t readSome(void* data, std::size_t size) override; + size_t readSome(void* data, size_t size) override; private: const std::string& in; - std::size_t offset; + size_t offset; }; } diff --git a/src/Common/StringOutputStream.cpp b/src/Common/StringOutputStream.cpp index 07c9df90..ce4ae994 100755 --- a/src/Common/StringOutputStream.cpp +++ b/src/Common/StringOutputStream.cpp @@ -22,7 +22,7 @@ namespace Common { StringOutputStream::StringOutputStream(std::string& out) : out(out) { } -std::size_t StringOutputStream::writeSome(const void* data, std::size_t size) { +size_t StringOutputStream::writeSome(const void* data, size_t size) { out.append(static_cast(data), size); return size; } diff --git a/src/Common/StringOutputStream.h b/src/Common/StringOutputStream.h index 31c8a9c5..528a13cf 100755 --- a/src/Common/StringOutputStream.h +++ b/src/Common/StringOutputStream.h @@ -25,7 +25,7 @@ namespace Common { class StringOutputStream : public IOutputStream { public: StringOutputStream(std::string& out); - std::size_t writeSome(const void* data, std::size_t size) override; + size_t writeSome(const void* data, size_t size) override; private: std::string& out; diff --git a/src/Common/StringTools.cpp b/src/Common/StringTools.cpp index 750718b1..200d2ced 100755 --- a/src/Common/StringTools.cpp +++ b/src/Common/StringTools.cpp @@ -43,7 +43,7 @@ const uint8_t characterValues[256] = { } -std::string asString(const void* data, std::size_t size) { +std::string asString(const void* data, size_t size) { return std::string(static_cast(data), size); } @@ -51,6 +51,11 @@ std::string asString(const std::vector& data) { return std::string(reinterpret_cast(data.data()), data.size()); } +std::vector asBinaryArray(const std::string& data) { + auto dataPtr = reinterpret_cast(data.data()); + return std::vector(dataPtr, dataPtr + data.size()); +} + uint8_t fromHex(char character) { uint8_t value = characterValues[static_cast(character)]; if (value > 0x0f) { @@ -69,7 +74,7 @@ bool fromHex(char character, uint8_t& value) { return true; } -std::size_t fromHex(const std::string& text, void* data, std::size_t bufferSize) { +size_t fromHex(const std::string& text, void* data, size_t bufferSize) { if ((text.size() & 1) != 0) { throw std::runtime_error("fromHex: invalid string size"); } @@ -78,14 +83,14 @@ std::size_t fromHex(const std::string& text, void* data, std::size_t bufferSize) throw std::runtime_error("fromHex: invalid buffer size"); } - for (std::size_t i = 0; i < text.size() >> 1; ++i) { + for (size_t i = 0; i < text.size() >> 1; ++i) { static_cast(data)[i] = fromHex(text[i << 1]) << 4 | fromHex(text[(i << 1) + 1]); } return text.size() >> 1; } -bool fromHex(const std::string& text, void* data, std::size_t bufferSize, std::size_t& size) { +bool fromHex(const std::string& text, void* data, size_t bufferSize, size_t& size) { if ((text.size() & 1) != 0) { return false; } @@ -94,7 +99,7 @@ bool fromHex(const std::string& text, void* data, std::size_t bufferSize, std::s return false; } - for (std::size_t i = 0; i < text.size() >> 1; ++i) { + for (size_t i = 0; i < text.size() >> 1; ++i) { uint8_t value1; if (!fromHex(text[i << 1], value1)) { return false; @@ -118,7 +123,7 @@ std::vector fromHex(const std::string& text) { } std::vector data(text.size() >> 1); - for (std::size_t i = 0; i < data.size(); ++i) { + for (size_t i = 0; i < data.size(); ++i) { data[i] = fromHex(text[i << 1]) << 4 | fromHex(text[(i << 1) + 1]); } @@ -130,7 +135,7 @@ bool fromHex(const std::string& text, std::vector& data) { return false; } - for (std::size_t i = 0; i < text.size() >> 1; ++i) { + for (size_t i = 0; i < text.size() >> 1; ++i) { uint8_t value1; if (!fromHex(text[i << 1], value1)) { return false; @@ -147,9 +152,9 @@ bool fromHex(const std::string& text, std::vector& data) { return true; } -std::string toHex(const void* data, std::size_t size) { +std::string toHex(const void* data, size_t size) { std::string text; - for (std::size_t i = 0; i < size; ++i) { + for (size_t i = 0; i < size; ++i) { text += "0123456789abcdef"[static_cast(data)[i] >> 4]; text += "0123456789abcdef"[static_cast(data)[i] & 15]; } @@ -157,8 +162,8 @@ std::string toHex(const void* data, std::size_t size) { return text; } -void toHex(const void* data, std::size_t size, std::string& text) { - for (std::size_t i = 0; i < size; ++i) { +void toHex(const void* data, size_t size, std::string& text) { + for (size_t i = 0; i < size; ++i) { text += "0123456789abcdef"[static_cast(data)[i] >> 4]; text += "0123456789abcdef"[static_cast(data)[i] & 15]; } @@ -166,7 +171,7 @@ void toHex(const void* data, std::size_t size, std::string& text) { std::string toHex(const std::vector& data) { std::string text; - for (std::size_t i = 0; i < data.size(); ++i) { + for (size_t i = 0; i < data.size(); ++i) { text += "0123456789abcdef"[data[i] >> 4]; text += "0123456789abcdef"[data[i] & 15]; } @@ -175,14 +180,14 @@ std::string toHex(const std::vector& data) { } void toHex(const std::vector& data, std::string& text) { - for (std::size_t i = 0; i < data.size(); ++i) { + for (size_t i = 0; i < data.size(); ++i) { text += "0123456789abcdef"[data[i] >> 4]; text += "0123456789abcdef"[data[i] & 15]; } } std::string extract(std::string& text, char delimiter) { - std::size_t delimiterPosition = text.find(delimiter); + size_t delimiterPosition = text.find(delimiter); std::string subText; if (delimiterPosition != std::string::npos) { subText = text.substr(0, delimiterPosition); @@ -194,8 +199,8 @@ std::string extract(std::string& text, char delimiter) { return subText; } -std::string extract(const std::string& text, char delimiter, std::size_t& offset) { - std::size_t delimiterPosition = text.find(delimiter, offset); +std::string extract(const std::string& text, char delimiter, size_t& offset) { + size_t delimiterPosition = text.find(delimiter, offset); if (delimiterPosition != std::string::npos) { offset = delimiterPosition + 1; return text.substr(offset, delimiterPosition); diff --git a/src/Common/StringTools.h b/src/Common/StringTools.h index c8e70cca..a56203a4 100755 --- a/src/Common/StringTools.h +++ b/src/Common/StringTools.h @@ -24,24 +24,25 @@ namespace Common { -std::string asString(const void* data, std::size_t size); // Does not throw +std::string asString(const void* data, size_t size); // Does not throw std::string asString(const std::vector& data); // Does not throw +std::vector asBinaryArray(const std::string& data); uint8_t fromHex(char character); // Returns value of hex 'character', throws on error bool fromHex(char character, uint8_t& value); // Assigns value of hex 'character' to 'value', returns false on error, does not throw -std::size_t fromHex(const std::string& text, void* data, std::size_t bufferSize); // Assigns values of hex 'text' to buffer 'data' up to 'bufferSize', returns actual data size, throws on error -bool fromHex(const std::string& text, void* data, std::size_t bufferSize, std::size_t& size); // Assigns values of hex 'text' to buffer 'data' up to 'bufferSize', assigns actual data size to 'size', returns false on error, does not throw +size_t fromHex(const std::string& text, void* data, size_t bufferSize); // Assigns values of hex 'text' to buffer 'data' up to 'bufferSize', returns actual data size, throws on error +bool fromHex(const std::string& text, void* data, size_t bufferSize, size_t& size); // Assigns values of hex 'text' to buffer 'data' up to 'bufferSize', assigns actual data size to 'size', returns false on error, does not throw std::vector fromHex(const std::string& text); // Returns values of hex 'text', throws on error bool fromHex(const std::string& text, std::vector& data); // Appends values of hex 'text' to 'data', returns false on error, does not throw template bool podFromHex(const std::string& text, T& val) { - std::size_t outSize; + size_t outSize; return fromHex(text, &val, sizeof(val), outSize) && outSize == sizeof(val); } -std::string toHex(const void* data, std::size_t size); // Returns hex representation of ('data', 'size'), does not throw -void toHex(const void* data, std::size_t size, std::string& text); // Appends hex representation of ('data', 'size') to 'text', does not throw +std::string toHex(const void* data, size_t size); // Returns hex representation of ('data', 'size'), does not throw +void toHex(const void* data, size_t size, std::string& text); // Appends hex representation of ('data', 'size') to 'text', does not throw std::string toHex(const std::vector& data); // Returns hex representation of 'data', does not throw void toHex(const std::vector& data, std::string& text); // Appends hex representation of 'data' to 'text', does not throw @@ -51,7 +52,7 @@ std::string podToHex(const T& s) { } std::string extract(std::string& text, char delimiter); // Does not throw -std::string extract(const std::string& text, char delimiter, std::size_t& offset); // Does not throw +std::string extract(const std::string& text, char delimiter, size_t& offset); // Does not throw template T fromString(const std::string& text) { // Throws on error T value; @@ -72,7 +73,7 @@ template bool fromString(const std::string& text, T& value) { // Doe template std::vector fromDelimitedString(const std::string& source, char delimiter) { // Throws on error std::vector data; - for (std::size_t offset = 0; offset != source.size();) { + for (size_t offset = 0; offset != source.size();) { data.emplace_back(fromString(extract(source, delimiter, offset))); } @@ -80,7 +81,7 @@ template std::vector fromDelimitedString(const std::string& sourc } template bool fromDelimitedString(const std::string& source, char delimiter, std::vector& data) { // Does not throw - for (std::size_t offset = 0; offset != source.size();) { + for (size_t offset = 0; offset != source.size();) { T value; if (!fromString(extract(source, delimiter, offset), value)) { return false; diff --git a/src/Common/StringView.h b/src/Common/StringView.h index 6a1eb85b..526dd714 100755 --- a/src/Common/StringView.h +++ b/src/Common/StringView.h @@ -32,7 +32,7 @@ namespace Common { class StringView { public: typedef char Object; - typedef std::size_t Size; + typedef size_t Size; const static Size INVALID; const static StringView EMPTY; diff --git a/src/Common/util.cpp b/src/Common/Util.cpp similarity index 99% rename from src/Common/util.cpp rename to src/Common/Util.cpp index 994929c6..8c8dec57 100644 --- a/src/Common/util.cpp +++ b/src/Common/Util.cpp @@ -15,12 +15,12 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "util.h" +#include "Util.h" #include #include -#include "cryptonote_config.h" +#include "CryptoNoteConfig.h" #ifdef WIN32 #include @@ -31,7 +31,7 @@ #endif -namespace tools +namespace Tools { #ifdef WIN32 std::string get_windows_version_display_string() @@ -296,7 +296,7 @@ std::string get_nix_version_display_string() } #endif - std::string get_default_data_dir() + std::string getDefaultDataDirectory() { //namespace fs = boost::filesystem; // Windows < Vista: C:\Documents and Settings\Username\Application Data\CRYPTONOTE_NAME diff --git a/src/Common/util.h b/src/Common/Util.h old mode 100644 new mode 100755 similarity index 94% rename from src/Common/util.h rename to src/Common/Util.h index e3b67818..bf4eac15 --- a/src/Common/util.h +++ b/src/Common/Util.h @@ -20,9 +20,9 @@ #include #include -namespace tools +namespace Tools { - std::string get_default_data_dir(); + std::string getDefaultDataDirectory(); std::string get_os_version_string(); bool create_directories_if_necessary(const std::string& path); std::error_code replace_file(const std::string& replacement_name, const std::string& replaced_name); diff --git a/src/Common/varint.h b/src/Common/Varint.h old mode 100644 new mode 100755 similarity index 99% rename from src/Common/varint.h rename to src/Common/Varint.h index fd97b696..2d7e4b07 --- a/src/Common/varint.h +++ b/src/Common/Varint.h @@ -23,7 +23,7 @@ #include #include -namespace tools { +namespace Tools { template typename std::enable_if::value && std::is_unsigned::value, void>::type diff --git a/src/Common/VectorOutputStream.cpp b/src/Common/VectorOutputStream.cpp new file mode 100644 index 00000000..283f5d35 --- /dev/null +++ b/src/Common/VectorOutputStream.cpp @@ -0,0 +1,30 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "VectorOutputStream.h" + +namespace Common { + +VectorOutputStream::VectorOutputStream(std::vector& out) : out(out) { +} + +size_t VectorOutputStream::writeSome(const void* data, size_t size) { + out.insert(out.end(), static_cast(data), static_cast(data) + size); + return size; +} + +} diff --git a/src/Common/VectorOutputStream.h b/src/Common/VectorOutputStream.h new file mode 100644 index 00000000..564f267e --- /dev/null +++ b/src/Common/VectorOutputStream.h @@ -0,0 +1,36 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include "IOutputStream.h" + +namespace Common { + +class VectorOutputStream : public IOutputStream { +public: + VectorOutputStream(std::vector& out); + VectorOutputStream& operator=(const VectorOutputStream&) = delete; + size_t writeSome(const void* data, size_t size) override; + +private: + std::vector& out; +}; + +} diff --git a/src/Common/static_assert.h b/src/Common/static_assert.h old mode 100755 new mode 100644 index a893d24a..e7b4ce0d --- a/src/Common/static_assert.h +++ b/src/Common/static_assert.h @@ -1,26 +1,26 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#ifndef __cplusplus -#ifdef __clang__ - -#define static_assert _Static_assert - -#endif -#endif +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#ifndef __cplusplus +#ifdef __clang__ + +#define static_assert _Static_assert + +#endif +#endif diff --git a/src/connectivity_tool/conn_tool.cpp b/src/ConnectivityTool/ConnectivityTool.cpp old mode 100644 new mode 100755 similarity index 88% rename from src/connectivity_tool/conn_tool.cpp rename to src/ConnectivityTool/ConnectivityTool.cpp index d2b5a2a3..231ef33a --- a/src/connectivity_tool/conn_tool.cpp +++ b/src/ConnectivityTool/ConnectivityTool.cpp @@ -18,6 +18,8 @@ #include #include +#include +#include #include #include #include @@ -27,14 +29,14 @@ #include #include -#include "Common/command_line.h" +#include "Common/CommandLine.h" #include "Common/StringTools.h" #include "crypto/crypto.h" -#include "p2p/p2p_protocol_defs.h" -#include "p2p/LevinProtocol.h" -#include "rpc/core_rpc_server_commands_defs.h" -#include "rpc/HttpClient.h" -#include "serialization/SerializationTools.h" +#include "P2p/P2pProtocolDefinitions.h" +#include "P2p/LevinProtocol.h" +#include "Rpc/CoreRpcServerCommandsDefinitions.h" +#include "Rpc/HttpClient.h" +#include "Serialization/SerializationTools.h" #include "version.h" namespace po = boost::program_options; @@ -65,33 +67,26 @@ struct response_schema { boost::optional ns_rsp; }; - -template -void withTimeout(System::Dispatcher& dispatcher, SystemObj& obj, unsigned timeout, std::function f) { - System::Event timeoutEvent(dispatcher); - System::Timer timeoutTimer(dispatcher); - - dispatcher.spawn([&](){ +void withTimeout(System::Dispatcher& dispatcher, unsigned timeout, std::function f) { + std::string result; + System::ContextGroup cg(dispatcher); + System::ContextGroupTimeout cgTimeout(dispatcher, cg, std::chrono::milliseconds(timeout)); + + cg.spawn([&] { try { - timeoutTimer.sleep(std::chrono::milliseconds(timeout)); - obj.stop(); - } catch (std::exception&) {} - timeoutEvent.set(); + f(); + } catch (System::InterruptedException&) { + result = "Operation timeout"; + } catch (std::exception& e) { + result = e.what(); + } }); - try { - f(); - } catch (System::InterruptedException&) { - timeoutEvent.wait(); - throw std::runtime_error("Operation timeout"); - } catch (std::exception&) { - timeoutTimer.stop(); - timeoutEvent.wait(); - throw; - } + cg.wait(); - timeoutTimer.stop(); - timeoutEvent.wait(); + if (!result.empty()) { + throw std::runtime_error(result); + } } @@ -125,7 +120,7 @@ std::ostream& get_response_schema_as_json(std::ostream& ss, response_schema &rs) ss << " ]," << ENDL; ss << " \"local_peerlist_white\": [" << ENDL; i = 0; - for (const peerlist_entry &pe : networkState.local_peerlist_white) { + for (const PeerlistEntry &pe : networkState.local_peerlist_white) { ss << " {\"peer_id\": \"" << pe.id << "\", \"ip\": \"" << Common::ipAddressToString(pe.adr.ip) << "\", \"port\": " << pe.adr.port << ", \"last_seen\": " << networkState.local_time - pe.last_seen << "}"; if (networkState.local_peerlist_white.size() - 1 != i) ss << ","; @@ -136,7 +131,7 @@ std::ostream& get_response_schema_as_json(std::ostream& ss, response_schema &rs) ss << " \"local_peerlist_gray\": [" << ENDL; i = 0; - for (const peerlist_entry &pe : networkState.local_peerlist_gray) { + for (const PeerlistEntry &pe : networkState.local_peerlist_gray) { ss << " {\"peer_id\": \"" << pe.id << "\", \"ip\": \"" << Common::ipAddressToString(pe.adr.ip) << "\", \"port\": " << pe.adr.port << ", \"last_seen\": " << networkState.local_time - pe.last_seen << "}"; if (networkState.local_peerlist_gray.size() - 1 != i) ss << ","; @@ -178,12 +173,12 @@ bool print_COMMAND_REQUEST_NETWORK_STATE(const COMMAND_REQUEST_NETWORK_STATE::re } std::cout << "Peer list white:" << ns.my_id << ENDL; - for (const peerlist_entry &pe : ns.local_peerlist_white) { + for (const PeerlistEntry &pe : ns.local_peerlist_white) { std::cout << pe.id << "\t" << pe.adr << "\t" << Common::timeIntervalToString(ns.local_time - pe.last_seen) << ENDL; } std::cout << "Peer list gray:" << ns.my_id << ENDL; - for (const peerlist_entry &pe : ns.local_peerlist_gray) { + for (const PeerlistEntry &pe : ns.local_peerlist_gray) { std::cout << pe.id << "\t" << pe.adr << "\t" << Common::timeIntervalToString(ns.local_time - pe.last_seen) << ENDL; } @@ -224,13 +219,13 @@ bool handle_get_daemon_info(po::variables_map& vm) { return true; } //--------------------------------------------------------------------------------------------------------------- -bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) { +bool handle_request_stat(po::variables_map& vm, PeerIdType peer_id) { if(!command_line::has_arg(vm, arg_priv_key)) { std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "secret key not set \"" << ENDL << "}"; return false; } - crypto::secret_key prvk; + Crypto::SecretKey prvk; if (!Common::podFromHex(command_line::get_arg(vm, arg_priv_key), prvk)) { std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "wrong secret key set \"" << ENDL << "}"; return false; @@ -250,7 +245,7 @@ bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) { System::TcpConnection connection; - withTimeout(dispatcher, connector, timeout, [&] { + withTimeout(dispatcher, timeout, [&] { connection = connector.connect(addr, command_line::get_arg(vm, arg_port)); }); @@ -261,7 +256,7 @@ bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) { if (!peer_id) { COMMAND_REQUEST_PEER_ID::request req; COMMAND_REQUEST_PEER_ID::response rsp; - withTimeout(dispatcher, connection, timeout, [&] { + withTimeout(dispatcher, timeout, [&] { levin.invoke(COMMAND_REQUEST_PEER_ID::ID, req, rsp); }); peer_id = rsp.my_id; @@ -270,10 +265,10 @@ bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) { proof_of_trust pot; pot.peer_id = peer_id; pot.time = time(NULL); - crypto::public_key pubk; + Crypto::PublicKey pubk; Common::podFromHex(P2P_STAT_TRUSTED_PUB_KEY, pubk); - crypto::hash h = get_proof_of_trust_hash(pot); - crypto::generate_signature(h, pubk, prvk, pot.sign); + Crypto::Hash h = get_proof_of_trust_hash(pot); + Crypto::generate_signature(h, pubk, prvk, pot.sign); if (command_line::get_arg(vm, arg_request_stat_info)) { COMMAND_REQUEST_STAT_INFO::request req; @@ -282,7 +277,7 @@ bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) { req.tr = pot; try { - withTimeout(dispatcher, connection, timeout, [&] { + withTimeout(dispatcher, timeout, [&] { levin.invoke(COMMAND_REQUEST_STAT_INFO::ID, req, res); }); rs.si_rsp = std::move(res); @@ -299,12 +294,12 @@ bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) { if (command_line::get_arg(vm, arg_request_net_state)) { ++pot.time; h = get_proof_of_trust_hash(pot); - crypto::generate_signature(h, pubk, prvk, pot.sign); + Crypto::generate_signature(h, pubk, prvk, pot.sign); COMMAND_REQUEST_NETWORK_STATE::request req{ pot }; COMMAND_REQUEST_NETWORK_STATE::response res; try { - withTimeout(dispatcher, connection, timeout, [&] { + withTimeout(dispatcher, timeout, [&] { levin.invoke(COMMAND_REQUEST_NETWORK_STATE::ID, req, res); }); rs.ns_rsp = std::move(res); @@ -328,8 +323,8 @@ bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) { //--------------------------------------------------------------------------------------------------------------- bool generate_and_print_keys() { - crypto::public_key pk; - crypto::secret_key sk; + Crypto::PublicKey pk; + Crypto::SecretKey sk; generate_keys(pk, sk); std::cout << "PUBLIC KEY: " << Common::podToHex(pk) << ENDL << "PRIVATE KEY: " << Common::podToHex(sk); diff --git a/src/CryptoNote/BaseTransaction.cpp b/src/CryptoNote/BaseTransaction.cpp deleted file mode 100755 index b4411aa8..00000000 --- a/src/CryptoNote/BaseTransaction.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "BaseTransaction.h" -#include -#include - -namespace CryptoNote { - -BaseTransaction::BaseTransaction( - uint64_t blockIndex, - uint64_t unlockTime, - std::vector&& keyOutputs, - std::vector&& multisignatureOutputs, - std::vector&& extra) : - blockIndex(blockIndex), - unlockTime(unlockTime), - keyOutputs(std::move(keyOutputs)), - multisignatureOutputs(std::move(multisignatureOutputs)), - extra(std::move(extra)) { -} - -BaseTransaction::BaseTransaction(BaseTransaction&& other) : blockIndex(other.blockIndex), unlockTime(other.unlockTime), keyOutputs(std::move(other.keyOutputs)), multisignatureOutputs(std::move(other.multisignatureOutputs)), extra(std::move(other.extra)) { -} - -uint64_t BaseTransaction::getBlockIndex() const { - return blockIndex; -} - -uint64_t BaseTransaction::getUnlockTime() const { - return unlockTime; -} - -uint32_t BaseTransaction::getOutputCount() const { - return static_cast(keyOutputs.size() + multisignatureOutputs.size()); -} - -BaseTransaction::OutputType BaseTransaction::getOutputType(uint32_t index) const { - auto iterator = std::lower_bound(keyOutputs.begin(), keyOutputs.end(), index, [](const KeyOutputEntry& keyOutputEntry, uint32_t index)->bool { return keyOutputEntry.index < index; }); - if (iterator != keyOutputs.end() && iterator->index == index) { - return KEY_OUTPUT; - } - - return MULTISIGNATURE_OUTPUT; -} - -const KeyOutput& BaseTransaction::getKeyOutput(uint32_t index) const { - auto iterator = std::lower_bound(keyOutputs.begin(), keyOutputs.end(), index, [](const KeyOutputEntry& keyOutputEntry, uint32_t index)->bool { return keyOutputEntry.index < index; }); - assert(iterator != keyOutputs.end()); - assert(iterator->index == index); - return iterator->output; -} - -const MultisignatureOutput& BaseTransaction::getMultisignatureOutput(uint32_t index) const { - auto iterator = std::lower_bound(multisignatureOutputs.begin(), multisignatureOutputs.end(), index, [](const MultisignatureOutputEntry& multisignatureOutputEntry, uint32_t index)->bool { return multisignatureOutputEntry.index < index; }); - assert(iterator != multisignatureOutputs.end()); - assert(iterator->index == index); - return iterator->output; -} - -const std::vector& BaseTransaction::getExtra() const { - return extra; -} - -} diff --git a/src/CryptoNote/BaseTransaction.h b/src/CryptoNote/BaseTransaction.h deleted file mode 100755 index d795cfd9..00000000 --- a/src/CryptoNote/BaseTransaction.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include "KeyOutput.h" -#include "MultisignatureOutput.h" - -namespace CryptoNote { - -class BaseTransaction { -public: - enum OutputType { - KEY_OUTPUT = 0, - MULTISIGNATURE_OUTPUT = 1 - }; - - struct KeyOutputEntry { - uint32_t index; - KeyOutput output; - }; - - struct MultisignatureOutputEntry { - uint32_t index; - MultisignatureOutput output; - }; - - BaseTransaction(uint64_t blockIndex, uint64_t unlockTime, std::vector&& keyOutputs, std::vector&& multisignatureOutputs, std::vector&& extra); - BaseTransaction(const BaseTransaction& other) = delete; - BaseTransaction(BaseTransaction&& other); - BaseTransaction& operator=(const BaseTransaction& other) = delete; - uint64_t getBlockIndex() const; - uint64_t getUnlockTime() const; - uint32_t getOutputCount() const; - OutputType getOutputType(uint32_t index) const; - const KeyOutput& getKeyOutput(uint32_t index) const; - const MultisignatureOutput& getMultisignatureOutput(uint32_t index) const; - const std::vector& getExtra() const; - -private: - uint64_t blockIndex; - uint64_t unlockTime; - std::vector keyOutputs; - std::vector multisignatureOutputs; - std::vector extra; -}; - -} diff --git a/src/CryptoNote/Block.cpp b/src/CryptoNote/Block.cpp deleted file mode 100755 index 38f71359..00000000 --- a/src/CryptoNote/Block.cpp +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "Block.h" -#include "KeyInput.h" -#include "KeyOutput.h" -#include "MultisignatureInput.h" -#include "MultisignatureOutput.h" -#include "Transaction.h" - -namespace CryptoNote { - -Block::Block( - uint8_t majorVersion, - uint8_t minorVersion, - uint64_t timestamp, - const crypto::hash& previousBlockHash, - BaseTransaction&& baseTransaction, - std::vector&& transactions, - uint8_t parentMajorVersion, - uint8_t parentMinorVersion, - uint32_t nonce, - const crypto::hash& parentPreviousBlockHash, - BaseTransaction&& parentBaseTransaction, - std::vector&& parentBaseTransactionBranch, - uint32_t parentTransactionCount, - std::vector&& branch) : - majorVersion(majorVersion), - minorVersion(minorVersion), - timestamp(timestamp), - previousBlockHash(previousBlockHash), - baseTransaction(std::move(baseTransaction)), - transactions(std::move(transactions)), - parentMajorVersion(parentMajorVersion), - parentMinorVersion(parentMinorVersion), - nonce(nonce), - parentPreviousBlockHash(parentPreviousBlockHash), - parentBaseTransaction(std::move(parentBaseTransaction)), - parentBaseTransactionBranch(std::move(parentBaseTransactionBranch)), - parentTransactionCount(parentTransactionCount), - branch(std::move(branch)) { -} - -uint8_t Block::getMajorVersion() const { - return majorVersion; -} - -uint8_t Block::getMinorVersion() const { - return minorVersion; -} - -uint64_t Block::getTimestamp() const { - return timestamp; -} - -const crypto::hash& Block::getPreviousBlockHash() const { - return previousBlockHash; -} - -const BaseTransaction& Block::getBaseTransaction() const { - return baseTransaction; -} - -uint32_t Block::getTransactionCount() const { - return static_cast(transactions.size()); -} - -const Transaction& Block::getTransaction(uint32_t index) const { - return transactions[index]; -} - -uint8_t Block::getParentMajorVersion() const { - return parentMajorVersion; -} - -uint8_t Block::getParentMinorVersion() const { - return parentMinorVersion; -} - -uint32_t Block::getNonce() const { - return nonce; -} - -const crypto::hash& Block::getParentPreviousBlockHash() const { - return parentPreviousBlockHash; -} - -const BaseTransaction& Block::getParentBaseTransaction() const { - return parentBaseTransaction; -} - -const std::vector& Block::getParentBaseTransactionBranch() const { - return parentBaseTransactionBranch; -} - -uint32_t Block::getParentTransactionCount() const { - return parentTransactionCount; -} - -const std::vector& Block::getBranch() const { - return branch; -} - -} diff --git a/src/CryptoNote/Block.h b/src/CryptoNote/Block.h deleted file mode 100755 index 2aba9bda..00000000 --- a/src/CryptoNote/Block.h +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include "BaseTransaction.h" - -namespace CryptoNote { - -class Transaction; - -class Block { -public: - Block( - uint8_t majorVersion, - uint8_t minorVersion, - uint64_t timestamp, - const crypto::hash& previousBlockHash, - BaseTransaction&& baseTransaction, - std::vector&& transactions, - uint8_t parentMajorVersion, - uint8_t parentMinorVersion, - uint32_t nonce, - const crypto::hash& parentPreviousBlockHash, - BaseTransaction&& parentBaseTransaction, - std::vector&& parentBaseTransactionBranch, - uint32_t parentTransactionCount, - std::vector&& branch); - Block(const Block& other) = delete; - Block& operator=(const Block& other) = delete; - uint8_t getMajorVersion() const; - uint8_t getMinorVersion() const; - uint64_t getTimestamp() const; - const crypto::hash& getPreviousBlockHash() const; - const BaseTransaction& getBaseTransaction() const; - uint32_t getTransactionCount() const; - const Transaction& getTransaction(uint32_t index) const; - uint8_t getParentMajorVersion() const; - uint8_t getParentMinorVersion() const; - uint32_t getNonce() const; - const crypto::hash& getParentPreviousBlockHash() const; - const BaseTransaction& getParentBaseTransaction() const; - const std::vector& getParentBaseTransactionBranch() const; - uint32_t getParentTransactionCount() const; - const std::vector& getBranch() const; - -private: - uint8_t majorVersion; - uint8_t minorVersion; - uint64_t timestamp; - crypto::hash previousBlockHash; - BaseTransaction baseTransaction; - std::vector transactions; - uint8_t parentMajorVersion; - uint8_t parentMinorVersion; - uint32_t nonce; - crypto::hash parentPreviousBlockHash; - BaseTransaction parentBaseTransaction; - std::vector parentBaseTransactionBranch; - uint32_t parentTransactionCount; - std::vector branch; -}; - -} diff --git a/src/CryptoNote/KeyInput.cpp b/src/CryptoNote/KeyInput.cpp deleted file mode 100755 index ed5b7f0b..00000000 --- a/src/CryptoNote/KeyInput.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "KeyInput.h" - -namespace CryptoNote { - -KeyInput::KeyInput(uint64_t amount, std::vector&& outputs, const crypto::key_image& keyImage) : amount(amount), outputs(std::move(outputs)), keyImage(keyImage) { -} - -uint64_t KeyInput::getAmount() const { - return amount; -} - -uint32_t KeyInput::getOutputCount() const { - return static_cast(outputs.size()); -} - -uint32_t KeyInput::getOutputIndex(uint32_t index) const { - return outputs[index].index; -} - -const crypto::signature& KeyInput::getOutputSignature(uint32_t index) const { - return outputs[index].signature; -} - -const crypto::key_image& KeyInput::getKeyImage() const { - return keyImage; -} - -} diff --git a/src/CryptoNote/KeyInput.h b/src/CryptoNote/KeyInput.h deleted file mode 100755 index 511d491a..00000000 --- a/src/CryptoNote/KeyInput.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include "../crypto/crypto.h" - -namespace CryptoNote { - -class KeyInput { -public: - struct Output { - uint32_t index; - crypto::signature signature; - }; - - KeyInput(uint64_t amount, std::vector&& outputs, const crypto::key_image& keyImage); - KeyInput(const KeyInput& other) = delete; - KeyInput& operator=(const KeyInput& other) = delete; - uint64_t getAmount() const; - uint32_t getOutputCount() const; - uint32_t getOutputIndex(uint32_t index) const; - const crypto::signature& getOutputSignature(uint32_t index) const; - const crypto::key_image& getKeyImage() const; - -private: - uint64_t amount; - std::vector outputs; - crypto::key_image keyImage; -}; - -} diff --git a/src/CryptoNote/MultisignatureInput.cpp b/src/CryptoNote/MultisignatureInput.cpp deleted file mode 100755 index 36482e3a..00000000 --- a/src/CryptoNote/MultisignatureInput.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "MultisignatureInput.h" - -namespace CryptoNote { - -MultisignatureInput::MultisignatureInput(uint64_t amount, uint32_t outputIndex, std::vector&& signatures) : amount(amount), outputIndex(outputIndex), signatures(std::move(signatures)) { -} - -uint64_t MultisignatureInput::getAmount() const { - return amount; -} - -uint32_t MultisignatureInput::getOutputIndex() const { - return outputIndex; -} - -uint32_t MultisignatureInput::getSignatureCount() const { - return static_cast(signatures.size()); -} - -const crypto::signature& MultisignatureInput::getSignature(uint32_t index) const { - return signatures[index]; -} - -} diff --git a/src/CryptoNote/MultisignatureInput.h b/src/CryptoNote/MultisignatureInput.h deleted file mode 100755 index f6efca40..00000000 --- a/src/CryptoNote/MultisignatureInput.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include "../crypto/crypto.h" - -namespace CryptoNote { - -class MultisignatureInput { -public: - MultisignatureInput(uint64_t amount, uint32_t outputIndex, std::vector&& signatures); - MultisignatureInput(const MultisignatureInput& other) = delete; - MultisignatureInput& operator=(const MultisignatureInput& other) = delete; - uint64_t getAmount() const; - uint32_t getOutputIndex() const; - uint32_t getSignatureCount() const; - const crypto::signature& getSignature(uint32_t index) const; - -private: - uint64_t amount; - uint32_t outputIndex; - std::vector signatures; -}; - -} diff --git a/src/CryptoNote/Transaction.cpp b/src/CryptoNote/Transaction.cpp deleted file mode 100755 index 6daa4367..00000000 --- a/src/CryptoNote/Transaction.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "Transaction.h" -#include -#include - -namespace CryptoNote { - -Transaction::Transaction( - uint64_t unlockTime, - std::vector&& keyInputs, - std::vector&& multisignatureInputs, - std::vector&& keyOutputs, - std::vector&& multisignatureOutputs, - std::vector&& extra) : - unlockTime(unlockTime), - keyInputs(std::move(keyInputs)), - multisignatureInputs(std::move(multisignatureInputs)), - keyOutputs(std::move(keyOutputs)), - multisignatureOutputs(std::move(multisignatureOutputs)), - extra(std::move(extra)) { -} - -Transaction::Transaction( - Transaction&& other) : - unlockTime(other.unlockTime), - keyInputs(std::move(other.keyInputs)), - multisignatureInputs(std::move(other.multisignatureInputs)), - keyOutputs(std::move(other.keyOutputs)), - multisignatureOutputs(std::move(other.multisignatureOutputs)), - extra(std::move(other.extra)) { -} - -uint64_t Transaction::getUnlockTime() const { - return unlockTime; -} - -uint32_t Transaction::getInputCount() const { - return static_cast(keyInputs.size() + multisignatureInputs.size()); -} - -Transaction::InputType Transaction::getInputType(uint32_t index) const { - auto iterator = std::lower_bound(keyInputs.begin(), keyInputs.end(), index, [](const KeyInputEntry& keyInputEntry, uint32_t index)->bool { return keyInputEntry.index < index; }); - if (iterator != keyInputs.end() && iterator->index == index) { - return KEY_INPUT; - } - - return MULTISIGNATURE_INPUT; -} - -const KeyInput& Transaction::getKeyInput(uint32_t index) const { - auto iterator = std::lower_bound(keyInputs.begin(), keyInputs.end(), index, [](const KeyInputEntry& keyInputEntry, uint32_t index)->bool { return keyInputEntry.index < index; }); - assert(iterator != keyInputs.end()); - assert(iterator->index == index); - return iterator->input; -} - -const MultisignatureInput& Transaction::getMultisignatureInput(uint32_t index) const { - auto iterator = std::lower_bound(multisignatureInputs.begin(), multisignatureInputs.end(), index, [](const MultisignatureInputEntry& multisignatureInputEntry, uint32_t index)->bool { return multisignatureInputEntry.index < index; }); - assert(iterator != multisignatureInputs.end()); - assert(iterator->index == index); - return iterator->input; -} - -uint32_t Transaction::getOutputCount() const { - return static_cast(keyOutputs.size() + multisignatureOutputs.size()); -} - -Transaction::OutputType Transaction::getOutputType(uint32_t index) const { - auto iterator = std::lower_bound(keyOutputs.begin(), keyOutputs.end(), index, [](const KeyOutputEntry& keyOutputEntry, uint32_t index)->bool { return keyOutputEntry.index < index; }); - if (iterator != keyOutputs.end() && iterator->index == index) { - return KEY_OUTPUT; - } - - return MULTISIGNATURE_OUTPUT; -} - -const KeyOutput& Transaction::getKeyOutput(uint32_t index) const { - auto iterator = std::lower_bound(keyOutputs.begin(), keyOutputs.end(), index, [](const KeyOutputEntry& keyOutputEntry, uint32_t index)->bool { return keyOutputEntry.index < index; }); - assert(iterator != keyOutputs.end()); - assert(iterator->index == index); - return iterator->output; -} - -const MultisignatureOutput& Transaction::getMultisignatureOutput(uint32_t index) const { - auto iterator = std::lower_bound(multisignatureOutputs.begin(), multisignatureOutputs.end(), index, [](const MultisignatureOutputEntry& multisignatureOutputEntry, uint32_t index)->bool { return multisignatureOutputEntry.index < index; }); - assert(iterator != multisignatureOutputs.end()); - assert(iterator->index == index); - return iterator->output; -} - -const std::vector& Transaction::getExtra() const { - return extra; -} - -} diff --git a/src/CryptoNote/Transaction.h b/src/CryptoNote/Transaction.h deleted file mode 100755 index be346cea..00000000 --- a/src/CryptoNote/Transaction.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include "KeyInput.h" -#include "KeyOutput.h" -#include "MultisignatureInput.h" -#include "MultisignatureOutput.h" - -namespace CryptoNote { - -class Transaction { -public: - enum InputType { - KEY_INPUT = 0, - MULTISIGNATURE_INPUT = 1 - }; - - enum OutputType { - KEY_OUTPUT = 0, - MULTISIGNATURE_OUTPUT = 1 - }; - - struct KeyInputEntry { - uint32_t index; - KeyInput input; - }; - - struct KeyOutputEntry { - uint32_t index; - KeyOutput output; - }; - - struct MultisignatureInputEntry { - uint32_t index; - MultisignatureInput input; - }; - - struct MultisignatureOutputEntry { - uint32_t index; - MultisignatureOutput output; - }; - - Transaction( - uint64_t unlockTime, - std::vector&& keyInputs, - std::vector&& multisignatureInputs, - std::vector&& keyOutputs, - std::vector&& multisignatureOutputs, - std::vector&& extra); - Transaction(const Transaction& other) = delete; - Transaction(Transaction&& other); - Transaction& operator=(const Transaction& other) = delete; - uint64_t getUnlockTime() const; - uint32_t getInputCount() const; - InputType getInputType(uint32_t index) const; - const KeyInput& getKeyInput(uint32_t index) const; - const MultisignatureInput& getMultisignatureInput(uint32_t index) const; - uint32_t getOutputCount() const; - OutputType getOutputType(uint32_t index) const; - const KeyOutput& getKeyOutput(uint32_t index) const; - const MultisignatureOutput& getMultisignatureOutput(uint32_t index) const; - const std::vector& getExtra() const; - -private: - uint64_t unlockTime; - std::vector keyInputs; - std::vector multisignatureInputs; - std::vector keyOutputs; - std::vector multisignatureOutputs; - std::vector extra; -}; - -} diff --git a/src/CryptoNote/UnsignedMultisignatureInput.cpp b/src/CryptoNote/UnsignedMultisignatureInput.cpp deleted file mode 100755 index cfcd734f..00000000 --- a/src/CryptoNote/UnsignedMultisignatureInput.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "UnsignedMultisignatureInput.h" - -namespace CryptoNote { - -UnsignedMultisignatureInput::UnsignedMultisignatureInput(uint64_t amount, uint32_t outputIndex) : amount(amount), outputIndex(outputIndex) { -} - -uint64_t UnsignedMultisignatureInput::getAmount() const { - return amount; -} - -uint32_t UnsignedMultisignatureInput::getOutputIndex() const { - return outputIndex; -} - -} diff --git a/src/CryptoNote/UnsignedTransaction.cpp b/src/CryptoNote/UnsignedTransaction.cpp deleted file mode 100755 index 64cfe80c..00000000 --- a/src/CryptoNote/UnsignedTransaction.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "UnsignedTransaction.h" -#include -#include - -namespace CryptoNote { - -UnsignedTransaction::UnsignedTransaction( - uint64_t unlockTime, - std::vector&& keyInputs, - std::vector&& multisignatureInputs, - std::vector&& keyOutputs, - std::vector&& multisignatureOutputs, - std::vector&& extra) : - unlockTime(unlockTime), - keyInputs(std::move(keyInputs)), - multisignatureInputs(std::move(multisignatureInputs)), - keyOutputs(std::move(keyOutputs)), - multisignatureOutputs(std::move(multisignatureOutputs)), - extra(std::move(extra)) { -} - -UnsignedTransaction::UnsignedTransaction( - UnsignedTransaction&& other) : - unlockTime(other.unlockTime), - keyInputs(std::move(other.keyInputs)), - multisignatureInputs(std::move(other.multisignatureInputs)), - keyOutputs(std::move(other.keyOutputs)), - multisignatureOutputs(std::move(other.multisignatureOutputs)), - extra(std::move(other.extra)) { -} - -uint64_t UnsignedTransaction::getUnlockTime() const { - return unlockTime; -} - -uint32_t UnsignedTransaction::getInputCount() const { - return static_cast(keyInputs.size() + multisignatureInputs.size()); -} - -UnsignedTransaction::InputType UnsignedTransaction::getInputType(uint32_t index) const { - auto iterator = std::lower_bound(keyInputs.begin(), keyInputs.end(), index, [](const KeyInputEntry& keyInputEntry, uint32_t index)->bool { return keyInputEntry.index < index; }); - if (iterator != keyInputs.end() && iterator->index == index) { - return KEY_INPUT; - } - - return MULTISIGNATURE_INPUT; -} - -const UnsignedKeyInput& UnsignedTransaction::getKeyInput(uint32_t index) const { - auto iterator = std::lower_bound(keyInputs.begin(), keyInputs.end(), index, [](const KeyInputEntry& keyInputEntry, uint32_t index)->bool { return keyInputEntry.index < index; }); - assert(iterator != keyInputs.end()); - assert(iterator->index == index); - return iterator->input; -} - -const UnsignedMultisignatureInput& UnsignedTransaction::getMultisignatureInput(uint32_t index) const { - auto iterator = std::lower_bound(multisignatureInputs.begin(), multisignatureInputs.end(), index, [](const MultisignatureInputEntry& multisignatureInputEntry, uint32_t index)->bool { return multisignatureInputEntry.index < index; }); - assert(iterator != multisignatureInputs.end()); - assert(iterator->index == index); - return iterator->input; -} - -uint32_t UnsignedTransaction::getOutputCount() const { - return static_cast(keyOutputs.size() + multisignatureOutputs.size()); -} - -UnsignedTransaction::OutputType UnsignedTransaction::getOutputType(uint32_t index) const { - auto iterator = std::lower_bound(keyOutputs.begin(), keyOutputs.end(), index, [](const KeyOutputEntry& keyOutputEntry, uint32_t index)->bool { return keyOutputEntry.index < index; }); - if (iterator != keyOutputs.end() && iterator->index == index) { - return KEY_OUTPUT; - } - - return MULTISIGNATURE_OUTPUT; -} - -const KeyOutput& UnsignedTransaction::getKeyOutput(uint32_t index) const { - auto iterator = std::lower_bound(keyOutputs.begin(), keyOutputs.end(), index, [](const KeyOutputEntry& keyOutputEntry, uint32_t index)->bool { return keyOutputEntry.index < index; }); - assert(iterator != keyOutputs.end()); - assert(iterator->index == index); - return iterator->output; -} - -const MultisignatureOutput& UnsignedTransaction::getMultisignatureOutput(uint32_t index) const { - auto iterator = std::lower_bound(multisignatureOutputs.begin(), multisignatureOutputs.end(), index, [](const MultisignatureOutputEntry& multisignatureOutputEntry, uint32_t index)->bool { return multisignatureOutputEntry.index < index; }); - assert(iterator != multisignatureOutputs.end()); - assert(iterator->index == index); - return iterator->output; -} - -const std::vector& UnsignedTransaction::getExtra() const { - return extra; -} - -} diff --git a/src/CryptoNote/UnsignedTransaction.h b/src/CryptoNote/UnsignedTransaction.h deleted file mode 100755 index b8224617..00000000 --- a/src/CryptoNote/UnsignedTransaction.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include "KeyOutput.h" -#include "MultisignatureOutput.h" -#include "UnsignedKeyInput.h" -#include "UnsignedMultisignatureInput.h" - -namespace CryptoNote { - -class UnsignedTransaction { -public: - enum InputType { - KEY_INPUT = 0, - MULTISIGNATURE_INPUT = 1 - }; - - enum OutputType { - KEY_OUTPUT = 0, - MULTISIGNATURE_OUTPUT = 1 - }; - - struct KeyInputEntry { - uint32_t index; - UnsignedKeyInput input; - }; - - struct KeyOutputEntry { - uint32_t index; - KeyOutput output; - }; - - struct MultisignatureInputEntry { - uint32_t index; - UnsignedMultisignatureInput input; - }; - - struct MultisignatureOutputEntry { - uint32_t index; - MultisignatureOutput output; - }; - - UnsignedTransaction( - uint64_t unlockTime, - std::vector&& keyInputs, - std::vector&& multisignatureInputs, - std::vector&& keyOutputs, - std::vector&& multisignatureOutputs, - std::vector&& extra); - UnsignedTransaction(const UnsignedTransaction& other) = delete; - UnsignedTransaction(UnsignedTransaction&& other); - UnsignedTransaction& operator=(const UnsignedTransaction& other) = delete; - uint64_t getUnlockTime() const; - uint32_t getInputCount() const; - InputType getInputType(uint32_t index) const; - const UnsignedKeyInput& getKeyInput(uint32_t index) const; - const UnsignedMultisignatureInput& getMultisignatureInput(uint32_t index) const; - uint32_t getOutputCount() const; - OutputType getOutputType(uint32_t index) const; - const KeyOutput& getKeyOutput(uint32_t index) const; - const MultisignatureOutput& getMultisignatureOutput(uint32_t index) const; - const std::vector& getExtra() const; - -private: - uint64_t unlockTime; - std::vector keyInputs; - std::vector multisignatureInputs; - std::vector keyOutputs; - std::vector multisignatureOutputs; - std::vector extra; -}; - -} diff --git a/src/cryptonote_config.h b/src/CryptoNoteConfig.h similarity index 95% rename from src/cryptonote_config.h rename to src/CryptoNoteConfig.h index 5e5e1e13..17b3d14b 100644 --- a/src/cryptonote_config.h +++ b/src/CryptoNoteConfig.h @@ -74,6 +74,7 @@ const char CRYPTONOTE_BLOCKINDEXES_FILENAME[] = "blockindexes.dat const char CRYPTONOTE_BLOCKSCACHE_FILENAME[] = "blockscache.dat"; const char CRYPTONOTE_POOLDATA_FILENAME[] = "poolstate.bin"; const char P2P_NET_DATA_FILENAME[] = "p2pstate.bin"; +const char CRYPTONOTE_BLOCKCHAIN_INDICES_FILENAME[] = "blockchainindices.dat"; const char MINER_CONFIG_FILE_NAME[] = "miner_conf.json"; } // parameters @@ -95,7 +96,9 @@ const int RPC_DEFAULT_PORT = 8081; const size_t P2P_LOCAL_WHITE_PEERLIST_LIMIT = 1000; const size_t P2P_LOCAL_GRAY_PEERLIST_LIMIT = 5000; +const size_t P2P_CONNECTION_MAX_WRITE_BUFFER_SIZE = 16 * 1024 * 1024; // 16 MB const uint32_t P2P_DEFAULT_CONNECTIONS_COUNT = 8; +const size_t P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT = 70; const uint32_t P2P_DEFAULT_HANDSHAKE_INTERVAL = 60; // seconds const uint32_t P2P_DEFAULT_PACKET_MAX_SIZE = 50000000; // 50000000 bytes maximum packet size const uint32_t P2P_DEFAULT_PEERS_IN_HANDSHAKE = 250; @@ -103,10 +106,7 @@ const uint32_t P2P_DEFAULT_CONNECTION_TIMEOUT = 5000; // const uint32_t P2P_DEFAULT_PING_CONNECTION_TIMEOUT = 2000; // 2 seconds const uint64_t P2P_DEFAULT_INVOKE_TIMEOUT = 60 * 2 * 1000; // 2 minutes const size_t P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT = 5000; // 5 seconds -const char P2P_STAT_TRUSTED_PUB_KEY[] = "93467628927eaa0b13a4e52e61864a75aa475e67f6b5748eb3fc1d2fe468aed4"; -const size_t P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT = 70; - -const unsigned THREAD_STACK_SIZE = 5 * 1024 * 1024; +const char P2P_STAT_TRUSTED_PUB_KEY[] = "93467628927eaa0b13a4e52e61864a75aa475e67f6b5748eb3fc1d2fe468aed4"; const char* const SEED_NODES[] = { "seed.bytecoin.org:8080", @@ -122,7 +122,7 @@ const char* const SEED_NODES[] = { }; struct CheckpointData { - uint64_t height; + uint32_t height; const char* blockId; }; @@ -154,7 +154,8 @@ const CheckpointData CHECKPOINTS[] = { {750300, "5117631dbeb5c14748a91127a515ecbf13f6849e14fda7ee03cd55da41f1710c"}, {780000, "8dd55a9bae429e3685b90317281e633917023d3512eb7f37372209d1a5fc1070"}, {785500, "de1a487d70964d25ed6f7de196866f357a293e867ee81313e7fd0352d0126bdd"}, - {789000, "acef490bbccce3b7b7ae8554a414f55413fbf4ca1472c6359b126a4439bd9f01"} + {789000, "acef490bbccce3b7b7ae8554a414f55413fbf4ca1472c6359b126a4439bd9f01"}, + {796000, "04e387a00d35db21d4d93d04040b31f22573972a7e61d72cc07d0ab69bcb9c44"} }; } // CryptoNote diff --git a/src/cryptonote_core/account.cpp b/src/CryptoNoteCore/Account.cpp old mode 100644 new mode 100755 similarity index 70% rename from src/cryptonote_core/account.cpp rename to src/CryptoNoteCore/Account.cpp index e67042aa..9cf7e458 --- a/src/cryptonote_core/account.cpp +++ b/src/CryptoNoteCore/Account.cpp @@ -15,35 +15,35 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "account.h" -#include "cryptonote_serialization.h" +#include "Account.h" +#include "CryptoNoteSerialization.h" namespace CryptoNote { //----------------------------------------------------------------- -account_base::account_base() { - set_null(); +AccountBase::AccountBase() { + setNull(); } //----------------------------------------------------------------- -void account_base::set_null() { - m_keys = account_keys(); +void AccountBase::setNull() { + m_keys = AccountKeys(); } //----------------------------------------------------------------- -void account_base::generate() { - crypto::generate_keys(m_keys.m_account_address.m_spendPublicKey, m_keys.m_spend_secret_key); - crypto::generate_keys(m_keys.m_account_address.m_viewPublicKey, m_keys.m_view_secret_key); +void AccountBase::generate() { + Crypto::generate_keys(m_keys.address.spendPublicKey, m_keys.spendSecretKey); + Crypto::generate_keys(m_keys.address.viewPublicKey, m_keys.viewSecretKey); m_creation_timestamp = time(NULL); } //----------------------------------------------------------------- -const account_keys &account_base::get_keys() const { +const AccountKeys &AccountBase::getAccountKeys() const { return m_keys; } -void account_base::set_keys(const account_keys &keys) { +void AccountBase::setAccountKeys(const AccountKeys &keys) { m_keys = keys; } //----------------------------------------------------------------- -void account_base::serialize(ISerializer &s) { +void AccountBase::serialize(ISerializer &s) { s(m_keys, "m_keys"); s(m_creation_timestamp, "m_creation_timestamp"); } diff --git a/src/cryptonote_core/account.h b/src/CryptoNoteCore/Account.h old mode 100644 new mode 100755 similarity index 73% rename from src/cryptonote_core/account.h rename to src/CryptoNoteCore/Account.h index 35939576..2e879210 --- a/src/cryptonote_core/account.h +++ b/src/CryptoNoteCore/Account.h @@ -17,33 +17,26 @@ #pragma once -#include "cryptonote_core/cryptonote_basic.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" #include "crypto/crypto.h" namespace CryptoNote { - template struct AccountBaseSerializer; class ISerializer; - struct account_keys { - AccountPublicAddress m_account_address; - crypto::secret_key m_spend_secret_key; - crypto::secret_key m_view_secret_key; - }; - /************************************************************************/ /* */ /************************************************************************/ - class account_base { + class AccountBase { public: - account_base(); + AccountBase(); void generate(); - const account_keys& get_keys() const; - void set_keys(const account_keys& keys); - + const AccountKeys& getAccountKeys() const; + void setAccountKeys(const AccountKeys& keys); uint64_t get_createtime() const { return m_creation_timestamp; } void set_createtime(uint64_t val) { m_creation_timestamp = val; } + void serialize(ISerializer& s); template inline void serialize(t_archive &a, const unsigned int /*ver*/) { @@ -51,14 +44,9 @@ namespace CryptoNote { a & m_creation_timestamp; } - void serialize(ISerializer& s); - private: - void set_null(); - account_keys m_keys; + void setNull(); + AccountKeys m_keys; uint64_t m_creation_timestamp; - - friend struct AccountBaseSerializer; - friend struct AccountBaseSerializer; }; } diff --git a/src/CryptoNoteCore/BlockIndex.cpp b/src/CryptoNoteCore/BlockIndex.cpp new file mode 100755 index 00000000..bcd0f392 --- /dev/null +++ b/src/CryptoNoteCore/BlockIndex.cpp @@ -0,0 +1,88 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "BlockIndex.h" + +#include + +#include "CryptoNoteSerialization.h" +#include "Serialization/SerializationOverloads.h" + +namespace CryptoNote { + Crypto::Hash BlockIndex::getBlockId(uint32_t height) const { + assert(height < m_container.size()); + + return m_container[static_cast(height)]; + } + + std::vector BlockIndex::getBlockIds(uint32_t startBlockIndex, uint32_t maxCount) const { + std::vector result; + if (startBlockIndex >= m_container.size()) { + return result; + } + + size_t count = std::min(static_cast(maxCount), m_container.size() - static_cast(startBlockIndex)); + result.reserve(count); + for (size_t i = 0; i < count; ++i) { + result.push_back(m_container[startBlockIndex + i]); + } + + return result; + } + + bool BlockIndex::findSupplement(const std::vector& ids, uint32_t& offset) const { + for (const auto& id : ids) { + if (getBlockHeight(id, offset)) { + return true; + } + } + + return false; + } + + std::vector BlockIndex::buildSparseChain(const Crypto::Hash& startBlockId) const { + assert(m_index.count(startBlockId) > 0); + + uint32_t startBlockHeight; + getBlockHeight(startBlockId, startBlockHeight); + + std::vector result; + size_t sparseChainEnd = static_cast(startBlockHeight + 1); + for (size_t i = 1; i <= sparseChainEnd; i *= 2) { + result.emplace_back(m_container[sparseChainEnd - i]); + } + + if (result.back() != m_container[0]) { + result.emplace_back(m_container[0]); + } + + return result; + } + + Crypto::Hash BlockIndex::getTailId() const { + assert(!m_container.empty()); + return m_container.back(); + } + + void BlockIndex::serialize(ISerializer& s) { + if (s.type() == ISerializer::INPUT) { + readSequence(std::back_inserter(m_container), "index", s); + } else { + writeSequence(m_container.begin(), m_container.end(), "index", s); + } + } +} diff --git a/src/cryptonote_core/BlockIndex.h b/src/CryptoNoteCore/BlockIndex.h old mode 100644 new mode 100755 similarity index 69% rename from src/cryptonote_core/BlockIndex.h rename to src/CryptoNoteCore/BlockIndex.h index d447d190..8f2cc942 --- a/src/cryptonote_core/BlockIndex.h +++ b/src/CryptoNoteCore/BlockIndex.h @@ -20,10 +20,12 @@ #include #include "crypto/hash.h" -#include +#include namespace CryptoNote { + class ISerializer; + class BlockIndex { public: @@ -36,49 +38,47 @@ namespace CryptoNote } // returns true if new element was inserted, false if already exists - bool push(const crypto::hash& h) { + bool push(const Crypto::Hash& h) { auto result = m_container.push_back(h); return result.second; } - bool hasBlock(const crypto::hash& h) const { + bool hasBlock(const Crypto::Hash& h) const { return m_index.find(h) != m_index.end(); } - bool getBlockHeight(const crypto::hash& h, uint64_t& height) const { + bool getBlockHeight(const Crypto::Hash& h, uint32_t& height) const { auto hi = m_index.find(h); if (hi == m_index.end()) return false; - height = std::distance(m_container.begin(), m_container.project<0>(hi)); + height = static_cast(std::distance(m_container.begin(), m_container.project<0>(hi))); return true; } - size_t size() const { - return m_container.size(); + uint32_t size() const { + return static_cast(m_container.size()); } void clear() { m_container.clear(); } - crypto::hash getBlockId(uint64_t height) const; - bool getBlockIds(uint64_t startHeight, size_t maxCount, std::list& items) const; - bool findSupplement(const std::list& ids, uint64_t& offset) const; - bool getShortChainHistory(std::list& ids) const; - crypto::hash getTailId() const; + Crypto::Hash getBlockId(uint32_t height) const; + std::vector getBlockIds(uint32_t startBlockIndex, uint32_t maxCount) const; + bool findSupplement(const std::vector& ids, uint32_t& offset) const; + std::vector buildSparseChain(const Crypto::Hash& startBlockId) const; + Crypto::Hash getTailId() const; - template void serialize(Archive& ar, const unsigned int version) { - ar & m_container; - } + void serialize(ISerializer& s); private: typedef boost::multi_index_container < - crypto::hash, + Crypto::Hash, boost::multi_index::indexed_by< boost::multi_index::random_access<>, - boost::multi_index::hashed_unique> + boost::multi_index::hashed_unique> > > ContainerT; diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/CryptoNoteCore/Blockchain.cpp similarity index 59% rename from src/cryptonote_core/blockchain_storage.cpp rename to src/CryptoNoteCore/Blockchain.cpp index 7c49b1c6..f069131e 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/CryptoNoteCore/Blockchain.cpp @@ -15,28 +15,21 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "blockchain_storage.h" +#include "Blockchain.h" #include #include - -#include -#include #include -#include -#include - -#include "Common/boost_serialization_helper.h" #include "Common/Math.h" #include "Common/ShuffleGenerator.h" -#include "Common/StringTools.h" - -#include "cryptonote_format_utils.h" -#include "rpc/core_rpc_server_commands_defs.h" +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "Rpc/CoreRpcServerCommandsDefinitions.h" +#include "Serialization/BinarySerializationTools.h" +#include "CryptoNoteTools.h" using namespace Logging; - -#undef ERROR +using namespace Common; namespace { @@ -53,57 +46,137 @@ std::string appendPath(const std::string& path, const std::string& fileName) { } namespace std { -bool operator<(const crypto::hash& hash1, const crypto::hash& hash2) { - return memcmp(&hash1, &hash2, crypto::HASH_SIZE) < 0; +bool operator<(const Crypto::Hash& hash1, const Crypto::Hash& hash2) { + return memcmp(&hash1, &hash2, Crypto::HASH_SIZE) < 0; } -bool operator<(const crypto::key_image& keyImage1, const crypto::key_image& keyImage2) { +bool operator<(const Crypto::KeyImage& keyImage1, const Crypto::KeyImage& keyImage2) { return memcmp(&keyImage1, &keyImage2, 32) < 0; } } #define CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER 1 +#define CURRENT_BLOCKCHAININDICES_STORAGE_ARCHIVE_VER 1 namespace CryptoNote { class BlockCacheSerializer; +class BlockchainIndicesSerializer; } -BOOST_CLASS_VERSION(CryptoNote::BlockCacheSerializer, CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER); +namespace CryptoNote { -namespace CryptoNote -{ - -template -void blockchain_storage::TransactionIndex::serialize(Archive& archive, unsigned int version) { - archive & block; - archive & transaction; +template +bool serialize(google::sparse_hash_map& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializeMap(value, name, serializer, [&value](size_t size) { value.resize(size); }); } -template -void blockchain_storage::MultisignatureOutputUsage::serialize(Archive& archive, unsigned int version) { - archive & transactionIndex; - archive & outputIndex; - archive & isUsed; +template +bool serialize(google::sparse_hash_set& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + size_t size = value.size(); + if (!serializer.beginArray(size, name)) { + return false; + } + + if (serializer.type() == ISerializer::OUTPUT) { + for (auto& key : value) { + serializer(const_cast(key), ""); + } + } else { + value.resize(size); + while (size--) { + K key; + serializer(key, ""); + value.insert(key); + } + } + + serializer.endArray(); + return true; +} + +// custom serialization to speedup cache loading +bool serialize(std::vector>& value, Common::StringView name, CryptoNote::ISerializer& s) { + const size_t elementSize = sizeof(std::pair); + size_t size = value.size() * elementSize; + + if (!s.beginArray(size, name)) { + return false; + } + + if (s.type() == CryptoNote::ISerializer::INPUT) { + if (size % elementSize != 0) { + throw std::runtime_error("Invalid vector size"); + } + value.resize(size / elementSize); + } + + if (size) { + s.binary(value.data(), size, ""); + } + + s.endArray(); + return true; +} + +void serialize(Blockchain::TransactionIndex& value, ISerializer& s) { + s(value.block, "block"); + s(value.transaction, "tx"); } class BlockCacheSerializer { public: - BlockCacheSerializer(blockchain_storage& bs, const crypto::hash lastBlockHash, ILogger& logger) : + BlockCacheSerializer(Blockchain& bs, const Crypto::Hash lastBlockHash, ILogger& logger) : m_bs(bs), m_lastBlockHash(lastBlockHash), m_loaded(false), logger(logger, "BlockCacheSerializer") { } - template void serialize(Archive& ar, unsigned int version) { + void load(const std::string& filename) { + try { + std::ifstream stdStream(filename, std::ios::binary); + if (!stdStream) { + return; + } + + StdInputStream stream(stdStream); + BinaryInputStreamSerializer s(stream); + CryptoNote::serialize(*this, s); + } catch (std::exception& e) { + logger(WARNING) << "loading failed: " << e.what(); + } + } + + bool save(const std::string& filename) { + try { + std::ofstream file(filename, std::ios::binary); + if (!file) { + return false; + } + + StdOutputStream stream(file); + BinaryOutputStreamSerializer s(stream); + CryptoNote::serialize(*this, s); + } catch (std::exception&) { + return false; + } + + return true; + } + + void serialize(ISerializer& s) { + auto start = std::chrono::steady_clock::now(); + + uint8_t version = CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER; + s(version, "version"); // ignore old versions, do rebuild if (version < CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER) return; std::string operation; - if (Archive::is_loading::value) { + if (s.type() == ISerializer::INPUT) { operation = "- loading "; - crypto::hash blockHash; - ar & blockHash; + Crypto::Hash blockHash; + s(blockHash, "last_block"); if (blockHash != m_lastBlockHash) { return; @@ -111,23 +184,27 @@ public: } else { operation = "- saving "; - ar & m_lastBlockHash; + s(m_lastBlockHash, "last_block"); } logger(INFO) << operation << "block index..."; - ar & m_bs.m_blockIndex; + s(m_bs.m_blockIndex, "block_index"); logger(INFO) << operation << "transaction map..."; - ar & m_bs.m_transactionMap; + s(m_bs.m_transactionMap, "transactions"); - logger(INFO) << operation << "spend keys..."; - ar & m_bs.m_spent_keys; + logger(INFO) << operation << "spent keys..."; + s(m_bs.m_spent_keys, "spent_keys"); logger(INFO) << operation << "outputs..."; - ar & m_bs.m_outputs; + s(m_bs.m_outputs, "outputs"); logger(INFO) << operation << "multi-signature outputs..."; - ar & m_bs.m_multisignatureOutputs; + s(m_bs.m_multisignatureOutputs, "multisig_outputs"); + + auto dur = std::chrono::steady_clock::now() - start; + + logger(INFO) << "Serialization time: " << std::chrono::duration_cast(dur).count() << "ms"; m_loaded = true; } @@ -140,13 +217,104 @@ private: LoggerRef logger; bool m_loaded; - blockchain_storage& m_bs; - crypto::hash m_lastBlockHash; + Blockchain& m_bs; + Crypto::Hash m_lastBlockHash; +}; + +class BlockchainIndicesSerializer { + +public: + BlockchainIndicesSerializer(Blockchain& bs, const Crypto::Hash lastBlockHash, ILogger& logger) : + m_bs(bs), m_lastBlockHash(lastBlockHash), m_loaded(false), logger(logger, "BlockchainIndicesSerializer") { + } + + void serialize(ISerializer& s) { + + uint8_t version = CURRENT_BLOCKCHAININDICES_STORAGE_ARCHIVE_VER; + + KV_MEMBER(version); + + // ignore old versions, do rebuild + if (version != CURRENT_BLOCKCHAININDICES_STORAGE_ARCHIVE_VER) + return; + + std::string operation; + + if (s.type() == ISerializer::INPUT) { + operation = "- loading "; + + Crypto::Hash blockHash; + s(blockHash, "blockHash"); + + if (blockHash != m_lastBlockHash) { + return; + } + + } else { + operation = "- saving "; + s(m_lastBlockHash, "blockHash"); + } + + logger(INFO) << operation << "paymentID index..."; + s(m_bs.m_paymentIdIndex, "paymentIdIndex"); + + logger(INFO) << operation << "timestamp index..."; + s(m_bs.m_timestampIndex, "timestampIndex"); + + logger(INFO) << operation << "generated transactions index..."; + s(m_bs.m_generatedTransactionsIndex, "generatedTransactionsIndex"); + + m_loaded = true; + } + + template void serialize(Archive& ar, unsigned int version) { + + // ignore old versions, do rebuild + if (version < CURRENT_BLOCKCHAININDICES_STORAGE_ARCHIVE_VER) + return; + + std::string operation; + if (Archive::is_loading::value) { + operation = "- loading "; + Crypto::Hash blockHash; + ar & blockHash; + + if (blockHash != m_lastBlockHash) { + return; + } + + } else { + operation = "- saving "; + ar & m_lastBlockHash; + } + + logger(INFO) << operation << "paymentID index..."; + ar & m_bs.m_paymentIdIndex; + + logger(INFO) << operation << "timestamp index..."; + ar & m_bs.m_timestampIndex; + + logger(INFO) << operation << "generated transactions index..."; + ar & m_bs.m_generatedTransactionsIndex; + + m_loaded = true; + } + + bool loaded() const { + return m_loaded; + } + +private: + + LoggerRef logger; + bool m_loaded; + Blockchain& m_bs; + Crypto::Hash m_lastBlockHash; }; -blockchain_storage::blockchain_storage(const Currency& currency, tx_memory_pool& tx_pool, ILogger& logger) : -logger(logger, "blockchain_storage"), +Blockchain::Blockchain(const Currency& currency, tx_memory_pool& tx_pool, ILogger& logger) : +logger(logger, "Blockchain"), m_currency(currency), m_tx_pool(tx_pool), m_current_block_cumul_sz_limit(0), @@ -156,23 +324,23 @@ m_upgradeDetector(currency, m_blocks, BLOCK_MAJOR_VERSION_2, logger), m_checkpoints(logger) { m_outputs.set_deleted_key(0); - crypto::key_image nullImage = boost::value_initialized(); + Crypto::KeyImage nullImage = boost::value_initialized(); m_spent_keys.set_deleted_key(nullImage); } -bool blockchain_storage::addObserver(IBlockchainStorageObserver* observer) { +bool Blockchain::addObserver(IBlockchainStorageObserver* observer) { return m_observerManager.add(observer); } -bool blockchain_storage::removeObserver(IBlockchainStorageObserver* observer) { +bool Blockchain::removeObserver(IBlockchainStorageObserver* observer) { return m_observerManager.remove(observer); } -bool blockchain_storage::checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock) { - return check_tx_inputs(tx, maxUsedBlock.height, maxUsedBlock.id); +bool Blockchain::checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock) { + return checkTransactionInputs(tx, maxUsedBlock.height, maxUsedBlock.id); } -bool blockchain_storage::checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed) { +bool Blockchain::checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed) { BlockInfo tail; @@ -180,27 +348,27 @@ bool blockchain_storage::checkTransactionInputs(const CryptoNote::Transaction& t //check is ring_signature already checked ? if (maxUsedBlock.empty()) { //not checked, lets try to check - if (!lastFailed.empty() && get_current_blockchain_height() > lastFailed.height && get_block_id_by_height(lastFailed.height) == lastFailed.id) { + if (!lastFailed.empty() && getCurrentBlockchainHeight() > lastFailed.height && getBlockIdByHeight(lastFailed.height) == lastFailed.id) { return false; //we already sure that this tx is broken for this height } - if (!check_tx_inputs(tx, maxUsedBlock.height, maxUsedBlock.id, &tail)) { + if (!checkTransactionInputs(tx, maxUsedBlock.height, maxUsedBlock.id, &tail)) { lastFailed = tail; return false; } } else { - if (maxUsedBlock.height >= get_current_blockchain_height()) { + if (maxUsedBlock.height >= getCurrentBlockchainHeight()) { return false; } - if (get_block_id_by_height(maxUsedBlock.height) != maxUsedBlock.id) { + if (getBlockIdByHeight(maxUsedBlock.height) != maxUsedBlock.id) { //if we already failed on this height and id, skip actual ring signature check - if (lastFailed.id == get_block_id_by_height(lastFailed.height)) { + if (lastFailed.id == getBlockIdByHeight(lastFailed.height)) { return false; } //check ring signature again, it is possible (with very small chance) that this transaction become again valid - if (!check_tx_inputs(tx, maxUsedBlock.height, maxUsedBlock.id, &tail)) { + if (!checkTransactionInputs(tx, maxUsedBlock.height, maxUsedBlock.id, &tail)) { lastFailed = tail; return false; } @@ -210,28 +378,41 @@ bool blockchain_storage::checkTransactionInputs(const CryptoNote::Transaction& t return true; } -bool blockchain_storage::haveSpentKeyImages(const CryptoNote::Transaction& tx) { - return this->have_tx_keyimges_as_spent(tx); +bool Blockchain::haveSpentKeyImages(const CryptoNote::Transaction& tx) { + return this->haveTransactionKeyImagesAsSpent(tx); } -bool blockchain_storage::have_tx(const crypto::hash &id) { +/** +* \pre m_blockchain_lock is locked +*/ +bool Blockchain::checkTransactionSize(size_t blobSize) { + if (blobSize > getCurrentCumulativeBlocksizeLimit() - m_currency.minerTxBlobReservedSize()) { + logger(ERROR) << "transaction is too big " << blobSize << ", maximum allowed size is " << + (getCurrentCumulativeBlocksizeLimit() - m_currency.minerTxBlobReservedSize()); + return false; + } + + return true; +} + +bool Blockchain::haveTransaction(const Crypto::Hash &id) { std::lock_guard lk(m_blockchain_lock); return m_transactionMap.find(id) != m_transactionMap.end(); } -bool blockchain_storage::have_tx_keyimg_as_spent(const crypto::key_image &key_im) { +bool Blockchain::have_tx_keyimg_as_spent(const Crypto::KeyImage &key_im) { std::lock_guard lk(m_blockchain_lock); return m_spent_keys.find(key_im) != m_spent_keys.end(); } -uint64_t blockchain_storage::get_current_blockchain_height() { +uint32_t Blockchain::getCurrentBlockchainHeight() { std::lock_guard lk(m_blockchain_lock); - return m_blocks.size(); + return static_cast(m_blocks.size()); } -bool blockchain_storage::init(const std::string& config_folder, bool load_existing) { +bool Blockchain::init(const std::string& config_folder, bool load_existing) { std::lock_guard lk(m_blockchain_lock); - if (!config_folder.empty() && !tools::create_directories_if_necessary(config_folder)) { + if (!config_folder.empty() && !Tools::create_directories_if_necessary(config_folder)) { logger(ERROR, BRIGHT_RED) << "Failed to create data directory: " << m_config_folder; return false; } @@ -245,55 +426,14 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi if (load_existing && !m_blocks.empty()) { logger(INFO, BRIGHT_WHITE) << "Loading blockchain..."; BlockCacheSerializer loader(*this, get_block_hash(m_blocks.back().bl), logger.getLogger()); - tools::unserialize_obj_from_file(loader, appendPath(config_folder, m_currency.blocksCacheFileName())); + loader.load(appendPath(config_folder, m_currency.blocksCacheFileName())); if (!loader.loaded()) { logger(WARNING, BRIGHT_YELLOW) << "No actual blockchain cache found, rebuilding internal structures..."; - std::chrono::steady_clock::time_point timePoint = std::chrono::steady_clock::now(); - m_blockIndex.clear(); - m_transactionMap.clear(); - m_spent_keys.clear(); - m_outputs.clear(); - m_multisignatureOutputs.clear(); - for (uint32_t b = 0; b < m_blocks.size(); ++b) { - if (b % 1000 == 0) { - logger(INFO, BRIGHT_WHITE) << "Height " << b << " of " << m_blocks.size(); - } - const BlockEntry& block = m_blocks[b]; - crypto::hash blockHash = get_block_hash(block.bl); - m_blockIndex.push(blockHash); - for (uint16_t t = 0; t < block.transactions.size(); ++t) { - const TransactionEntry& transaction = block.transactions[t]; - crypto::hash transactionHash = get_transaction_hash(transaction.tx); - TransactionIndex transactionIndex = {b, t}; - m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex)); - - // process inputs - for (auto& i : transaction.tx.vin) { - if (i.type() == typeid(TransactionInputToKey)) { - m_spent_keys.insert(::boost::get(i).keyImage); - } else if (i.type() == typeid(TransactionInputMultisignature)) { - auto out = ::boost::get(i); - m_multisignatureOutputs[out.amount][out.outputIndex].isUsed = true; - } - } - - // process outputs - for (uint16_t o = 0; o < transaction.tx.vout.size(); ++o) { - const auto& out = transaction.tx.vout[o]; - if (out.target.type() == typeid(TransactionOutputToKey)) { - m_outputs[out.amount].push_back(std::make_pair<>(transactionIndex, o)); - } else if (out.target.type() == typeid(TransactionOutputMultisignature)) { - MultisignatureOutputUsage usage = {transactionIndex, o, false}; - m_multisignatureOutputs[out.amount].push_back(usage); - } - } - } - } - - std::chrono::duration duration = std::chrono::steady_clock::now() - timePoint; - logger(INFO, BRIGHT_WHITE) << "Rebuilding internal structures took: " << duration.count(); + rebuildCache(); } + + loadBlockchainIndices(); } else { m_blocks.clear(); } @@ -301,15 +441,14 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi if (m_blocks.empty()) { logger(INFO, BRIGHT_WHITE) << "Blockchain not loaded, generating genesis block."; - block_verification_context bvc = - boost::value_initialized(); - add_new_block(m_currency.genesisBlock(), bvc); + block_verification_context bvc = boost::value_initialized(); + pushBlock(m_currency.genesisBlock(), bvc); if (bvc.m_verifivation_failed) { logger(ERROR, BRIGHT_RED) << "Failed to add genesis block to blockchain"; return false; } } else { - crypto::hash firstBlockHash = get_block_hash(m_blocks[0].bl); + Crypto::Hash firstBlockHash = get_block_hash(m_blocks[0].bl); if (!(firstBlockHash == m_currency.genesisBlockHash())) { logger(ERROR, BRIGHT_RED) << "Failed to init: genesis block mismatch. " "Probably you set --testnet flag with data " @@ -334,16 +473,63 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi logger(INFO, BRIGHT_GREEN) << "Blockchain initialized. last block: " << m_blocks.size() - 1 << ", " << Common::timeIntervalToString(timestamp_diff) - << " time ago, current difficulty: " << get_difficulty_for_next_block(); + << " time ago, current difficulty: " << getDifficultyForNextBlock(); return true; } -bool blockchain_storage::storeCache() { +void Blockchain::rebuildCache() { + std::chrono::steady_clock::time_point timePoint = std::chrono::steady_clock::now(); + m_blockIndex.clear(); + m_transactionMap.clear(); + m_spent_keys.clear(); + m_outputs.clear(); + m_multisignatureOutputs.clear(); + for (uint32_t b = 0; b < m_blocks.size(); ++b) { + if (b % 1000 == 0) { + logger(INFO, BRIGHT_WHITE) << "Height " << b << " of " << m_blocks.size(); + } + const BlockEntry& block = m_blocks[b]; + Crypto::Hash blockHash = get_block_hash(block.bl); + m_blockIndex.push(blockHash); + for (uint16_t t = 0; t < block.transactions.size(); ++t) { + const TransactionEntry& transaction = block.transactions[t]; + Crypto::Hash transactionHash = getObjectHash(transaction.tx); + TransactionIndex transactionIndex = { b, t }; + m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex)); + + // process inputs + for (auto& i : transaction.tx.inputs) { + if (i.type() == typeid(KeyInput)) { + m_spent_keys.insert(::boost::get(i).keyImage); + } else if (i.type() == typeid(MultisignatureInput)) { + auto out = ::boost::get(i); + m_multisignatureOutputs[out.amount][out.outputIndex].isUsed = true; + } + } + + // process outputs + for (uint16_t o = 0; o < transaction.tx.outputs.size(); ++o) { + const auto& out = transaction.tx.outputs[o]; + if (out.target.type() == typeid(KeyOutput)) { + m_outputs[out.amount].push_back(std::make_pair<>(transactionIndex, o)); + } else if (out.target.type() == typeid(MultisignatureOutput)) { + MultisignatureOutputUsage usage = { transactionIndex, o, false }; + m_multisignatureOutputs[out.amount].push_back(usage); + } + } + } + } + + std::chrono::duration duration = std::chrono::steady_clock::now() - timePoint; + logger(INFO, BRIGHT_WHITE) << "Rebuilding internal structures took: " << duration.count(); +} + +bool Blockchain::storeCache() { std::lock_guard lk(m_blockchain_lock); logger(INFO, BRIGHT_WHITE) << "Saving blockchain..."; - BlockCacheSerializer ser(*this, get_tail_id(), logger.getLogger()); - if (!tools::serialize_obj_to_file(ser, appendPath(m_config_folder, m_currency.blocksCacheFileName()))) { + BlockCacheSerializer ser(*this, getTailId(), logger.getLogger()); + if (!ser.save(appendPath(m_config_folder, m_currency.blocksCacheFileName()))) { logger(ERROR, BRIGHT_RED) << "Failed to save blockchain cache"; return false; } @@ -351,12 +537,14 @@ bool blockchain_storage::storeCache() { return true; } -bool blockchain_storage::deinit() { +bool Blockchain::deinit() { storeCache(); + storeBlockchainIndices(); + assert(m_messageQueueList.empty()); return true; } -bool blockchain_storage::reset_and_set_genesis_block(const Block& b) { +bool Blockchain::resetAndSetGenesisBlock(const Block& b) { std::lock_guard lk(m_blockchain_lock); m_blocks.clear(); m_blockIndex.clear(); @@ -366,69 +554,89 @@ bool blockchain_storage::reset_and_set_genesis_block(const Block& b) { m_alternative_chains.clear(); m_outputs.clear(); + m_paymentIdIndex.clear(); + m_timestampIndex.clear(); + m_generatedTransactionsIndex.clear(); + m_orthanBlocksIndex.clear(); + block_verification_context bvc = boost::value_initialized(); - add_new_block(b, bvc); + addNewBlock(b, bvc); return bvc.m_added_to_main_chain && !bvc.m_verifivation_failed; } -crypto::hash blockchain_storage::get_tail_id(uint64_t& height) { +Crypto::Hash Blockchain::getTailId(uint32_t& height) { + assert(!m_blocks.empty()); std::lock_guard lk(m_blockchain_lock); - height = get_current_blockchain_height() - 1; - return get_tail_id(); + height = getCurrentBlockchainHeight() - 1; + return getTailId(); } -crypto::hash blockchain_storage::get_tail_id() { +Crypto::Hash Blockchain::getTailId() { std::lock_guard lk(m_blockchain_lock); - return m_blockIndex.getTailId(); + return m_blocks.empty() ? NULL_HASH : m_blockIndex.getTailId(); } -bool blockchain_storage::getPoolChanges(const crypto::hash& tailBlockId, const std::vector& knownTxsIds, - std::vector& addedTxs, std::vector& deletedTxsIds) { - // Locks are necessary in order to make sure blockchain isn't changed between get_tail_id() and getPoolChanges() - std::lock_guard txPoolLock(m_tx_pool); - std::lock_guard blockchainLock(m_blockchain_lock); +std::vector Blockchain::buildSparseChain() { + std::lock_guard lk(m_blockchain_lock); + assert(m_blockIndex.size() != 0); + return doBuildSparseChain(m_blockIndex.getTailId()); +} - if (tailBlockId != get_tail_id()) { - return false; +std::vector Blockchain::buildSparseChain(const Crypto::Hash& startBlockId) { + std::lock_guard lk(m_blockchain_lock); + assert(haveBlock(startBlockId)); + return doBuildSparseChain(startBlockId); +} + +std::vector Blockchain::doBuildSparseChain(const Crypto::Hash& startBlockId) const { + assert(m_blockIndex.size() != 0); + + std::vector sparseChain; + + if (m_blockIndex.hasBlock(startBlockId)) { + sparseChain = m_blockIndex.buildSparseChain(startBlockId); + } else { + assert(m_alternative_chains.count(startBlockId) > 0); + + std::vector alternativeChain; + Crypto::Hash blockchainAncestor; + for (auto it = m_alternative_chains.find(startBlockId); it != m_alternative_chains.end(); it = m_alternative_chains.find(blockchainAncestor)) { + alternativeChain.emplace_back(it->first); + blockchainAncestor = it->second.bl.previousBlockHash; + } + + for (size_t i = 1; i <= alternativeChain.size(); i *= 2) { + sparseChain.emplace_back(alternativeChain[i - 1]); + } + + assert(!sparseChain.empty()); + assert(m_blockIndex.hasBlock(blockchainAncestor)); + std::vector sparseMainChain = m_blockIndex.buildSparseChain(blockchainAncestor); + sparseChain.reserve(sparseChain.size() + sparseMainChain.size()); + std::copy(sparseMainChain.begin(), sparseMainChain.end(), std::back_inserter(sparseChain)); } - getPoolChanges(knownTxsIds, addedTxs, deletedTxsIds); - - return true; + return sparseChain; } -void blockchain_storage::getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, - std::vector& deletedTxsIds) { - std::lock_guard txPoolLock(m_tx_pool); - - std::vector addedTxsIds; - m_tx_pool.get_difference(knownTxsIds, addedTxsIds, deletedTxsIds); - - std::vector misses; - m_tx_pool.getTransactions(addedTxsIds, addedTxs, misses); - assert(misses.empty()); -} - -bool blockchain_storage::get_short_chain_history(std::list& ids) { - std::lock_guard lk(m_blockchain_lock); - return m_blockIndex.getShortChainHistory(ids); -} - -crypto::hash blockchain_storage::get_block_id_by_height(uint64_t height) { +Crypto::Hash Blockchain::getBlockIdByHeight(uint32_t height) { std::lock_guard lk(m_blockchain_lock); + assert(height < m_blockIndex.size()); return m_blockIndex.getBlockId(height); } -bool blockchain_storage::get_block_by_hash(const crypto::hash& blockHash, Block& b) { +bool Blockchain::getBlockByHash(const Crypto::Hash& blockHash, Block& b) { std::lock_guard lk(m_blockchain_lock); - uint64_t height = 0; + uint32_t height = 0; if (m_blockIndex.getBlockHeight(blockHash, height)) { b = m_blocks[height].bl; return true; } + logger(WARNING) << blockHash; + auto blockByHashIterator = m_alternative_chains.find(blockHash); if (blockByHashIterator != m_alternative_chains.end()) { b = blockByHashIterator->second.bl; @@ -438,7 +646,12 @@ bool blockchain_storage::get_block_by_hash(const crypto::hash& blockHash, Block& return false; } -difficulty_type blockchain_storage::get_difficulty_for_next_block() { +bool Blockchain::getBlockHeight(const Crypto::Hash& blockId, uint32_t& blockHeight) { + std::lock_guard lock(m_blockchain_lock); + return m_blockIndex.getBlockHeight(blockId, blockHeight); +} + +difficulty_type Blockchain::getDifficultyForNextBlock() { std::lock_guard lk(m_blockchain_lock); std::vector timestamps; std::vector commulative_difficulties; @@ -455,7 +668,7 @@ difficulty_type blockchain_storage::get_difficulty_for_next_block() { return m_currency.nextDifficulty(timestamps, commulative_difficulties); } -uint64_t blockchain_storage::getCoinsInCirculation() { +uint64_t Blockchain::getCoinsInCirculation() { std::lock_guard lk(m_blockchain_lock); if (m_blocks.empty()) { return 0; @@ -464,11 +677,11 @@ uint64_t blockchain_storage::getCoinsInCirculation() { } } -uint8_t blockchain_storage::get_block_major_version_for_height(uint64_t height) const { +uint8_t Blockchain::getBlockMajorVersionForHeight(uint32_t height) const { return height > m_upgradeDetector.upgradeHeight() ? m_upgradeDetector.targetVersion() : BLOCK_MAJOR_VERSION_1; } -bool blockchain_storage::rollback_blockchain_switching(std::list &original_chain, size_t rollback_height) { +bool Blockchain::rollback_blockchain_switching(std::list &original_chain, size_t rollback_height) { std::lock_guard lk(m_blockchain_lock); // remove failed subchain for (size_t i = m_blocks.size() - 1; i >= rollback_height; i--) { @@ -476,7 +689,7 @@ bool blockchain_storage::rollback_blockchain_switching(std::list &origina } // return back original chain - for(auto &bl : original_chain) { + for (auto &bl : original_chain) { block_verification_context bvc = boost::value_initialized(); bool r = pushBlock(bl, bvc); @@ -491,7 +704,7 @@ bool blockchain_storage::rollback_blockchain_switching(std::list &origina return true; } -bool blockchain_storage::switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain) { +bool Blockchain::switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain) { std::lock_guard lk(m_blockchain_lock); if (!(alt_chain.size())) { @@ -525,11 +738,13 @@ bool blockchain_storage::switch_to_alternative_blockchain(std::listsecond, get_block_hash(ch_ent->second.bl)); logger(INFO, BRIGHT_WHITE) << "The block was inserted as invalid while connecting new alternative chain, block_id: " << get_block_hash(ch_ent->second.bl); + m_orthanBlocksIndex.remove(ch_ent->second.bl); m_alternative_chains.erase(ch_ent); for (auto alt_ch_to_orph_iter = ++alt_ch_iter; alt_ch_to_orph_iter != alt_chain.end(); alt_ch_to_orph_iter++) { //block_verification_context bvc = boost::value_initialized(); //add_block_as_invalid((*alt_ch_iter)->second, (*alt_ch_iter)->first); + m_orthanBlocksIndex.remove((*alt_ch_to_orph_iter)->second.bl); m_alternative_chains.erase(*alt_ch_to_orph_iter); } @@ -541,7 +756,7 @@ bool blockchain_storage::switch_to_alternative_blockchain(std::list(); - bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc); + bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc, false); if (!r) { logger(ERROR, BRIGHT_RED) << ("Failed to push ex-main chain blocks to alternative chain "); rollback_blockchain_switching(disconnected_chain, split_height); @@ -550,16 +765,24 @@ bool blockchain_storage::switch_to_alternative_blockchain(std::list blocksFromCommonRoot; + blocksFromCommonRoot.reserve(alt_chain.size() + 1); + blocksFromCommonRoot.push_back(alt_chain.front()->second.bl.previousBlockHash); + //removing all_chain entries from alternative chain for (auto ch_ent : alt_chain) { + blocksFromCommonRoot.push_back(get_block_hash(ch_ent->second.bl)); + m_orthanBlocksIndex.remove(ch_ent->second.bl); m_alternative_chains.erase(ch_ent); } + sendMessage(BlockchainMessage(std::move(ChainSwitchMessage(std::move(blocksFromCommonRoot))))); + logger(INFO, BRIGHT_GREEN) << "REORGANIZE SUCCESS! on height: " << split_height << ", new blockchain size: " << m_blocks.size(); return true; } -difficulty_type blockchain_storage::get_next_difficulty_for_alternative_chain(const std::list& alt_chain, BlockEntry& bei) { +difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std::list& alt_chain, BlockEntry& bei) { std::vector timestamps; std::vector commulative_difficulties; if (alt_chain.size() < m_currency.difficultyBlocksCount()) { @@ -602,35 +825,35 @@ difficulty_type blockchain_storage::get_next_difficulty_for_alternative_chain(co return m_currency.nextDifficulty(timestamps, commulative_difficulties); } -bool blockchain_storage::prevalidate_miner_transaction(const Block& b, uint64_t height) { +bool Blockchain::prevalidate_miner_transaction(const Block& b, uint32_t height) { - if (!(b.minerTx.vin.size() == 1)) { + if (!(b.baseTransaction.inputs.size() == 1)) { logger(ERROR, BRIGHT_RED) << "coinbase transaction in the block has no inputs"; return false; } - if (!(b.minerTx.vin[0].type() == typeid(TransactionInputGenerate))) { + if (!(b.baseTransaction.inputs[0].type() == typeid(BaseInput))) { logger(ERROR, BRIGHT_RED) << "coinbase transaction in the block has the wrong type"; return false; } - if (boost::get(b.minerTx.vin[0]).height != height) { + if (boost::get(b.baseTransaction.inputs[0]).blockIndex != height) { logger(INFO, BRIGHT_RED) << "The miner transaction in block has invalid height: " << - boost::get(b.minerTx.vin[0]).height << ", expected: " << height; + boost::get(b.baseTransaction.inputs[0]).blockIndex << ", expected: " << height; return false; } - if (!(b.minerTx.unlockTime == height + m_currency.minedMoneyUnlockWindow())) { + if (!(b.baseTransaction.unlockTime == height + m_currency.minedMoneyUnlockWindow())) { logger(ERROR, BRIGHT_RED) << "coinbase transaction transaction have wrong unlock time=" - << b.minerTx.unlockTime << ", expected " + << b.baseTransaction.unlockTime << ", expected " << height + m_currency.minedMoneyUnlockWindow(); return false; } - if (!check_outs_overflow(b.minerTx)) { + if (!check_outs_overflow(b.baseTransaction)) { logger(INFO, BRIGHT_RED) << "miner transaction have money overflow in block " << get_block_hash(b); return false; } @@ -638,11 +861,11 @@ bool blockchain_storage::prevalidate_miner_transaction(const Block& b, uint64_t return true; } -bool blockchain_storage::validate_miner_transaction(const Block& b, uint64_t height, size_t cumulativeBlockSize, +bool Blockchain::validate_miner_transaction(const Block& b, uint32_t height, size_t cumulativeBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, uint64_t& reward, int64_t& emissionChange) { uint64_t minerReward = 0; - for (auto& o : b.minerTx.vout) { + for (auto& o : b.baseTransaction.outputs) { minerReward += o.amount; } @@ -650,7 +873,7 @@ bool blockchain_storage::validate_miner_transaction(const Block& b, uint64_t hei get_last_n_blocks_sizes(lastBlocksSizes, m_currency.rewardBlocksWindow()); size_t blocksSizeMedian = Common::medianValue(lastBlocksSizes); - bool penalizeFee = get_block_major_version_for_height(height) > BLOCK_MAJOR_VERSION_1; + bool penalizeFee = getBlockMajorVersionForHeight(height) > BLOCK_MAJOR_VERSION_1; if (!m_currency.getBlockReward(blocksSizeMedian, cumulativeBlockSize, alreadyGeneratedCoins, fee, penalizeFee, reward, emissionChange)) { logger(INFO, BRIGHT_WHITE) << "block size " << cumulativeBlockSize << " is bigger than allowed for this blockchain"; return false; @@ -669,7 +892,7 @@ bool blockchain_storage::validate_miner_transaction(const Block& b, uint64_t hei return true; } -bool blockchain_storage::get_backward_blocks_sizes(size_t from_height, std::vector& sz, size_t count) { +bool Blockchain::getBackwardBlocksSize(size_t from_height, std::vector& sz, size_t count) { std::lock_guard lk(m_blockchain_lock); if (!(from_height < m_blocks.size())) { logger(ERROR, BRIGHT_RED) @@ -685,170 +908,20 @@ bool blockchain_storage::get_backward_blocks_sizes(size_t from_height, std::vect return true; } -bool blockchain_storage::get_last_n_blocks_sizes(std::vector& sz, size_t count) { +bool Blockchain::get_last_n_blocks_sizes(std::vector& sz, size_t count) { std::lock_guard lk(m_blockchain_lock); if (!m_blocks.size()) { return true; } - return get_backward_blocks_sizes(m_blocks.size() - 1, sz, count); + return getBackwardBlocksSize(m_blocks.size() - 1, sz, count); } -uint64_t blockchain_storage::get_current_comulative_blocksize_limit() { +uint64_t Blockchain::getCurrentCumulativeBlocksizeLimit() { return m_current_block_cumul_sz_limit; } -bool blockchain_storage::create_block_template(Block& b, const AccountPublicAddress& miner_address, difficulty_type& diffic, uint32_t& height, const blobdata& ex_nonce) { - size_t median_size; - uint64_t already_generated_coins; - - { - std::lock_guard lk(m_blockchain_lock); - height = static_cast(m_blocks.size()); - diffic = get_difficulty_for_next_block(); - if (!(diffic)) { - logger(ERROR, BRIGHT_RED) << "difficulty overhead."; - return false; - } - - b = boost::value_initialized(); - b.majorVersion = get_block_major_version_for_height(height); - - if (BLOCK_MAJOR_VERSION_1 == b.majorVersion) { - b.minorVersion = BLOCK_MINOR_VERSION_1; - } else if (BLOCK_MAJOR_VERSION_2 == b.majorVersion) { - b.minorVersion = BLOCK_MINOR_VERSION_0; - - b.parentBlock.majorVersion = BLOCK_MAJOR_VERSION_1; - b.parentBlock.majorVersion = BLOCK_MINOR_VERSION_0; - b.parentBlock.numberOfTransactions = 1; - tx_extra_merge_mining_tag mm_tag = boost::value_initialized(); - - if (!append_mm_tag_to_extra(b.parentBlock.minerTx.extra, mm_tag)) { - logger(ERROR, BRIGHT_RED) << "Failed to append merge mining tag to extra of the parent block miner transaction"; - return false; - } - } - - b.prevId = get_tail_id(); - b.timestamp = time(NULL); - - median_size = m_current_block_cumul_sz_limit / 2; - already_generated_coins = m_blocks.back().already_generated_coins; - } - - size_t txs_size; - uint64_t fee; - if (!m_tx_pool.fill_block_template(b, median_size, m_currency.maxBlockCumulativeSize(height), already_generated_coins, - txs_size, fee)) { - return false; - } - -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - size_t real_txs_size = 0; - uint64_t real_fee = 0; - CRITICAL_REGION_BEGIN(m_tx_pool.m_transactions_lock); - for (crypto::hash &cur_hash : b.txHashes) { - auto cur_res = m_tx_pool.m_transactions.find(cur_hash); - if (cur_res == m_tx_pool.m_transactions.end()) { - logger(ERROR, BRIGHT_RED) << "Creating block template: error: transaction not found"; - continue; - } - tx_memory_pool::tx_details &cur_tx = cur_res->second; - real_txs_size += cur_tx.blob_size; - real_fee += cur_tx.fee; - if (cur_tx.blob_size != get_object_blobsize(cur_tx.tx)) { - logger(ERROR, BRIGHT_RED) << "Creating block template: error: invalid transaction size"; - } - uint64_t inputs_amount; - if (!get_inputs_money_amount(cur_tx.tx, inputs_amount)) { - logger(ERROR, BRIGHT_RED) << "Creating block template: error: cannot get inputs amount"; - } else if (cur_tx.fee != inputs_amount - get_outs_money_amount(cur_tx.tx)) { - logger(ERROR, BRIGHT_RED) << "Creating block template: error: invalid fee"; - } - } - if (txs_size != real_txs_size) { - logger(ERROR, BRIGHT_RED) << "Creating block template: error: wrongly calculated transaction size"; - } - if (fee != real_fee) { - logger(ERROR, BRIGHT_RED) << "Creating block template: error: wrongly calculated fee"; - } - CRITICAL_REGION_END(); - logger(DEBUGGING) << "Creating block template: height " << height << - ", median size " << median_size << - ", already generated coins " << already_generated_coins << - ", transaction size " << txs_size << - ", fee " << fee; -#endif - - /* - two-phase miner transaction generation: we don't know exact block size until we prepare block, but we don't know reward until we know - block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size - */ - //make blocks coin-base tx looks close to real coinbase tx to get truthful blob size - bool penalizeFee = b.majorVersion > BLOCK_MAJOR_VERSION_1; - bool r = m_currency.constructMinerTx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.minerTx, ex_nonce, 11, penalizeFee); - if (!r) { - logger(ERROR, BRIGHT_RED) << "Failed to construc miner tx, first chance"; - return false; - } - - size_t cumulative_size = txs_size + get_object_blobsize(b.minerTx); -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - logger(DEBUGGING) << "Creating block template: miner tx size " << get_object_blobsize(b.minerTx) << - ", cumulative size " << cumulative_size; -#endif - for (size_t try_count = 0; try_count != 10; ++try_count) { - r = m_currency.constructMinerTx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.minerTx, ex_nonce, 11, penalizeFee); - - if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to construc miner tx, second chance"; return false; } - size_t coinbase_blob_size = get_object_blobsize(b.minerTx); - if (coinbase_blob_size > cumulative_size - txs_size) { - cumulative_size = txs_size + coinbase_blob_size; -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - logger(DEBUGGING) << "Creating block template: miner tx size " << coinbase_blob_size << - ", cumulative size " << cumulative_size << " is greater then before"; -#endif - continue; - } - - if (coinbase_blob_size < cumulative_size - txs_size) { - size_t delta = cumulative_size - txs_size - coinbase_blob_size; -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - logger(DEBUGGING) << "Creating block template: miner tx size " << coinbase_blob_size << - ", cumulative size " << txs_size + coinbase_blob_size << - " is less then before, adding " << delta << " zero bytes"; -#endif - b.minerTx.extra.insert(b.minerTx.extra.end(), delta, 0); - //here could be 1 byte difference, because of extra field counter is varint, and it can become from 1-byte len to 2-bytes len. - if (cumulative_size != txs_size + get_object_blobsize(b.minerTx)) { - if (!(cumulative_size + 1 == txs_size + get_object_blobsize(b.minerTx))) { logger(ERROR, BRIGHT_RED) << "unexpected case: cumulative_size=" << cumulative_size << " + 1 is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.minerTx)=" << get_object_blobsize(b.minerTx); return false; } - b.minerTx.extra.resize(b.minerTx.extra.size() - 1); - if (cumulative_size != txs_size + get_object_blobsize(b.minerTx)) { - //fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size - logger(TRACE, BRIGHT_RED) << - "Miner tx creation have no luck with delta_extra size = " << delta << " and " << delta - 1; - cumulative_size += delta - 1; - continue; - } - logger(DEBUGGING, BRIGHT_GREEN) << - "Setting extra for block: " << b.minerTx.extra.size() << ", try_count=" << try_count; - } - } - if (!(cumulative_size == txs_size + get_object_blobsize(b.minerTx))) { logger(ERROR, BRIGHT_RED) << "unexpected case: cumulative_size=" << cumulative_size << " is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.minerTx)=" << get_object_blobsize(b.minerTx); return false; } -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - logger(DEBUGGING) << "Creating block template: miner tx size " << coinbase_blob_size << - ", cumulative size " << cumulative_size << " is now good"; -#endif - return true; - } - - logger(ERROR, BRIGHT_RED) << - "Failed to create_block_template with " << 10 << " tries"; - return false; -} - -bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, std::vector& timestamps) { +bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vector& timestamps) { if (timestamps.size() >= m_currency.timestampCheckWindow()) return true; @@ -865,10 +938,10 @@ bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, s return true; } -bool blockchain_storage::handle_alternative_block(const Block& b, const crypto::hash& id, block_verification_context& bvc) { +bool Blockchain::handle_alternative_block(const Block& b, const Crypto::Hash& id, block_verification_context& bvc, bool sendNewAlternativeBlockMessage) { std::lock_guard lk(m_blockchain_lock); - uint64_t block_height = get_block_height(b); + auto block_height = get_block_height(b); if (block_height == 0) { logger(ERROR, BRIGHT_RED) << "Block with id: " << Common::podToHex(id) << " (as alternative) have wrong miner transaction"; @@ -876,10 +949,10 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: return false; } - if (!m_checkpoints.is_alternative_block_allowed(get_current_blockchain_height(), block_height)) { + if (!m_checkpoints.is_alternative_block_allowed(getCurrentBlockchainHeight(), block_height)) { logger(TRACE) << "Block with id: " << id << std::endl << " can't be accepted for alternative chain, block height: " << block_height << std::endl << - " blockchain height: " << get_current_blockchain_height(); + " blockchain height: " << getCurrentBlockchainHeight(); bvc.m_verifivation_failed = true; return false; } @@ -906,9 +979,9 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: //block is not related with head of main chain //first of all - look in alternative chains container - uint64_t mainPrevHeight = 0; - const bool mainPrev = m_blockIndex.getBlockHeight(b.prevId, mainPrevHeight); - const auto it_prev = m_alternative_chains.find(b.prevId); + uint32_t mainPrevHeight = 0; + const bool mainPrev = m_blockIndex.getBlockHeight(b.previousBlockHash, mainPrevHeight); + const auto it_prev = m_alternative_chains.find(b.previousBlockHash); if (it_prev != m_alternative_chains.end() || mainPrev) { //we have new block in alternative chain @@ -920,15 +993,15 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: while (alt_it != m_alternative_chains.end()) { alt_chain.push_front(alt_it); timestamps.push_back(alt_it->second.bl.timestamp); - alt_it = m_alternative_chains.find(alt_it->second.bl.prevId); + alt_it = m_alternative_chains.find(alt_it->second.bl.previousBlockHash); } if (alt_chain.size()) { //make sure that it has right connection to main chain if (!(m_blocks.size() > alt_chain.front()->second.height)) { logger(ERROR, BRIGHT_RED) << "main blockchain wrong height"; return false; } - crypto::hash h = null_hash; + Crypto::Hash h = NULL_HASH; get_block_hash(m_blocks[alt_chain.front()->second.height - 1].bl, h); - if (!(h == alt_chain.front()->second.bl.prevId)) { logger(ERROR, BRIGHT_RED) << "alternative chain have wrong connection to main chain"; return false; } + if (!(h == alt_chain.front()->second.bl.previousBlockHash)) { logger(ERROR, BRIGHT_RED) << "alternative chain have wrong connection to main chain"; return false; } complete_timestamps_vector(alt_chain.front()->second.height - 1, timestamps); } else { if (!(mainPrev)) { logger(ERROR, BRIGHT_RED) << "internal error: broken imperative condition it_main_prev != m_blocks_index.end()"; return false; } @@ -961,7 +1034,7 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: m_is_in_checkpoint_zone = false; difficulty_type current_diff = get_next_difficulty_for_alternative_chain(alt_chain, bei); if (!(current_diff)) { logger(ERROR, BRIGHT_RED) << "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!"; return false; } - crypto::hash proof_of_work = null_hash; + Crypto::Hash proof_of_work = NULL_HASH; if (!m_currency.checkProofOfWork(m_cn_context, bei.bl, current_diff, proof_of_work)) { logger(INFO, BRIGHT_RED) << "Block with id: " << id @@ -988,6 +1061,9 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: auto i_res = m_alternative_chains.insert(blocks_ext_by_hash::value_type(id, bei)); if (!(i_res.second)) { logger(ERROR, BRIGHT_RED) << "insertion of new alternative block returned as it already exist"; return false; } + + m_orthanBlocksIndex.add(bei.bl); + alt_chain.push_back(i_res.first); if (is_a_checkpoint) { @@ -1023,9 +1099,12 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: << ENDL << "id:\t" << id << ENDL << "PoW:\t" << proof_of_work << ENDL << "difficulty:\t" << current_diff; + if (sendNewAlternativeBlockMessage) { + sendMessage(BlockchainMessage(std::move(NewAlternativeBlockMessage(id)))); + } return true; } - } else { + } else { //block orphaned bvc.m_marked_as_orphaned = true; logger(INFO, BRIGHT_RED) << @@ -1033,68 +1112,68 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: } return true; - } +} -bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) { +bool Blockchain::getBlocks(uint32_t start_offset, uint32_t count, std::list& blocks, std::list& txs) { std::lock_guard lk(m_blockchain_lock); if (start_offset >= m_blocks.size()) return false; for (size_t i = start_offset; i < start_offset + count && i < m_blocks.size(); i++) { blocks.push_back(m_blocks[i].bl); - std::list missed_ids; - get_transactions(m_blocks[i].bl.txHashes, txs, missed_ids); + std::list missed_ids; + getTransactions(m_blocks[i].bl.transactionHashes, txs, missed_ids); if (!(!missed_ids.size())) { logger(ERROR, BRIGHT_RED) << "have missed transactions in own block in main blockchain"; return false; } } return true; } -bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks) { +bool Blockchain::getBlocks(uint32_t start_offset, uint32_t count, std::list& blocks) { std::lock_guard lk(m_blockchain_lock); if (start_offset >= m_blocks.size()) { return false; } - for (size_t i = start_offset; i < start_offset + count && i < m_blocks.size(); i++) { + for (uint32_t i = start_offset; i < start_offset + count && i < m_blocks.size(); i++) { blocks.push_back(m_blocks[i].bl); } return true; } -bool blockchain_storage::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) { +bool Blockchain::handleGetObjects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) { //Deprecated. Should be removed with CryptoNoteProtocolHandler. std::lock_guard lk(m_blockchain_lock); - rsp.current_blockchain_height = get_current_blockchain_height(); + rsp.current_blockchain_height = getCurrentBlockchainHeight(); std::list blocks; - get_blocks(arg.blocks, blocks, rsp.missed_ids); + getBlocks(arg.blocks, blocks, rsp.missed_ids); for (const auto& bl : blocks) { - std::list missed_tx_id; + std::list missed_tx_id; std::list txs; - get_transactions(bl.txHashes, txs, rsp.missed_ids); - if (!(!missed_tx_id.size())) { logger(ERROR, BRIGHT_RED) << "Internal error: have missed missed_tx_id.size()=" << missed_tx_id.size() << ENDL << "for block id = " << get_block_hash(bl); return false; } + getTransactions(bl.transactionHashes, txs, rsp.missed_ids); + if (!(!missed_tx_id.size())) { logger(ERROR, BRIGHT_RED) << "Internal error: have missed missed_tx_id.size()=" << missed_tx_id.size() << ENDL << "for block id = " << get_block_hash(bl); return false; } //WTF??? rsp.blocks.push_back(block_complete_entry()); block_complete_entry& e = rsp.blocks.back(); //pack block - e.block = t_serializable_object_to_blob(bl); + e.block = asString(toBinaryArray(bl)); //pack transactions for (Transaction& tx : txs) { - e.txs.push_back(t_serializable_object_to_blob(tx)); + e.txs.push_back(asString(toBinaryArray(tx))); } } //get another transactions, if need std::list txs; - get_transactions(arg.txs, txs, rsp.missed_ids); + getTransactions(arg.txs, txs, rsp.missed_ids); //pack aside transactions for (const auto& tx : txs) { - rsp.txs.push_back(t_serializable_object_to_blob(tx)); + rsp.txs.push_back(asString(toBinaryArray(tx))); } return true; } -bool blockchain_storage::get_alternative_blocks(std::list& blocks) { +bool Blockchain::getAlternativeBlocks(std::list& blocks) { std::lock_guard lk(m_blockchain_lock); for (auto& alt_bl : m_alternative_chains) { blocks.push_back(alt_bl.second.bl); @@ -1103,31 +1182,31 @@ bool blockchain_storage::get_alternative_blocks(std::list& blocks) { return true; } -size_t blockchain_storage::get_alternative_blocks_count() { +uint32_t Blockchain::getAlternativeBlocksCount() { std::lock_guard lk(m_blockchain_lock); - return m_alternative_chains.size(); + return static_cast(m_alternative_chains.size()); } -bool blockchain_storage::add_out_to_get_random_outs(std::vector>& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) { +bool Blockchain::add_out_to_get_random_outs(std::vector>& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) { std::lock_guard lk(m_blockchain_lock); const Transaction& tx = transactionByIndex(amount_outs[i].first).tx; - if (!(tx.vout.size() > amount_outs[i].second)) { + if (!(tx.outputs.size() > amount_outs[i].second)) { logger(ERROR, BRIGHT_RED) << "internal error: in global outs index, transaction out index=" - << amount_outs[i].second << " more than transaction outputs = " << tx.vout.size() << ", for tx id = " << get_transaction_hash(tx); return false; + << amount_outs[i].second << " more than transaction outputs = " << tx.outputs.size() << ", for tx id = " << getObjectHash(tx); return false; } - if (!(tx.vout[amount_outs[i].second].target.type() == typeid(TransactionOutputToKey))) { logger(ERROR, BRIGHT_RED) << "unknown tx out type"; return false; } + if (!(tx.outputs[amount_outs[i].second].target.type() == typeid(KeyOutput))) { logger(ERROR, BRIGHT_RED) << "unknown tx out type"; return false; } //check if transaction is unlocked if (!is_tx_spendtime_unlocked(tx.unlockTime)) return false; COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oen = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry()); - oen.global_amount_index = i; - oen.out_key = boost::get(tx.vout[amount_outs[i].second].target).key; + oen.global_amount_index = static_cast(i); + oen.out_key = boost::get(tx.outputs[amount_outs[i].second].target).key; return true; } -size_t blockchain_storage::find_end_of_allowed_index(const std::vector>& amount_outs) { +size_t Blockchain::find_end_of_allowed_index(const std::vector>& amount_outs) { std::lock_guard lk(m_blockchain_lock); if (amount_outs.empty()) { return 0; @@ -1136,7 +1215,7 @@ size_t blockchain_storage::find_end_of_allowed_index(const std::vector lk(m_blockchain_lock); for (uint64_t amount : req.amounts) { @@ -1164,7 +1243,7 @@ bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDO if (!(up_index_limit <= amount_outs.size())) { logger(ERROR, BRIGHT_RED) << "internal error: find_end_of_allowed_index returned wrong index=" << up_index_limit << ", with amount_outs.size = " << amount_outs.size(); return false; } if (up_index_limit > 0) { - ShuffleGenerator> generator(up_index_limit); + ShuffleGenerator> generator(up_index_limit); for (uint64_t j = 0; j < up_index_limit && result_outs.outs.size() < req.outs_count; ++j) { add_out_to_get_random_outs(amount_outs, result_outs, amount, generator()); } @@ -1173,44 +1252,27 @@ bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDO return true; } -bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, uint64_t& starter_offset) { +uint32_t Blockchain::findBlockchainSupplement(const std::vector& qblock_ids) { + assert(!qblock_ids.empty()); + assert(qblock_ids.back() == m_blockIndex.getBlockId(0)); + std::lock_guard lk(m_blockchain_lock); - - if (!qblock_ids.size() /*|| !req.m_total_height*/) { - logger(ERROR, BRIGHT_RED) << - "Client sent wrong NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << qblock_ids.size() << /*", m_height=" << req.m_total_height <<*/ ", dropping connection"; - return false; - } - //check genesis match - if (qblock_ids.back() != get_block_hash(m_blocks[0].bl)) { - logger(ERROR, BRIGHT_RED) << - "Client sent wrong NOTIFY_REQUEST_CHAIN: genesis block missmatch: " << ENDL << "id: " - << qblock_ids.back() << ", " << ENDL << "expected: " << get_block_hash(m_blocks[0].bl) - << "," << ENDL << " dropping connection"; - return false; - } - - /* Figure out what blocks we should request to get state_normal */ - if (m_blockIndex.findSupplement(qblock_ids, starter_offset)) { - return true; - } - - //this should NEVER happen, but, dose of paranoia in such cases is not too bad - logger(ERROR, BRIGHT_RED) << - "Internal error handling connection, can't find split point"; - return false; + uint32_t blockIndex; + // assert above guarantees that method returns true + m_blockIndex.findSupplement(qblock_ids, blockIndex); + return blockIndex; } -uint64_t blockchain_storage::block_difficulty(size_t i) { +uint64_t Blockchain::blockDifficulty(size_t i) { std::lock_guard lk(m_blockchain_lock); - if (!(i < m_blocks.size())) { logger(ERROR, BRIGHT_RED) << "wrong block index i = " << i << " at blockchain_storage::block_difficulty()"; return false; } + if (!(i < m_blocks.size())) { logger(ERROR, BRIGHT_RED) << "wrong block index i = " << i << " at Blockchain::block_difficulty()"; return false; } if (i == 0) return m_blocks[i].cumulative_difficulty; return m_blocks[i].cumulative_difficulty - m_blocks[i - 1].cumulative_difficulty; } -void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_index) { +void Blockchain::print_blockchain(uint64_t start_index, uint64_t end_index) { std::stringstream ss; std::lock_guard lk(m_blockchain_lock); if (start_index >= m_blocks.size()) { @@ -1222,7 +1284,7 @@ void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_ind for (size_t i = start_index; i != m_blocks.size() && i != end_index; i++) { ss << "height " << i << ", timestamp " << m_blocks[i].bl.timestamp << ", cumul_dif " << m_blocks[i].cumulative_difficulty << ", cumul_size " << m_blocks[i].block_cumulative_size << "\nid\t\t" << get_block_hash(m_blocks[i].bl) - << "\ndifficulty\t\t" << block_difficulty(i) << ", nonce " << m_blocks[i].bl.nonce << ", tx_count " << m_blocks[i].bl.txHashes.size() << ENDL; + << "\ndifficulty\t\t" << blockDifficulty(i) << ", nonce " << m_blocks[i].bl.nonce << ", tx_count " << m_blocks[i].bl.transactionHashes.size() << ENDL; } logger(DEBUGGING) << "Current blockchain:" << ENDL << ss.str(); @@ -1230,25 +1292,21 @@ void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_ind "Blockchain printed with log level 1"; } -void blockchain_storage::print_blockchain_index() { +void Blockchain::print_blockchain_index() { std::stringstream ss; std::lock_guard lk(m_blockchain_lock); - std::list blockIds; - m_blockIndex.getBlockIds(0, std::numeric_limits::max(), blockIds); - - logger(INFO, BRIGHT_WHITE) << - "Current blockchain index:" << ENDL; + std::vector blockIds = m_blockIndex.getBlockIds(0, std::numeric_limits::max()); + logger(INFO, BRIGHT_WHITE) << "Current blockchain index:"; size_t height = 0; for (auto i = blockIds.begin(); i != blockIds.end(); ++i, ++height) { - logger(INFO, BRIGHT_WHITE) << - "id\t\t" << *i << " height" << height; + logger(INFO, BRIGHT_WHITE) << "id\t\t" << *i << " height" << height; } } -void blockchain_storage::print_blockchain_outs(const std::string& file) { +void Blockchain::print_blockchain_outs(const std::string& file) { std::stringstream ss; std::lock_guard lk(m_blockchain_lock); for (const outputs_container::value_type& v : m_outputs) { @@ -1256,7 +1314,7 @@ void blockchain_storage::print_blockchain_outs(const std::string& file) { if (!vals.empty()) { ss << "amount: " << v.first << ENDL; for (size_t i = 0; i != vals.size(); i++) { - ss << "\t" << get_transaction_hash(transactionByIndex(vals[i].first).tx) << ": " << vals[i].second << ENDL; + ss << "\t" << getObjectHash(transactionByIndex(vals[i].first).tx) << ": " << vals[i].second << ENDL; } } } @@ -1270,37 +1328,20 @@ void blockchain_storage::print_blockchain_outs(const std::string& file) { } } -bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) { +std::vector Blockchain::findBlockchainSupplement(const std::vector& remoteBlockIds, size_t maxCount, + uint32_t& totalBlockCount, uint32_t& startBlockIndex) { + + assert(!remoteBlockIds.empty()); + assert(remoteBlockIds.back() == m_blockIndex.getBlockId(0)); + std::lock_guard lk(m_blockchain_lock); - if (!find_blockchain_supplement(qblock_ids, resp.start_height)) - return false; + totalBlockCount = getCurrentBlockchainHeight(); + startBlockIndex = findBlockchainSupplement(remoteBlockIds); - resp.total_height = get_current_blockchain_height(); - size_t count = 0; - - return m_blockIndex.getBlockIds(resp.start_height, BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT, resp.m_block_ids); + return m_blockIndex.getBlockIds(startBlockIndex, static_cast(maxCount)); } -bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) { - std::lock_guard lk(m_blockchain_lock); - if (!find_blockchain_supplement(qblock_ids, start_height)) { - return false; - } - - total_height = get_current_blockchain_height(); - size_t count = 0; - for (size_t i = start_height; i != m_blocks.size() && count < max_count; i++, count++) { - blocks.resize(blocks.size() + 1); - blocks.back().first = m_blocks[i].bl; - std::list mis; - get_transactions(m_blocks[i].bl.txHashes, blocks.back().second, mis); - if (!(!mis.size())) { logger(ERROR, BRIGHT_RED) << "internal error, transaction from block not found"; return false; } - } - - return true; -} - -bool blockchain_storage::have_block(const crypto::hash& id) { +bool Blockchain::haveBlock(const Crypto::Hash& id) { std::lock_guard lk(m_blockchain_lock); if (m_blockIndex.hasBlock(id)) return true; @@ -1311,12 +1352,12 @@ bool blockchain_storage::have_block(const crypto::hash& id) { return false; } -size_t blockchain_storage::get_total_transactions() { +size_t Blockchain::getTotalTransactions() { std::lock_guard lk(m_blockchain_lock); return m_transactionMap.size(); } -bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) { +bool Blockchain::getTransactionOutputGlobalIndexes(const Crypto::Hash& tx_id, std::vector& indexs) { std::lock_guard lk(m_blockchain_lock); auto it = m_transactionMap.find(tx_id); if (it == m_transactionMap.end()) { @@ -1334,23 +1375,46 @@ bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std:: return true; } -bool blockchain_storage::check_tx_inputs(const Transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id, BlockInfo* tail) { +bool Blockchain::get_out_by_msig_gindex(uint64_t amount, uint64_t gindex, MultisignatureOutput& out) { + std::lock_guard lk(m_blockchain_lock); + auto it = m_multisignatureOutputs.find(amount); + if (it == m_multisignatureOutputs.end()) { + return false; + } + + if (it->second.size() <= gindex) { + return false; + } + + auto msigUsage = it->second[gindex]; + auto& targetOut = transactionByIndex(msigUsage.transactionIndex).tx.outputs[msigUsage.outputIndex].target; + if (targetOut.type() != typeid(MultisignatureOutput)) { + return false; + } + + out = boost::get(targetOut); + return true; +} + + + +bool Blockchain::checkTransactionInputs(const Transaction& tx, uint32_t& max_used_block_height, Crypto::Hash& max_used_block_id, BlockInfo* tail) { std::lock_guard lk(m_blockchain_lock); if (tail) - tail->id = get_tail_id(tail->height); + tail->id = getTailId(tail->height); - bool res = check_tx_inputs(tx, &max_used_block_height); + bool res = checkTransactionInputs(tx, &max_used_block_height); if (!res) return false; if (!(max_used_block_height < m_blocks.size())) { logger(ERROR, BRIGHT_RED) << "internal error: max used block index=" << max_used_block_height << " is not less then blockchain size = " << m_blocks.size(); return false; } get_block_hash(m_blocks[max_used_block_height].bl, max_used_block_id); return true; } -bool blockchain_storage::have_tx_keyimges_as_spent(const Transaction &tx) { - for (const auto& in : tx.vin) { - if (in.type() == typeid(TransactionInputToKey)) { - if (have_tx_keyimg_as_spent(boost::get(in).keyImage)) { +bool Blockchain::haveTransactionKeyImagesAsSpent(const Transaction &tx) { + for (const auto& in : tx.inputs) { + if (in.type() == typeid(KeyInput)) { + if (have_tx_keyimg_as_spent(boost::get(in).keyImage)) { return true; } } @@ -1359,23 +1423,23 @@ bool blockchain_storage::have_tx_keyimges_as_spent(const Transaction &tx) { return false; } -bool blockchain_storage::check_tx_inputs(const Transaction& tx, uint64_t* pmax_used_block_height) { - crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx); - return check_tx_inputs(tx, tx_prefix_hash, pmax_used_block_height); +bool Blockchain::checkTransactionInputs(const Transaction& tx, uint32_t* pmax_used_block_height) { + Crypto::Hash tx_prefix_hash = getObjectHash(*static_cast(&tx)); + return checkTransactionInputs(tx, tx_prefix_hash, pmax_used_block_height); } -bool blockchain_storage::check_tx_inputs(const Transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height) { +bool Blockchain::checkTransactionInputs(const Transaction& tx, const Crypto::Hash& tx_prefix_hash, uint32_t* pmax_used_block_height) { size_t inputIndex = 0; if (pmax_used_block_height) { *pmax_used_block_height = 0; } - crypto::hash transactionHash = get_transaction_hash(tx); - for (const auto& txin : tx.vin) { + Crypto::Hash transactionHash = getObjectHash(tx); + for (const auto& txin : tx.inputs) { assert(inputIndex < tx.signatures.size()); - if (txin.type() == typeid(TransactionInputToKey)) { - const TransactionInputToKey& in_to_key = boost::get(txin); - if (!(!in_to_key.keyOffsets.empty())) { logger(ERROR, BRIGHT_RED) << "empty in_to_key.keyOffsets in transaction with id " << get_transaction_hash(tx); return false; } + if (txin.type() == typeid(KeyInput)) { + const KeyInput& in_to_key = boost::get(txin); + if (!(!in_to_key.outputIndexes.empty())) { logger(ERROR, BRIGHT_RED) << "empty in_to_key.outputIndexes in transaction with id " << getObjectHash(tx); return false; } if (have_tx_keyimg_as_spent(in_to_key.keyImage)) { logger(DEBUGGING) << @@ -1390,8 +1454,8 @@ bool blockchain_storage::check_tx_inputs(const Transaction& tx, const crypto::ha } ++inputIndex; - } else if (txin.type() == typeid(TransactionInputMultisignature)) { - if (!validateInput(::boost::get(txin), transactionHash, tx_prefix_hash, tx.signatures[inputIndex])) { + } else if (txin.type() == typeid(MultisignatureInput)) { + if (!validateInput(::boost::get(txin), transactionHash, tx_prefix_hash, tx.signatures[inputIndex])) { return false; } @@ -1406,10 +1470,10 @@ bool blockchain_storage::check_tx_inputs(const Transaction& tx, const crypto::ha return true; } -bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time) { +bool Blockchain::is_tx_spendtime_unlocked(uint64_t unlock_time) { if (unlock_time < m_currency.maxBlockHeight()) { //interpret as block index - if (get_current_blockchain_height() - 1 + m_currency.lockedTxAllowedDeltaBlocks() >= unlock_time) + if (getCurrentBlockchainHeight() - 1 + m_currency.lockedTxAllowedDeltaBlocks() >= unlock_time) return true; else return false; @@ -1425,14 +1489,14 @@ bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time) { return false; } -bool blockchain_storage::check_tx_input(const TransactionInputToKey& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t* pmax_related_block_height) { +bool Blockchain::check_tx_input(const KeyInput& txin, const Crypto::Hash& tx_prefix_hash, const std::vector& sig, uint32_t* pmax_related_block_height) { std::lock_guard lk(m_blockchain_lock); struct outputs_visitor { - std::vector& m_results_collector; - blockchain_storage& m_bch; + std::vector& m_results_collector; + Blockchain& m_bch; LoggerRef logger; - outputs_visitor(std::vector& results_collector, blockchain_storage& bch, ILogger& logger) :m_results_collector(results_collector), m_bch(bch), logger(logger, "outputs_visitor") { + outputs_visitor(std::vector& results_collector, Blockchain& bch, ILogger& logger) :m_results_collector(results_collector), m_bch(bch), logger(logger, "outputs_visitor") { } bool handle_output(const Transaction& tx, const TransactionOutput& out, size_t transactionOutputIndex) { @@ -1443,30 +1507,30 @@ bool blockchain_storage::check_tx_input(const TransactionInputToKey& txin, const return false; } - if (out.target.type() != typeid(TransactionOutputToKey)) { + if (out.target.type() != typeid(KeyOutput)) { logger(INFO, BRIGHT_WHITE) << "Output have wrong type id, which=" << out.target.which(); return false; } - m_results_collector.push_back(&boost::get(out.target).key); + m_results_collector.push_back(&boost::get(out.target).key); return true; } }; //check ring signature - std::vector output_keys; + std::vector output_keys; outputs_visitor vi(output_keys, *this, logger.getLogger()); - if (!scan_outputkeys_for_indexes(txin, vi, pmax_related_block_height)) { + if (!scanOutputKeysForIndexes(txin, vi, pmax_related_block_height)) { logger(INFO, BRIGHT_WHITE) << "Failed to get output keys for tx with amount = " << m_currency.formatAmount(txin.amount) << - " and count indexes " << txin.keyOffsets.size(); + " and count indexes " << txin.outputIndexes.size(); return false; } - if (txin.keyOffsets.size() != output_keys.size()) { + if (txin.outputIndexes.size() != output_keys.size()) { logger(INFO, BRIGHT_WHITE) << - "Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.keyOffsets.size() << " returned wrong keys count " << output_keys.size(); + "Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.outputIndexes.size() << " returned wrong keys count " << output_keys.size(); return false; } @@ -1475,15 +1539,15 @@ bool blockchain_storage::check_tx_input(const TransactionInputToKey& txin, const return true; } - return crypto::check_ring_signature(tx_prefix_hash, txin.keyImage, output_keys, sig.data()); + return Crypto::check_ring_signature(tx_prefix_hash, txin.keyImage, output_keys, sig.data()); } -uint64_t blockchain_storage::get_adjusted_time() { +uint64_t Blockchain::get_adjusted_time() { //TODO: add collecting median time return time(NULL); } -bool blockchain_storage::check_block_timestamp_main(const Block& b) { +bool Blockchain::check_block_timestamp_main(const Block& b) { if (b.timestamp > get_adjusted_time() + m_currency.blockFutureTimeLimit()) { logger(INFO, BRIGHT_WHITE) << "Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 2 hours"; @@ -1499,7 +1563,7 @@ bool blockchain_storage::check_block_timestamp_main(const Block& b) { return check_block_timestamp(std::move(timestamps), b); } -bool blockchain_storage::check_block_timestamp(std::vector timestamps, const Block& b) { +bool Blockchain::check_block_timestamp(std::vector timestamps, const Block& b) { if (timestamps.size() < m_currency.timestampCheckWindow()) { return true; } @@ -1516,9 +1580,9 @@ bool blockchain_storage::check_block_timestamp(std::vector timestamps, return true; } -bool blockchain_storage::checkBlockVersion(const Block& b, const crypto::hash& blockHash) { - uint64_t height = get_block_height(b); - const uint8_t expectedBlockVersion = get_block_major_version_for_height(height); +bool Blockchain::checkBlockVersion(const Block& b, const Crypto::Hash& blockHash) { + uint32_t height = get_block_height(b); + const uint8_t expectedBlockVersion = getBlockMajorVersionForHeight(height); if (b.majorVersion != expectedBlockVersion) { logger(TRACE) << "Block " << blockHash << " has wrong major version: " << static_cast(b.majorVersion) << ", at height " << height << " expected version is " << static_cast(expectedBlockVersion); @@ -1528,11 +1592,11 @@ bool blockchain_storage::checkBlockVersion(const Block& b, const crypto::hash& b return true; } -bool blockchain_storage::checkParentBlockSize(const Block& b, const crypto::hash& blockHash) { +bool Blockchain::checkParentBlockSize(const Block& b, const Crypto::Hash& blockHash) { if (BLOCK_MAJOR_VERSION_2 == b.majorVersion) { auto serializer = makeParentBlockSerializer(b, false, false); size_t parentBlockSize; - if (!get_object_blobsize(serializer, parentBlockSize)) { + if (!getObjectBinarySize(serializer, parentBlockSize)) { logger(ERROR, BRIGHT_RED) << "Block " << blockHash << ": failed to determine parent block size"; return false; @@ -1549,7 +1613,7 @@ bool blockchain_storage::checkParentBlockSize(const Block& b, const crypto::hash return true; } -bool blockchain_storage::checkCumulativeBlockSize(const crypto::hash& blockId, size_t cumulativeBlockSize, uint64_t height) { +bool Blockchain::checkCumulativeBlockSize(const Crypto::Hash& blockId, size_t cumulativeBlockSize, uint64_t height) { size_t maxBlockCumulativeSize = m_currency.maxBlockCumulativeSize(height); if (cumulativeBlockSize > maxBlockCumulativeSize) { logger(INFO, BRIGHT_WHITE) << @@ -1562,22 +1626,22 @@ bool blockchain_storage::checkCumulativeBlockSize(const crypto::hash& blockId, s } // Returns true, if cumulativeSize is calculated precisely, else returns false. -bool blockchain_storage::getBlockCumulativeSize(const Block& block, size_t& cumulativeSize) { +bool Blockchain::getBlockCumulativeSize(const Block& block, size_t& cumulativeSize) { std::vector blockTxs; - std::vector missedTxs; - get_transactions(block.txHashes, blockTxs, missedTxs, true); + std::vector missedTxs; + getTransactions(block.transactionHashes, blockTxs, missedTxs, true); - cumulativeSize = get_object_blobsize(block.minerTx); + cumulativeSize = getObjectBinarySize(block.baseTransaction); for (const Transaction& tx : blockTxs) { - cumulativeSize += get_object_blobsize(tx); + cumulativeSize += getObjectBinarySize(tx); } return missedTxs.empty(); } // Precondition: m_blockchain_lock is locked. -bool blockchain_storage::update_next_comulative_size_limit() { - uint8_t nextBlockMajorVersion = get_block_major_version_for_height(m_blocks.size()); +bool Blockchain::update_next_comulative_size_limit() { + uint8_t nextBlockMajorVersion = getBlockMajorVersionForHeight(static_cast(m_blocks.size())); size_t nextBlockGrantedFullRewardZone = nextBlockMajorVersion == BLOCK_MAJOR_VERSION_1 ? parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 : m_currency.blockGrantedFullRewardZone(); @@ -1594,10 +1658,10 @@ bool blockchain_storage::update_next_comulative_size_limit() { return true; } -bool blockchain_storage::add_new_block(const Block& bl_, block_verification_context& bvc) { +bool Blockchain::addNewBlock(const Block& bl_, block_verification_context& bvc) { //copy block here to let modify block.target Block bl = bl_; - crypto::hash id; + Crypto::Hash id; if (!get_block_hash(bl, id)) { logger(ERROR, BRIGHT_RED) << "Failed to get block hash, possible block has invalid format"; @@ -1611,19 +1675,22 @@ bool blockchain_storage::add_new_block(const Block& bl_, block_verification_cont std::lock_guard poolLock(m_tx_pool); std::lock_guard bcLock(m_blockchain_lock); - if (have_block(id)) { + if (haveBlock(id)) { logger(TRACE) << "block with id = " << id << " already exists"; bvc.m_already_exists = true; return false; } //check that block refers to chain tail - if (!(bl.prevId == get_tail_id())) { + if (!(bl.previousBlockHash == getTailId())) { //chain switching or wrong block bvc.m_added_to_main_chain = false; add_result = handle_alternative_block(bl, id, bvc); } else { add_result = pushBlock(bl, bvc); + if (add_result) { + sendMessage(BlockchainMessage(std::move(NewBlockMessage(id)))); + } } } @@ -1634,16 +1701,31 @@ bool blockchain_storage::add_new_block(const Block& bl_, block_verification_cont return add_result; } -const blockchain_storage::TransactionEntry& blockchain_storage::transactionByIndex(TransactionIndex index) { +const Blockchain::TransactionEntry& Blockchain::transactionByIndex(TransactionIndex index) { return m_blocks[index.block].transactions[index.transaction]; } -bool blockchain_storage::pushBlock(const Block& blockData, block_verification_context& bvc) { +bool Blockchain::pushBlock(const Block& blockData, block_verification_context& bvc) { + std::vector transactions; + if (!loadTransactions(blockData, transactions)) { + bvc.m_verifivation_failed = true; + return false; + } + + if (!pushBlock(blockData, transactions, bvc)) { + saveTransactions(transactions); + return false; + } + + return true; +} + +bool Blockchain::pushBlock(const Block& blockData, const std::vector& transactions, block_verification_context& bvc) { std::lock_guard lk(m_blockchain_lock); auto blockProcessingStart = std::chrono::steady_clock::now(); - crypto::hash blockHash = get_block_hash(blockData); + Crypto::Hash blockHash = get_block_hash(blockData); if (m_blockIndex.hasBlock(blockHash)) { logger(ERROR, BRIGHT_RED) << @@ -1662,9 +1744,9 @@ bool blockchain_storage::pushBlock(const Block& blockData, block_verification_co return false; } - if (blockData.prevId != get_tail_id()) { + if (blockData.previousBlockHash != getTailId()) { logger(INFO, BRIGHT_WHITE) << - "Block " << blockHash << " has wrong prevId: " << blockData.prevId << ", expected: " << get_tail_id(); + "Block " << blockHash << " has wrong previousBlockHash: " << blockData.previousBlockHash << ", expected: " << getTailId(); bvc.m_verifivation_failed = true; return false; } @@ -1677,19 +1759,19 @@ bool blockchain_storage::pushBlock(const Block& blockData, block_verification_co } auto targetTimeStart = std::chrono::steady_clock::now(); - difficulty_type currentDifficulty = get_difficulty_for_next_block(); + difficulty_type currentDifficulty = getDifficultyForNextBlock(); auto target_calculating_time = std::chrono::duration_cast(std::chrono::steady_clock::now() - targetTimeStart).count(); - if (!(currentDifficulty)) { - logger(ERROR, BRIGHT_RED) << "!!!!!!!!! difficulty overhead !!!!!!!!!"; - return false; + if (!(currentDifficulty)) { + logger(ERROR, BRIGHT_RED) << "!!!!!!!!! difficulty overhead !!!!!!!!!"; + return false; } - + auto longhashTimeStart = std::chrono::steady_clock::now(); - crypto::hash proof_of_work = null_hash; - if (m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height())) { - if (!m_checkpoints.check_block(get_current_blockchain_height(), blockHash)) { + Crypto::Hash proof_of_work = NULL_HASH; + if (m_checkpoints.is_in_checkpoint_zone(getCurrentBlockchainHeight())) { + if (!m_checkpoints.check_block(getCurrentBlockchainHeight(), blockHash)) { logger(ERROR, BRIGHT_RED) << "CHECKPOINT VALIDATION FAILED"; bvc.m_verifivation_failed = true; @@ -1706,48 +1788,38 @@ bool blockchain_storage::pushBlock(const Block& blockData, block_verification_co auto longhash_calculating_time = std::chrono::duration_cast(std::chrono::steady_clock::now() - longhashTimeStart).count(); - if (!prevalidate_miner_transaction(blockData, m_blocks.size())) { + if (!prevalidate_miner_transaction(blockData, static_cast(m_blocks.size()))) { logger(INFO, BRIGHT_WHITE) << "Block " << blockHash << " failed to pass prevalidation"; bvc.m_verifivation_failed = true; return false; } - crypto::hash minerTransactionHash = get_transaction_hash(blockData.minerTx); + Crypto::Hash minerTransactionHash = getObjectHash(blockData.baseTransaction); BlockEntry block; block.bl = blockData; block.transactions.resize(1); - block.transactions[0].tx = blockData.minerTx; - TransactionIndex transactionIndex = {static_cast(m_blocks.size()), static_cast(0)}; + block.transactions[0].tx = blockData.baseTransaction; + TransactionIndex transactionIndex = { static_cast(m_blocks.size()), static_cast(0) }; pushTransaction(block, minerTransactionHash, transactionIndex); - size_t coinbase_blob_size = get_object_blobsize(blockData.minerTx); + size_t coinbase_blob_size = getObjectBinarySize(blockData.baseTransaction); size_t cumulative_block_size = coinbase_blob_size; uint64_t fee_summary = 0; - for (const crypto::hash& tx_id : blockData.txHashes) { + for (size_t i = 0; i < transactions.size(); ++i) { + const Crypto::Hash& tx_id = blockData.transactionHashes[i]; block.transactions.resize(block.transactions.size() + 1); size_t blob_size = 0; uint64_t fee = 0; - if (!m_tx_pool.take_tx(tx_id, block.transactions.back().tx, blob_size, fee)) { - logger(INFO, BRIGHT_WHITE) << - "Block " << blockHash << " has at least one unknown transaction: " << tx_id; - bvc.m_verifivation_failed = true; - tx_verification_context tvc = boost::value_initialized(); - block.transactions.pop_back(); - popTransactions(block, minerTransactionHash); - return false; - } + block.transactions.back().tx = transactions[i]; - if (!check_tx_inputs(block.transactions.back().tx)) { + blob_size = toBinaryArray(block.transactions.back().tx).size(); + fee = getInputAmount(block.transactions.back().tx) - getOutputAmount(block.transactions.back().tx); + if (!checkTransactionInputs(block.transactions.back().tx)) { logger(INFO, BRIGHT_WHITE) << "Block " << blockHash << " has at least one transaction with wrong inputs: " << tx_id; bvc.m_verifivation_failed = true; - tx_verification_context tvc = boost::value_initialized();; - if (!m_tx_pool.add_tx(block.transactions.back().tx, tvc, true)) { - logger(ERROR, BRIGHT_RED) << - "Cannot move transaction from blockchain to transaction pool."; - } block.transactions.pop_back(); popTransactions(block, minerTransactionHash); @@ -1769,7 +1841,7 @@ bool blockchain_storage::pushBlock(const Block& blockData, block_verification_co int64_t emissionChange = 0; uint64_t reward = 0; uint64_t already_generated_coins = m_blocks.empty() ? 0 : m_blocks.back().already_generated_coins; - if (!validate_miner_transaction(blockData, m_blocks.size(), cumulative_block_size, already_generated_coins, fee_summary, reward, emissionChange)) { + if (!validate_miner_transaction(blockData, static_cast(m_blocks.size()), cumulative_block_size, already_generated_coins, fee_summary, reward, emissionChange)) { logger(INFO, BRIGHT_WHITE) << "Block " << blockHash << " has invalid miner transaction"; bvc.m_verifivation_failed = true; popTransactions(block, minerTransactionHash); @@ -1804,25 +1876,39 @@ bool blockchain_storage::pushBlock(const Block& blockData, block_verification_co return true; } -bool blockchain_storage::pushBlock(BlockEntry& block) { - crypto::hash blockHash = get_block_hash(block.bl); +bool Blockchain::pushBlock(BlockEntry& block) { + Crypto::Hash blockHash = get_block_hash(block.bl); m_blocks.push_back(block); m_blockIndex.push(blockHash); + m_timestampIndex.add(block.bl.timestamp, blockHash); + m_generatedTransactionsIndex.add(block.bl); + assert(m_blockIndex.size() == m_blocks.size()); return true; } -void blockchain_storage::popBlock(const crypto::hash& blockHash) { +void Blockchain::popBlock(const Crypto::Hash& blockHash) { if (m_blocks.empty()) { logger(ERROR, BRIGHT_RED) << "Attempt to pop block from empty blockchain."; return; } - popTransactions(m_blocks.back(), get_transaction_hash(m_blocks.back().bl.minerTx)); + std::vector transactions(m_blocks.back().transactions.size() - 1); + for (size_t i = 0; i < m_blocks.back().transactions.size() - 1; ++i) { + transactions[i] = m_blocks.back().transactions[1 + i].tx; + } + + saveTransactions(transactions); + + popTransactions(m_blocks.back(), getObjectHash(m_blocks.back().bl.baseTransaction)); + + m_timestampIndex.remove(m_blocks.back().bl.timestamp, blockHash); + m_generatedTransactionsIndex.remove(m_blocks.back().bl); + m_blocks.pop_back(); m_blockIndex.pop(); @@ -1831,7 +1917,7 @@ void blockchain_storage::popBlock(const crypto::hash& blockHash) { m_upgradeDetector.blockPopped(); } -bool blockchain_storage::pushTransaction(BlockEntry& block, const crypto::hash& transactionHash, TransactionIndex transactionIndex) { +bool Blockchain::pushTransaction(BlockEntry& block, const Crypto::Hash& transactionHash, TransactionIndex transactionIndex) { auto result = m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex)); if (!result.second) { logger(ERROR, BRIGHT_RED) << @@ -1848,14 +1934,14 @@ bool blockchain_storage::pushTransaction(BlockEntry& block, const crypto::hash& return false; } - for (size_t i = 0; i < transaction.tx.vin.size(); ++i) { - if (transaction.tx.vin[i].type() == typeid(TransactionInputToKey)) { - auto result = m_spent_keys.insert(::boost::get(transaction.tx.vin[i]).keyImage); + for (size_t i = 0; i < transaction.tx.inputs.size(); ++i) { + if (transaction.tx.inputs[i].type() == typeid(KeyInput)) { + auto result = m_spent_keys.insert(::boost::get(transaction.tx.inputs[i]).keyImage); if (!result.second) { logger(ERROR, BRIGHT_RED) << "Double spending transaction was pushed to blockchain."; for (size_t j = 0; j < i; ++j) { - m_spent_keys.erase(::boost::get(transaction.tx.vin[i - 1 - j]).keyImage); + m_spent_keys.erase(::boost::get(transaction.tx.inputs[i - 1 - j]).keyImage); } m_transactionMap.erase(transactionHash); @@ -1864,36 +1950,38 @@ bool blockchain_storage::pushTransaction(BlockEntry& block, const crypto::hash& } } - for (const auto& inv : transaction.tx.vin) { - if (inv.type() == typeid(TransactionInputMultisignature)) { - const TransactionInputMultisignature& in = ::boost::get(inv); + for (const auto& inv : transaction.tx.inputs) { + if (inv.type() == typeid(MultisignatureInput)) { + const MultisignatureInput& in = ::boost::get(inv); auto& amountOutputs = m_multisignatureOutputs[in.amount]; amountOutputs[in.outputIndex].isUsed = true; } } - transaction.m_global_output_indexes.resize(transaction.tx.vout.size()); - for (uint16_t output = 0; output < transaction.tx.vout.size(); ++output) { - if (transaction.tx.vout[output].target.type() == typeid(TransactionOutputToKey)) { - auto& amountOutputs = m_outputs[transaction.tx.vout[output].amount]; + transaction.m_global_output_indexes.resize(transaction.tx.outputs.size()); + for (uint16_t output = 0; output < transaction.tx.outputs.size(); ++output) { + if (transaction.tx.outputs[output].target.type() == typeid(KeyOutput)) { + auto& amountOutputs = m_outputs[transaction.tx.outputs[output].amount]; transaction.m_global_output_indexes[output] = static_cast(amountOutputs.size()); amountOutputs.push_back(std::make_pair<>(transactionIndex, output)); - } else if (transaction.tx.vout[output].target.type() == typeid(TransactionOutputMultisignature)) { - auto& amountOutputs = m_multisignatureOutputs[transaction.tx.vout[output].amount]; + } else if (transaction.tx.outputs[output].target.type() == typeid(MultisignatureOutput)) { + auto& amountOutputs = m_multisignatureOutputs[transaction.tx.outputs[output].amount]; transaction.m_global_output_indexes[output] = static_cast(amountOutputs.size()); - MultisignatureOutputUsage outputUsage = {transactionIndex, output, false}; + MultisignatureOutputUsage outputUsage = { transactionIndex, output, false }; amountOutputs.push_back(outputUsage); } } + m_paymentIdIndex.add(transaction.tx); + return true; } -void blockchain_storage::popTransaction(const Transaction& transaction, const crypto::hash& transactionHash) { +void Blockchain::popTransaction(const Transaction& transaction, const Crypto::Hash& transactionHash) { TransactionIndex transactionIndex = m_transactionMap.at(transactionHash); - for (size_t outputIndex = 0; outputIndex < transaction.vout.size(); ++outputIndex) { - const TransactionOutput& output = transaction.vout[transaction.vout.size() - 1 - outputIndex]; - if (output.target.type() == typeid(TransactionOutputToKey)) { + for (size_t outputIndex = 0; outputIndex < transaction.outputs.size(); ++outputIndex) { + const TransactionOutput& output = transaction.outputs[transaction.outputs.size() - 1 - outputIndex]; + if (output.target.type() == typeid(KeyOutput)) { auto amountOutputs = m_outputs.find(output.amount); if (amountOutputs == m_outputs.end()) { logger(ERROR, BRIGHT_RED) << @@ -1913,7 +2001,7 @@ void blockchain_storage::popTransaction(const Transaction& transaction, const cr continue; } - if (amountOutputs->second.back().second != transaction.vout.size() - 1 - outputIndex) { + if (amountOutputs->second.back().second != transaction.outputs.size() - 1 - outputIndex) { logger(ERROR, BRIGHT_RED) << "Blockchain consistency broken - invalid output index."; continue; @@ -1923,7 +2011,7 @@ void blockchain_storage::popTransaction(const Transaction& transaction, const cr if (amountOutputs->second.empty()) { m_outputs.erase(amountOutputs); } - } else if (output.target.type() == typeid(TransactionOutputMultisignature)) { + } else if (output.target.type() == typeid(MultisignatureOutput)) { auto amountOutputs = m_multisignatureOutputs.find(output.amount); if (amountOutputs == m_multisignatureOutputs.end()) { logger(ERROR, BRIGHT_RED) << @@ -1949,7 +2037,7 @@ void blockchain_storage::popTransaction(const Transaction& transaction, const cr continue; } - if (amountOutputs->second.back().outputIndex != transaction.vout.size() - 1 - outputIndex) { + if (amountOutputs->second.back().outputIndex != transaction.outputs.size() - 1 - outputIndex) { logger(ERROR, BRIGHT_RED) << "Blockchain consistency broken - invalid output index."; continue; @@ -1962,15 +2050,15 @@ void blockchain_storage::popTransaction(const Transaction& transaction, const cr } } - for (auto& input : transaction.vin) { - if (input.type() == typeid(TransactionInputToKey)) { - size_t count = m_spent_keys.erase(::boost::get(input).keyImage); + for (auto& input : transaction.inputs) { + if (input.type() == typeid(KeyInput)) { + size_t count = m_spent_keys.erase(::boost::get(input).keyImage); if (count != 1) { logger(ERROR, BRIGHT_RED) << "Blockchain consistency broken - cannot find spent key."; } - } else if (input.type() == typeid(TransactionInputMultisignature)) { - const TransactionInputMultisignature& in = ::boost::get(input); + } else if (input.type() == typeid(MultisignatureInput)) { + const MultisignatureInput& in = ::boost::get(input); auto& amountOutputs = m_multisignatureOutputs[in.amount]; if (!amountOutputs[in.outputIndex].isUsed) { logger(ERROR, BRIGHT_RED) << @@ -1981,6 +2069,8 @@ void blockchain_storage::popTransaction(const Transaction& transaction, const cr } } + m_paymentIdIndex.remove(transaction); + size_t count = m_transactionMap.erase(transactionHash); if (count != 1) { logger(ERROR, BRIGHT_RED) << @@ -1988,21 +2078,16 @@ void blockchain_storage::popTransaction(const Transaction& transaction, const cr } } -void blockchain_storage::popTransactions(const BlockEntry& block, const crypto::hash& minerTransactionHash) { +void Blockchain::popTransactions(const BlockEntry& block, const Crypto::Hash& minerTransactionHash) { for (size_t i = 0; i < block.transactions.size() - 1; ++i) { - popTransaction(block.transactions[block.transactions.size() - 1 - i].tx, block.bl.txHashes[block.transactions.size() - 2 - i]); - tx_verification_context tvc = boost::value_initialized(); - if (!m_tx_pool.add_tx(block.transactions[block.transactions.size() - 1 - i].tx, tvc, true)) { - logger(ERROR, BRIGHT_RED) << - "Cannot move transaction from blockchain to transaction pool."; - } + popTransaction(block.transactions[block.transactions.size() - 1 - i].tx, block.bl.transactionHashes[block.transactions.size() - 2 - i]); } - popTransaction(block.bl.minerTx, minerTransactionHash); + popTransaction(block.bl.baseTransaction, minerTransactionHash); } -bool blockchain_storage::validateInput(const TransactionInputMultisignature& input, const crypto::hash& transactionHash, const crypto::hash& transactionPrefixHash, const std::vector& transactionSignatures) { - assert(input.signatures == transactionSignatures.size()); +bool Blockchain::validateInput(const MultisignatureInput& input, const Crypto::Hash& transactionHash, const Crypto::Hash& transactionPrefixHash, const std::vector& transactionSignatures) { + assert(input.signatureCount == transactionSignatures.size()); MultisignatureOutputsContainer::const_iterator amountOutputs = m_multisignatureOutputs.find(input.amount); if (amountOutputs == m_multisignatureOutputs.end()) { logger(DEBUGGING) << @@ -2030,25 +2115,25 @@ bool blockchain_storage::validateInput(const TransactionInputMultisignature& inp return false; } - assert(outputTransaction.vout[outputIndex.outputIndex].amount == input.amount); - assert(outputTransaction.vout[outputIndex.outputIndex].target.type() == typeid(TransactionOutputMultisignature)); - const TransactionOutputMultisignature& output = ::boost::get(outputTransaction.vout[outputIndex.outputIndex].target); - if (input.signatures != output.requiredSignatures) { + assert(outputTransaction.outputs[outputIndex.outputIndex].amount == input.amount); + assert(outputTransaction.outputs[outputIndex.outputIndex].target.type() == typeid(MultisignatureOutput)); + const MultisignatureOutput& output = ::boost::get(outputTransaction.outputs[outputIndex.outputIndex].target); + if (input.signatureCount != output.requiredSignatureCount) { logger(DEBUGGING) << "Transaction << " << transactionHash << " contains multisignature input with invalid signature count."; return false; } - std::size_t inputSignatureIndex = 0; - std::size_t outputKeyIndex = 0; - while (inputSignatureIndex < input.signatures) { + size_t inputSignatureIndex = 0; + size_t outputKeyIndex = 0; + while (inputSignatureIndex < input.signatureCount) { if (outputKeyIndex == output.keys.size()) { logger(DEBUGGING) << "Transaction << " << transactionHash << " contains multisignature input with invalid signatures."; return false; } - if (crypto::check_signature(transactionPrefixHash, output.keys[outputKeyIndex], transactionSignatures[inputSignatureIndex])) { + if (Crypto::check_signature(transactionPrefixHash, output.keys[outputKeyIndex], transactionSignatures[inputSignatureIndex])) { ++inputSignatureIndex; } @@ -2058,12 +2143,10 @@ bool blockchain_storage::validateInput(const TransactionInputMultisignature& inp return true; } -bool blockchain_storage::getLowerBound(uint64_t timestamp, uint64_t startOffset, uint64_t& height) { +bool Blockchain::getLowerBound(uint64_t timestamp, uint64_t startOffset, uint32_t& height) { std::lock_guard lk(m_blockchain_lock); - if (startOffset >= m_blocks.size()) { - return false; - } + assert(startOffset < m_blocks.size()); auto bound = std::lower_bound(m_blocks.begin() + startOffset, m_blocks.end(), timestamp - m_currency.blockFutureTimeLimit(), [](const BlockEntry& b, uint64_t timestamp) { return b.bl.timestamp < timestamp; }); @@ -2072,32 +2155,32 @@ bool blockchain_storage::getLowerBound(uint64_t timestamp, uint64_t startOffset, return false; } - height = std::distance(m_blocks.begin(), bound); + height = static_cast(std::distance(m_blocks.begin(), bound)); return true; } -bool blockchain_storage::getBlockIds(uint64_t startHeight, size_t maxCount, std::list& items) { +std::vector Blockchain::getBlockIds(uint32_t startHeight, uint32_t maxCount) { std::lock_guard lk(m_blockchain_lock); - return m_blockIndex.getBlockIds(startHeight, maxCount, items); + return m_blockIndex.getBlockIds(startHeight, maxCount); } -bool blockchain_storage::getBlockContainingTx(const crypto::hash& txId, crypto::hash& blockId, uint64_t& blockHeight) { +bool Blockchain::getBlockContainingTransaction(const Crypto::Hash& txId, Crypto::Hash& blockId, uint32_t& blockHeight) { std::lock_guard lk(m_blockchain_lock); auto it = m_transactionMap.find(txId); - if(it == m_transactionMap.end()) { + if (it == m_transactionMap.end()) { return false; } else { blockHeight = m_blocks[it->second.block].height; - blockId = get_block_id_by_height(blockHeight); + blockId = getBlockIdByHeight(blockHeight); return true; } } -bool blockchain_storage::getAlreadyGeneratedCoins(const crypto::hash& hash, uint64_t& generatedCoins) { +bool Blockchain::getAlreadyGeneratedCoins(const Crypto::Hash& hash, uint64_t& generatedCoins) { std::lock_guard lk(m_blockchain_lock); // try to find block in main chain - uint64_t height = 0; + uint32_t height = 0; if (m_blockIndex.getBlockHeight(hash, height)) { generatedCoins = m_blocks[height].already_generated_coins; return true; @@ -2114,11 +2197,11 @@ bool blockchain_storage::getAlreadyGeneratedCoins(const crypto::hash& hash, uint return false; } -bool blockchain_storage::getBlockSize(const crypto::hash& hash, size_t& size) { +bool Blockchain::getBlockSize(const Crypto::Hash& hash, size_t& size) { std::lock_guard lk(m_blockchain_lock); // try to find block in main chain - uint64_t height = 0; + uint32_t height = 0; if (m_blockIndex.getBlockHeight(hash, height)) { size = m_blocks[height].block_cumulative_size; return true; @@ -2135,7 +2218,7 @@ bool blockchain_storage::getBlockSize(const crypto::hash& hash, size_t& size) { return false; } -bool blockchain_storage::getMultisigOutputReference(const TransactionInputMultisignature& txInMultisig, std::pair& outputReference) { +bool Blockchain::getMultisigOutputReference(const MultisignatureInput& txInMultisig, std::pair& outputReference) { std::lock_guard lk(m_blockchain_lock); MultisignatureOutputsContainer::const_iterator amountIter = m_multisignatureOutputs.find(txInMultisig.amount); if (amountIter == m_multisignatureOutputs.end()) { @@ -2148,8 +2231,125 @@ bool blockchain_storage::getMultisigOutputReference(const TransactionInputMultis } const MultisignatureOutputUsage& outputIndex = amountIter->second[txInMultisig.outputIndex]; const Transaction& outputTransaction = m_blocks[outputIndex.transactionIndex.block].transactions[outputIndex.transactionIndex.transaction].tx; - outputReference.first = get_transaction_hash(outputTransaction); + outputReference.first = getObjectHash(outputTransaction); outputReference.second = outputIndex.outputIndex; return true; } + +bool Blockchain::storeBlockchainIndices() { + std::lock_guard lk(m_blockchain_lock); + + logger(INFO, BRIGHT_WHITE) << "Saving blockchain indices..."; + BlockchainIndicesSerializer ser(*this, getTailId(), logger.getLogger()); + + if (!storeToBinaryFile(ser, appendPath(m_config_folder, m_currency.blockchinIndicesFileName()))) { + logger(ERROR, BRIGHT_RED) << "Failed to save blockchain indices"; + return false; + } + + return true; +} + +bool Blockchain::loadBlockchainIndices() { + std::lock_guard lk(m_blockchain_lock); + + logger(INFO, BRIGHT_WHITE) << "Loading blockchain indices for BlockchainExplorer..."; + BlockchainIndicesSerializer loader(*this, get_block_hash(m_blocks.back().bl), logger.getLogger()); + + loadFromBinaryFile(loader, appendPath(m_config_folder, m_currency.blockchinIndicesFileName())); + + if (!loader.loaded()) { + logger(WARNING, BRIGHT_YELLOW) << "No actual blockchain indices for BlockchainExplorer found, rebuilding..."; + std::chrono::steady_clock::time_point timePoint = std::chrono::steady_clock::now(); + + m_paymentIdIndex.clear(); + m_timestampIndex.clear(); + m_generatedTransactionsIndex.clear(); + + for (uint32_t b = 0; b < m_blocks.size(); ++b) { + if (b % 1000 == 0) { + logger(INFO, BRIGHT_WHITE) << "Height " << b << " of " << m_blocks.size(); + } + const BlockEntry& block = m_blocks[b]; + m_timestampIndex.add(block.bl.timestamp, get_block_hash(block.bl)); + m_generatedTransactionsIndex.add(block.bl); + for (uint16_t t = 0; t < block.transactions.size(); ++t) { + const TransactionEntry& transaction = block.transactions[t]; + m_paymentIdIndex.add(transaction.tx); + } + } + + std::chrono::duration duration = std::chrono::steady_clock::now() - timePoint; + logger(INFO, BRIGHT_WHITE) << "Rebuilding blockchain indices took: " << duration.count(); + } + return true; +} + +bool Blockchain::getGeneratedTransactionsNumber(uint32_t height, uint64_t& generatedTransactions) { + std::lock_guard lk(m_blockchain_lock); + return m_generatedTransactionsIndex.find(height, generatedTransactions); +} + +bool Blockchain::getOrphanBlockIdsByHeight(uint32_t height, std::vector& blockHashes) { + std::lock_guard lk(m_blockchain_lock); + return m_orthanBlocksIndex.find(height, blockHashes); +} + +bool Blockchain::getBlockIdsByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& hashes, uint32_t& blocksNumberWithinTimestamps) { + std::lock_guard lk(m_blockchain_lock); + return m_timestampIndex.find(timestampBegin, timestampEnd, blocksNumberLimit, hashes, blocksNumberWithinTimestamps); +} + +bool Blockchain::getTransactionIdsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactionHashes) { + std::lock_guard lk(m_blockchain_lock); + return m_paymentIdIndex.find(paymentId, transactionHashes); +} + +bool Blockchain::loadTransactions(const Block& block, std::vector& transactions) { + transactions.resize(block.transactionHashes.size()); + size_t transactionSize; + uint64_t fee; + for (size_t i = 0; i < block.transactionHashes.size(); ++i) { + if (!m_tx_pool.take_tx(block.transactionHashes[i], transactions[i], transactionSize, fee)) { + tx_verification_context context; + for (size_t j = 0; j < i; ++j) { + if (!m_tx_pool.add_tx(transactions[i - 1 - j], context, true)) { + throw std::runtime_error("Blockchain::loadTransactions, failed to add transaction to pool"); + } + } + + return false; + } + } + + return true; +} + +void Blockchain::saveTransactions(const std::vector& transactions) { + tx_verification_context context; + for (size_t i = 0; i < transactions.size(); ++i) { + if (!m_tx_pool.add_tx(transactions[transactions.size() - 1 - i], context, true)) { + throw std::runtime_error("Blockchain::saveTransactions, failed to add transaction to pool"); + } + } +} + +bool Blockchain::addMessageQueue(MessageQueue& messageQueue) { + return m_messageQueueList.insert(messageQueue); +} + +bool Blockchain::removeMessageQueue(MessageQueue& messageQueue) { + return m_messageQueueList.remove(messageQueue); +} + +void Blockchain::sendMessage(const BlockchainMessage& message) { + for (IntrusiveLinkedList>::iterator iter = m_messageQueueList.begin(); iter != m_messageQueueList.end(); ++iter) { + iter->push(message); + } +} + +bool Blockchain::isBlockInMainChain(const Crypto::Hash& blockId) { + return m_blockIndex.hasBlock(blockId); +} + } diff --git a/src/CryptoNoteCore/Blockchain.h b/src/CryptoNoteCore/Blockchain.h new file mode 100755 index 00000000..f748fbfc --- /dev/null +++ b/src/CryptoNoteCore/Blockchain.h @@ -0,0 +1,382 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +#include "google/sparse_hash_set" +#include "google/sparse_hash_map" + +#include "Common/ObserverManager.h" +#include "Common/Util.h" +#include "CryptoNoteCore/BlockIndex.h" +#include "CryptoNoteCore/Checkpoints.h" +#include "CryptoNoteCore/Currency.h" +#include "CryptoNoteCore/IBlockchainStorageObserver.h" +#include "CryptoNoteCore/ITransactionValidator.h" +#include "CryptoNoteCore/SwappedVector.h" +#include "CryptoNoteCore/UpgradeDetector.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteCore/TransactionPool.h" +#include "CryptoNoteCore/BlockchainIndices.h" + +#include "CryptoNoteCore/MessageQueue.h" +#include "CryptoNoteCore/BlockchainMessages.h" +#include "CryptoNoteCore/IntrusiveLinkedList.h" + +#include + +#undef ERROR + +namespace CryptoNote { + struct NOTIFY_REQUEST_GET_OBJECTS_request; + struct NOTIFY_RESPONSE_GET_OBJECTS_request; + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request; + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response; + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount; + + using CryptoNote::BlockInfo; + class Blockchain : public CryptoNote::ITransactionValidator { + public: + Blockchain(const Currency& currency, tx_memory_pool& tx_pool, Logging::ILogger& logger); + + bool addObserver(IBlockchainStorageObserver* observer); + bool removeObserver(IBlockchainStorageObserver* observer); + + // ITransactionValidator + virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock) override; + virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed) override; + virtual bool haveSpentKeyImages(const CryptoNote::Transaction& tx) override; + virtual bool checkTransactionSize(size_t blobSize) override; + + bool init() { return init(Tools::getDefaultDataDirectory(), true); } + bool init(const std::string& config_folder, bool load_existing); + bool deinit(); + + bool getLowerBound(uint64_t timestamp, uint64_t startOffset, uint32_t& height); + std::vector getBlockIds(uint32_t startHeight, uint32_t maxCount); + + void setCheckpoints(Checkpoints&& chk_pts) { m_checkpoints = chk_pts; } + bool getBlocks(uint32_t start_offset, uint32_t count, std::list& blocks, std::list& txs); + bool getBlocks(uint32_t start_offset, uint32_t count, std::list& blocks); + bool getAlternativeBlocks(std::list& blocks); + uint32_t getAlternativeBlocksCount(); + Crypto::Hash getBlockIdByHeight(uint32_t height); + bool getBlockByHash(const Crypto::Hash &h, Block &blk); + bool getBlockHeight(const Crypto::Hash& blockId, uint32_t& blockHeight); + + template void serialize(archive_t & ar, const unsigned int version); + + bool haveTransaction(const Crypto::Hash &id); + bool haveTransactionKeyImagesAsSpent(const Transaction &tx); + + uint32_t getCurrentBlockchainHeight(); //TODO rename to getCurrentBlockchainSize + Crypto::Hash getTailId(); + Crypto::Hash getTailId(uint32_t& height); + difficulty_type getDifficultyForNextBlock(); + uint64_t getCoinsInCirculation(); + uint8_t getBlockMajorVersionForHeight(uint32_t height) const; + bool addNewBlock(const Block& bl_, block_verification_context& bvc); + bool resetAndSetGenesisBlock(const Block& b); + bool haveBlock(const Crypto::Hash& id); + size_t getTotalTransactions(); + std::vector buildSparseChain(); + std::vector buildSparseChain(const Crypto::Hash& startBlockId); + uint32_t findBlockchainSupplement(const std::vector& qblock_ids); // !!!! + std::vector findBlockchainSupplement(const std::vector& remoteBlockIds, size_t maxCount, + uint32_t& totalBlockCount, uint32_t& startBlockIndex); + bool handleGetObjects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp); //Deprecated. Should be removed with CryptoNoteProtocolHandler. + bool getRandomOutsByAmount(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res); + bool getBackwardBlocksSize(size_t from_height, std::vector& sz, size_t count); + bool getTransactionOutputGlobalIndexes(const Crypto::Hash& tx_id, std::vector& indexs); + bool get_out_by_msig_gindex(uint64_t amount, uint64_t gindex, MultisignatureOutput& out); + bool checkTransactionInputs(const Transaction& tx, uint32_t& pmax_used_block_height, Crypto::Hash& max_used_block_id, BlockInfo* tail = 0); + uint64_t getCurrentCumulativeBlocksizeLimit(); + bool isStoringBlockchain(){return m_is_blockchain_storing;} + uint64_t blockDifficulty(size_t i); + bool getBlockContainingTransaction(const Crypto::Hash& txId, Crypto::Hash& blockId, uint32_t& blockHeight); + bool getAlreadyGeneratedCoins(const Crypto::Hash& hash, uint64_t& generatedCoins); + bool getBlockSize(const Crypto::Hash& hash, size_t& size); + bool getMultisigOutputReference(const MultisignatureInput& txInMultisig, std::pair& outputReference); + bool getGeneratedTransactionsNumber(uint32_t height, uint64_t& generatedTransactions); + bool getOrphanBlockIdsByHeight(uint32_t height, std::vector& blockHashes); + bool getBlockIdsByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& hashes, uint32_t& blocksNumberWithinTimestamps); + bool getTransactionIdsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactionHashes); + bool isBlockInMainChain(const Crypto::Hash& blockId); + + template bool scanOutputKeysForIndexes(const KeyInput& tx_in_to_key, visitor_t& vis, uint32_t* pmax_related_block_height = NULL); + + bool addMessageQueue(MessageQueue& messageQueue); + bool removeMessageQueue(MessageQueue& messageQueue); + + template + bool getBlocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) { + std::lock_guard lk(m_blockchain_lock); + + for (const auto& bl_id : block_ids) { + uint32_t height = 0; + if (!m_blockIndex.getBlockHeight(bl_id, height)) { + missed_bs.push_back(bl_id); + } else { + if (!(height < m_blocks.size())) { logger(Logging::ERROR, Logging::BRIGHT_RED) << "Internal error: bl_id=" << Common::podToHex(bl_id) + << " have index record with offset=" << height << ", bigger then m_blocks.size()=" << m_blocks.size(); return false; } + blocks.push_back(m_blocks[height].bl); + } + } + + return true; + } + + template + void getBlockchainTransactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) { + std::lock_guard bcLock(m_blockchain_lock); + + for (const auto& tx_id : txs_ids) { + auto it = m_transactionMap.find(tx_id); + if (it == m_transactionMap.end()) { + missed_txs.push_back(tx_id); + } else { + txs.push_back(transactionByIndex(it->second).tx); + } + } + } + + template + void getTransactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs, bool checkTxPool = false) { + if (checkTxPool){ + std::lock_guard txLock(m_tx_pool); + + getBlockchainTransactions(txs_ids, txs, missed_txs); + + auto poolTxIds = std::move(missed_txs); + missed_txs.clear(); + m_tx_pool.getTransactions(poolTxIds, txs, missed_txs); + + } else { + getBlockchainTransactions(txs_ids, txs, missed_txs); + } + } + + //debug functions + void print_blockchain(uint64_t start_index, uint64_t end_index); + void print_blockchain_index(); + void print_blockchain_outs(const std::string& file); + + struct TransactionIndex { + uint32_t block; + uint16_t transaction; + + void serialize(ISerializer& s) { + s(block, "block"); + s(transaction, "tx"); + } + }; + + private: + + struct MultisignatureOutputUsage { + TransactionIndex transactionIndex; + uint16_t outputIndex; + bool isUsed; + + void serialize(ISerializer& s) { + s(transactionIndex, "txindex"); + s(outputIndex, "outindex"); + s(isUsed, "used"); + } + }; + + struct TransactionEntry { + Transaction tx; + std::vector m_global_output_indexes; + + void serialize(ISerializer& s) { + s(tx, "tx"); + s(m_global_output_indexes, "indexes"); + } + }; + + struct BlockEntry { + Block bl; + uint32_t height; + uint64_t block_cumulative_size; + difficulty_type cumulative_difficulty; + uint64_t already_generated_coins; + std::vector transactions; + + void serialize(ISerializer& s) { + s(bl, "block"); + s(height, "height"); + s(block_cumulative_size, "block_cumulative_size"); + s(cumulative_difficulty, "cumulative_difficulty"); + s(already_generated_coins, "already_generated_coins"); + s(transactions, "transactions"); + } + }; + + typedef google::sparse_hash_set key_images_container; + typedef std::unordered_map blocks_ext_by_hash; + typedef google::sparse_hash_map>> outputs_container; //Crypto::Hash - tx hash, size_t - index of out in transaction + typedef google::sparse_hash_map> MultisignatureOutputsContainer; + + const Currency& m_currency; + tx_memory_pool& m_tx_pool; + std::recursive_mutex m_blockchain_lock; // TODO: add here reader/writer lock + Crypto::cn_context m_cn_context; + Tools::ObserverManager m_observerManager; + + key_images_container m_spent_keys; + size_t m_current_block_cumul_sz_limit; + blocks_ext_by_hash m_alternative_chains; // Crypto::Hash -> block_extended_info + outputs_container m_outputs; + + std::string m_config_folder; + Checkpoints m_checkpoints; + std::atomic m_is_in_checkpoint_zone; + std::atomic m_is_blockchain_storing; + + typedef SwappedVector Blocks; + typedef std::unordered_map BlockMap; + typedef std::unordered_map TransactionMap; + typedef BasicUpgradeDetector UpgradeDetector; + + friend class BlockCacheSerializer; + friend class BlockchainIndicesSerializer; + + Blocks m_blocks; + CryptoNote::BlockIndex m_blockIndex; + TransactionMap m_transactionMap; + MultisignatureOutputsContainer m_multisignatureOutputs; + UpgradeDetector m_upgradeDetector; + + PaymentIdIndex m_paymentIdIndex; + TimestampBlocksIndex m_timestampIndex; + GeneratedTransactionsIndex m_generatedTransactionsIndex; + OrphanBlocksIndex m_orthanBlocksIndex; + + IntrusiveLinkedList> m_messageQueueList; + + Logging::LoggerRef logger; + + void rebuildCache(); + bool storeCache(); + bool switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain); + bool handle_alternative_block(const Block& b, const Crypto::Hash& id, block_verification_context& bvc, bool sendNewAlternativeBlockMessage = true); + difficulty_type get_next_difficulty_for_alternative_chain(const std::list& alt_chain, BlockEntry& bei); + bool prevalidate_miner_transaction(const Block& b, uint32_t height); + bool validate_miner_transaction(const Block& b, uint32_t height, size_t cumulativeBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, uint64_t& reward, int64_t& emissionChange); + bool rollback_blockchain_switching(std::list& original_chain, size_t rollback_height); + bool get_last_n_blocks_sizes(std::vector& sz, size_t count); + bool add_out_to_get_random_outs(std::vector>& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount& result_outs, uint64_t amount, size_t i); + bool is_tx_spendtime_unlocked(uint64_t unlock_time); + size_t find_end_of_allowed_index(const std::vector>& amount_outs); + bool check_block_timestamp_main(const Block& b); + bool check_block_timestamp(std::vector timestamps, const Block& b); + uint64_t get_adjusted_time(); + bool complete_timestamps_vector(uint64_t start_height, std::vector& timestamps); + bool checkBlockVersion(const Block& b, const Crypto::Hash& blockHash); + bool checkParentBlockSize(const Block& b, const Crypto::Hash& blockHash); + bool checkCumulativeBlockSize(const Crypto::Hash& blockId, size_t cumulativeBlockSize, uint64_t height); + std::vector doBuildSparseChain(const Crypto::Hash& startBlockId) const; + bool getBlockCumulativeSize(const Block& block, size_t& cumulativeSize); + bool update_next_comulative_size_limit(); + bool check_tx_input(const KeyInput& txin, const Crypto::Hash& tx_prefix_hash, const std::vector& sig, uint32_t* pmax_related_block_height = NULL); + bool checkTransactionInputs(const Transaction& tx, const Crypto::Hash& tx_prefix_hash, uint32_t* pmax_used_block_height = NULL); + bool checkTransactionInputs(const Transaction& tx, uint32_t* pmax_used_block_height = NULL); + bool have_tx_keyimg_as_spent(const Crypto::KeyImage &key_im); + const TransactionEntry& transactionByIndex(TransactionIndex index); + bool pushBlock(const Block& blockData, block_verification_context& bvc); + bool pushBlock(const Block& blockData, const std::vector& transactions, block_verification_context& bvc); + bool pushBlock(BlockEntry& block); + void popBlock(const Crypto::Hash& blockHash); + bool pushTransaction(BlockEntry& block, const Crypto::Hash& transactionHash, TransactionIndex transactionIndex); + void popTransaction(const Transaction& transaction, const Crypto::Hash& transactionHash); + void popTransactions(const BlockEntry& block, const Crypto::Hash& minerTransactionHash); + bool validateInput(const MultisignatureInput& input, const Crypto::Hash& transactionHash, const Crypto::Hash& transactionPrefixHash, const std::vector& transactionSignatures); + + bool storeBlockchainIndices(); + bool loadBlockchainIndices(); + + bool loadTransactions(const Block& block, std::vector& transactions); + void saveTransactions(const std::vector& transactions); + + void sendMessage(const BlockchainMessage& message); + + friend class LockedBlockchainStorage; + }; + + class LockedBlockchainStorage: boost::noncopyable { + public: + + LockedBlockchainStorage(Blockchain& bc) + : m_bc(bc), m_lock(bc.m_blockchain_lock) {} + + Blockchain* operator -> () { + return &m_bc; + } + + private: + + Blockchain& m_bc; + std::lock_guard m_lock; + }; + + template bool Blockchain::scanOutputKeysForIndexes(const KeyInput& tx_in_to_key, visitor_t& vis, uint32_t* pmax_related_block_height) { + std::lock_guard lk(m_blockchain_lock); + auto it = m_outputs.find(tx_in_to_key.amount); + if (it == m_outputs.end() || !tx_in_to_key.outputIndexes.size()) + return false; + + std::vector absolute_offsets = relative_output_offsets_to_absolute(tx_in_to_key.outputIndexes); + std::vector>& amount_outs_vec = it->second; + size_t count = 0; + for (uint64_t i : absolute_offsets) { + if(i >= amount_outs_vec.size() ) { + logger(Logging::INFO) << "Wrong index in transaction inputs: " << i << ", expected maximum " << amount_outs_vec.size() - 1; + return false; + } + + //auto tx_it = m_transactionMap.find(amount_outs_vec[i].first); + //if (!(tx_it != m_transactionMap.end())) { logger(ERROR, BRIGHT_RED) << "Wrong transaction id in output indexes: " << Common::podToHex(amount_outs_vec[i].first); return false; } + + const TransactionEntry& tx = transactionByIndex(amount_outs_vec[i].first); + + if (!(amount_outs_vec[i].second < tx.tx.outputs.size())) { + logger(Logging::ERROR, Logging::BRIGHT_RED) + << "Wrong index in transaction outputs: " + << amount_outs_vec[i].second << ", expected less then " + << tx.tx.outputs.size(); + return false; + } + + if (!vis.handle_output(tx.tx, tx.tx.outputs[amount_outs_vec[i].second], amount_outs_vec[i].second)) { + logger(Logging::INFO) << "Failed to handle_output for output no = " << count << ", with absolute offset " << i; + return false; + } + + if(count++ == absolute_offsets.size()-1 && pmax_related_block_height) { + if (*pmax_related_block_height < amount_outs_vec[i].first.block) { + *pmax_related_block_height = amount_outs_vec[i].first.block; + } + } + } + + return true; + } +} + diff --git a/src/CryptoNoteCore/BlockchainIndices.cpp b/src/CryptoNoteCore/BlockchainIndices.cpp new file mode 100755 index 00000000..584daa50 --- /dev/null +++ b/src/CryptoNoteCore/BlockchainIndices.cpp @@ -0,0 +1,262 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "BlockchainIndices.h" + +#include "Common/StringTools.h" +#include "CryptoNoteCore/CryptoNoteTools.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "BlockchainExplorer/BlockchainExplorerDataBuilder.h" +#include "CryptoNoteBasicImpl.h" + +namespace CryptoNote { + +bool PaymentIdIndex::add(const Transaction& transaction) { + Crypto::Hash paymentId; + Crypto::Hash transactionHash = getObjectHash(transaction); + if (!BlockchainExplorerDataBuilder::getPaymentId(transaction, paymentId)) { + return false; + } + + index.emplace(paymentId, transactionHash); + + return true; +} + +bool PaymentIdIndex::remove(const Transaction& transaction) { + Crypto::Hash paymentId; + Crypto::Hash transactionHash = getObjectHash(transaction); + if (!BlockchainExplorerDataBuilder::getPaymentId(transaction, paymentId)) { + return false; + } + + auto range = index.equal_range(paymentId); + for (auto iter = range.first; iter != range.second; ++iter){ + if (iter->second == transactionHash) { + index.erase(iter); + return true; + } + } + + return false; +} + +bool PaymentIdIndex::find(const Crypto::Hash& paymentId, std::vector& transactionHashes) { + bool found = false; + auto range = index.equal_range(paymentId); + for (auto iter = range.first; iter != range.second; ++iter){ + found = true; + transactionHashes.emplace_back(iter->second); + } + return found; +} + +void PaymentIdIndex::clear() { + index.clear(); +} + + +void PaymentIdIndex::serialize(ISerializer& s) { + s(index, "index"); +} + +bool TimestampBlocksIndex::add(uint64_t timestamp, const Crypto::Hash& hash) { + index.emplace(timestamp, hash); + return true; +} + +bool TimestampBlocksIndex::remove(uint64_t timestamp, const Crypto::Hash& hash) { + auto range = index.equal_range(timestamp); + for (auto iter = range.first; iter != range.second; ++iter) { + if (iter->second == hash) { + index.erase(iter); + return true; + } + } + + return false; +} + +bool TimestampBlocksIndex::find(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t hashesNumberLimit, std::vector& hashes, uint32_t& hashesNumberWithinTimestamps) { + uint32_t hashesNumber = 0; + if (timestampBegin > timestampEnd) { + //std::swap(timestampBegin, timestampEnd); + return false; + } + auto begin = index.lower_bound(timestampBegin); + auto end = index.upper_bound(timestampEnd); + + hashesNumberWithinTimestamps = static_cast(std::distance(begin, end)); + + for (auto iter = begin; iter != end && hashesNumber < hashesNumberLimit; ++iter){ + ++hashesNumber; + hashes.emplace_back(iter->second); + } + return hashesNumber > 0; +} + +void TimestampBlocksIndex::clear() { + index.clear(); +} + +void TimestampBlocksIndex::serialize(ISerializer& s) { + s(index, "index"); +} + +bool TimestampTransactionsIndex::add(uint64_t timestamp, const Crypto::Hash& hash) { + index.emplace(timestamp, hash); + return true; +} + +bool TimestampTransactionsIndex::remove(uint64_t timestamp, const Crypto::Hash& hash) { + auto range = index.equal_range(timestamp); + for (auto iter = range.first; iter != range.second; ++iter) { + if (iter->second == hash) { + index.erase(iter); + return true; + } + } + + return false; +} + +bool TimestampTransactionsIndex::find(uint64_t timestampBegin, uint64_t timestampEnd, uint64_t hashesNumberLimit, std::vector& hashes, uint64_t& hashesNumberWithinTimestamps) { + uint32_t hashesNumber = 0; + if (timestampBegin > timestampEnd) { + //std::swap(timestampBegin, timestampEnd); + return false; + } + auto begin = index.lower_bound(timestampBegin); + auto end = index.upper_bound(timestampEnd); + + hashesNumberWithinTimestamps = static_cast(std::distance(begin, end)); + + for (auto iter = begin; iter != end && hashesNumber < hashesNumberLimit; ++iter) { + ++hashesNumber; + hashes.emplace_back(iter->second); + } + return hashesNumber > 0; +} + +void TimestampTransactionsIndex::clear() { + index.clear(); +} + +void TimestampTransactionsIndex::serialize(ISerializer& s) { + s(index, "index"); +} + +GeneratedTransactionsIndex::GeneratedTransactionsIndex() : lastGeneratedTxNumber(0) { + +} + +bool GeneratedTransactionsIndex::add(const Block& block) { + uint32_t blockHeight = boost::get(block.baseTransaction.inputs.front()).blockIndex; + + if (index.size() != blockHeight) { + return false; + } + + bool status = index.emplace(blockHeight, lastGeneratedTxNumber + block.transactionHashes.size() + 1).second; //Plus miner tx + if (status) { + lastGeneratedTxNumber += block.transactionHashes.size() + 1; + } + return status; +} + +bool GeneratedTransactionsIndex::remove(const Block& block) { + uint32_t blockHeight = boost::get(block.baseTransaction.inputs.front()).blockIndex; + + if (blockHeight != index.size() - 1) { + return false; + } + + auto iter = index.find(blockHeight); + assert(iter != index.end()); + index.erase(iter); + + if (blockHeight != 0) { + iter = index.find(blockHeight - 1); + assert(iter != index.end()); + lastGeneratedTxNumber = iter->second; + } else { + lastGeneratedTxNumber = 0; + } + + return true; +} + +bool GeneratedTransactionsIndex::find(uint32_t height, uint64_t& generatedTransactions) { + if (height > std::numeric_limits::max()) { + return false; + } + auto iter = index.find(height); + if (iter == index.end()) { + return false; + } + generatedTransactions = iter->second; + return true; +} + +void GeneratedTransactionsIndex::clear() { + index.clear(); +} + +void GeneratedTransactionsIndex::serialize(ISerializer& s) { + s(index, "index"); + s(lastGeneratedTxNumber, "lastGeneratedTxNumber"); +} + +bool OrphanBlocksIndex::add(const Block& block) { + Crypto::Hash blockHash = get_block_hash(block); + uint32_t blockHeight = boost::get(block.baseTransaction.inputs.front()).blockIndex; + index.emplace(blockHeight, blockHash); + return true; +} + +bool OrphanBlocksIndex::remove(const Block& block) { + Crypto::Hash blockHash = get_block_hash(block); + uint32_t blockHeight = boost::get(block.baseTransaction.inputs.front()).blockIndex; + auto range = index.equal_range(blockHeight); + for (auto iter = range.first; iter != range.second; ++iter) { + if (iter->second == blockHash) { + index.erase(iter); + return true; + } + } + + return false; +} + +bool OrphanBlocksIndex::find(uint32_t height, std::vector& blockHashes) { + if (height > std::numeric_limits::max()) { + return false; + } + bool found = false; + auto range = index.equal_range(height); + for (auto iter = range.first; iter != range.second; ++iter) { + found = true; + blockHashes.emplace_back(iter->second); + } + return found; +} + +void OrphanBlocksIndex::clear() { + index.clear(); +} + +} diff --git a/src/CryptoNoteCore/BlockchainIndices.h b/src/CryptoNoteCore/BlockchainIndices.h new file mode 100755 index 00000000..96d76189 --- /dev/null +++ b/src/CryptoNoteCore/BlockchainIndices.h @@ -0,0 +1,121 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +#include "crypto/hash.h" +#include "CryptoNoteBasic.h" + +namespace CryptoNote { + +class ISerializer; + +class PaymentIdIndex { +public: + PaymentIdIndex() = default; + + bool add(const Transaction& transaction); + bool remove(const Transaction& transaction); + bool find(const Crypto::Hash& paymentId, std::vector& transactionHashes); + void clear(); + + void serialize(ISerializer& s); + + template + void serialize(Archive& archive, unsigned int version) { + archive & index; + } +private: + std::unordered_multimap index; +}; + +class TimestampBlocksIndex { +public: + TimestampBlocksIndex() = default; + + bool add(uint64_t timestamp, const Crypto::Hash& hash); + bool remove(uint64_t timestamp, const Crypto::Hash& hash); + bool find(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t hashesNumberLimit, std::vector& hashes, uint32_t& hashesNumberWithinTimestamps); + void clear(); + + void serialize(ISerializer& s); + + template + void serialize(Archive& archive, unsigned int version) { + archive & index; + } +private: + std::multimap index; +}; + +class TimestampTransactionsIndex { +public: + TimestampTransactionsIndex() = default; + + bool add(uint64_t timestamp, const Crypto::Hash& hash); + bool remove(uint64_t timestamp, const Crypto::Hash& hash); + bool find(uint64_t timestampBegin, uint64_t timestampEnd, uint64_t hashesNumberLimit, std::vector& hashes, uint64_t& hashesNumberWithinTimestamps); + void clear(); + + void serialize(ISerializer& s); + + template + void serialize(Archive& archive, unsigned int version) { + archive & index; + } +private: + std::multimap index; +}; + +class GeneratedTransactionsIndex { +public: + GeneratedTransactionsIndex(); + + bool add(const Block& block); + bool remove(const Block& block); + bool find(uint32_t height, uint64_t& generatedTransactions); + void clear(); + + void serialize(ISerializer& s); + + template + void serialize(Archive& archive, unsigned int version) { + archive & index; + archive & lastGeneratedTxNumber; + } +private: + std::unordered_map index; + uint64_t lastGeneratedTxNumber; +}; + +class OrphanBlocksIndex { +public: + OrphanBlocksIndex() = default; + + bool add(const Block& block); + bool remove(const Block& block); + bool find(uint32_t height, std::vector& blockHashes); + void clear(); +private: + std::unordered_multimap index; +}; + +} diff --git a/src/CryptoNoteCore/BlockchainMessages.cpp b/src/CryptoNoteCore/BlockchainMessages.cpp new file mode 100644 index 00000000..7b23cd38 --- /dev/null +++ b/src/CryptoNoteCore/BlockchainMessages.cpp @@ -0,0 +1,109 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "CryptoNoteCore/BlockchainMessages.h" + +namespace CryptoNote { + +NewBlockMessage::NewBlockMessage(const Crypto::Hash& hash) : blockHash(hash) {} + +void NewBlockMessage::get(Crypto::Hash& hash) const { + hash = blockHash; +} + +NewAlternativeBlockMessage::NewAlternativeBlockMessage(const Crypto::Hash& hash) : blockHash(hash) {} + +void NewAlternativeBlockMessage::get(Crypto::Hash& hash) const { + hash = blockHash; +} + +ChainSwitchMessage::ChainSwitchMessage(std::vector&& hashes) : blocksFromCommonRoot(std::move(hashes)) {} + +ChainSwitchMessage::ChainSwitchMessage(const ChainSwitchMessage& other) : blocksFromCommonRoot(other.blocksFromCommonRoot) {} + +void ChainSwitchMessage::get(std::vector& hashes) const { + hashes = blocksFromCommonRoot; +} + +BlockchainMessage::BlockchainMessage(NewBlockMessage&& message) : type(MessageType::NEW_BLOCK_MESSAGE), newBlockMessage(std::move(message)) {} + +BlockchainMessage::BlockchainMessage(NewAlternativeBlockMessage&& message) : type(MessageType::NEW_ALTERNATIVE_BLOCK_MESSAGE), newAlternativeBlockMessage(std::move(message)) {} + +BlockchainMessage::BlockchainMessage(ChainSwitchMessage&& message) : type(MessageType::CHAIN_SWITCH_MESSAGE) { + chainSwitchMessage = new ChainSwitchMessage(std::move(message)); +} + +BlockchainMessage::BlockchainMessage(const BlockchainMessage& other) : type(other.type) { + switch (type) { + case MessageType::NEW_BLOCK_MESSAGE: + new (&newBlockMessage) NewBlockMessage(other.newBlockMessage); + break; + case MessageType::NEW_ALTERNATIVE_BLOCK_MESSAGE: + new (&newAlternativeBlockMessage) NewAlternativeBlockMessage(other.newAlternativeBlockMessage); + break; + case MessageType::CHAIN_SWITCH_MESSAGE: + chainSwitchMessage = new ChainSwitchMessage(*other.chainSwitchMessage); + break; + } +} + +BlockchainMessage::~BlockchainMessage() { + switch (type) { + case MessageType::NEW_BLOCK_MESSAGE: + newBlockMessage.~NewBlockMessage(); + break; + case MessageType::NEW_ALTERNATIVE_BLOCK_MESSAGE: + newAlternativeBlockMessage.~NewAlternativeBlockMessage(); + break; + case MessageType::CHAIN_SWITCH_MESSAGE: + delete chainSwitchMessage; + break; + } +} + +BlockchainMessage::MessageType BlockchainMessage::getType() const { + return type; +} + +bool BlockchainMessage::getNewBlockHash(Crypto::Hash& hash) const { + if (type == MessageType::NEW_BLOCK_MESSAGE) { + newBlockMessage.get(hash); + return true; + } else { + return false; + } +} + +bool BlockchainMessage::getNewAlternativeBlockHash(Crypto::Hash& hash) const { + if (type == MessageType::NEW_ALTERNATIVE_BLOCK_MESSAGE) { + newAlternativeBlockMessage.get(hash); + return true; + } else { + return false; + } +} + +bool BlockchainMessage::getChainSwitch(std::vector& hashes) const { + if (type == MessageType::CHAIN_SWITCH_MESSAGE) { + chainSwitchMessage->get(hashes); + return true; + } else { + return false; + } +} + +} diff --git a/src/CryptoNoteCore/BlockchainMessages.h b/src/CryptoNoteCore/BlockchainMessages.h new file mode 100644 index 00000000..544f9df4 --- /dev/null +++ b/src/CryptoNoteCore/BlockchainMessages.h @@ -0,0 +1,84 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +#include + +namespace CryptoNote { + +class NewBlockMessage { +public: + NewBlockMessage(const Crypto::Hash& hash); + NewBlockMessage() = default; + void get(Crypto::Hash& hash) const; +private: + Crypto::Hash blockHash; +}; + +class NewAlternativeBlockMessage { +public: + NewAlternativeBlockMessage(const Crypto::Hash& hash); + NewAlternativeBlockMessage() = default; + void get(Crypto::Hash& hash) const; +private: + Crypto::Hash blockHash; +}; + +class ChainSwitchMessage { +public: + ChainSwitchMessage(std::vector&& hashes); + ChainSwitchMessage(const ChainSwitchMessage& other); + void get(std::vector& hashes) const; +private: + std::vector blocksFromCommonRoot; +}; + +class BlockchainMessage { +public: + enum class MessageType { + NEW_BLOCK_MESSAGE, + NEW_ALTERNATIVE_BLOCK_MESSAGE, + CHAIN_SWITCH_MESSAGE + }; + + BlockchainMessage(NewBlockMessage&& message); + BlockchainMessage(NewAlternativeBlockMessage&& message); + BlockchainMessage(ChainSwitchMessage&& message); + + BlockchainMessage(const BlockchainMessage& other); + + ~BlockchainMessage(); + + MessageType getType() const; + + bool getNewBlockHash(Crypto::Hash& hash) const; + bool getNewAlternativeBlockHash(Crypto::Hash& hash) const; + bool getChainSwitch(std::vector& hashes) const; +private: + const MessageType type; + + union { + NewBlockMessage newBlockMessage; + NewAlternativeBlockMessage newAlternativeBlockMessage; + ChainSwitchMessage* chainSwitchMessage; + }; +}; + +} diff --git a/src/cryptonote_core/checkpoints.cpp b/src/CryptoNoteCore/Checkpoints.cpp similarity index 81% rename from src/cryptonote_core/checkpoints.cpp rename to src/CryptoNoteCore/Checkpoints.cpp index 4b48bbb4..e58da863 100644 --- a/src/cryptonote_core/checkpoints.cpp +++ b/src/CryptoNoteCore/Checkpoints.cpp @@ -15,17 +15,17 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "checkpoints.h" +#include "Checkpoints.h" #include "Common/StringTools.h" using namespace Logging; namespace CryptoNote { //--------------------------------------------------------------------------- -checkpoints::checkpoints(Logging::ILogger &log) : logger(log, "checkpoints") {} +Checkpoints::Checkpoints(Logging::ILogger &log) : logger(log, "checkpoints") {} //--------------------------------------------------------------------------- -bool checkpoints::add_checkpoint(uint64_t height, const std::string &hash_str) { - crypto::hash h = null_hash; +bool Checkpoints::add_checkpoint(uint32_t height, const std::string &hash_str) { + Crypto::Hash h = NULL_HASH; if (!Common::podFromHex(hash_str, h)) { logger(ERROR) << "WRONG HASH IN CHECKPOINTS!!!"; @@ -41,11 +41,11 @@ bool checkpoints::add_checkpoint(uint64_t height, const std::string &hash_str) { return true; } //--------------------------------------------------------------------------- -bool checkpoints::is_in_checkpoint_zone(uint64_t height) const { +bool Checkpoints::is_in_checkpoint_zone(uint32_t height) const { return !m_points.empty() && (height <= (--m_points.end())->first); } //--------------------------------------------------------------------------- -bool checkpoints::check_block(uint64_t height, const crypto::hash &h, +bool Checkpoints::check_block(uint32_t height, const Crypto::Hash &h, bool &is_a_checkpoint) const { auto it = m_points.find(height); is_a_checkpoint = it != m_points.end(); @@ -64,13 +64,13 @@ bool checkpoints::check_block(uint64_t height, const crypto::hash &h, } } //--------------------------------------------------------------------------- -bool checkpoints::check_block(uint64_t height, const crypto::hash &h) const { +bool Checkpoints::check_block(uint32_t height, const Crypto::Hash &h) const { bool ignored; return check_block(height, h, ignored); } //--------------------------------------------------------------------------- -bool checkpoints::is_alternative_block_allowed(uint64_t blockchain_height, - uint64_t block_height) const { +bool Checkpoints::is_alternative_block_allowed(uint32_t blockchain_height, + uint32_t block_height) const { if (0 == block_height) return false; @@ -80,7 +80,7 @@ bool checkpoints::is_alternative_block_allowed(uint64_t blockchain_height, return true; --it; - uint64_t checkpoint_height = it->first; + uint32_t checkpoint_height = it->first; return checkpoint_height < block_height; } } diff --git a/src/cryptonote_core/checkpoints.h b/src/CryptoNoteCore/Checkpoints.h old mode 100644 new mode 100755 similarity index 66% rename from src/cryptonote_core/checkpoints.h rename to src/CryptoNoteCore/Checkpoints.h index 0778f1a2..0d1f3c4f --- a/src/cryptonote_core/checkpoints.h +++ b/src/CryptoNoteCore/Checkpoints.h @@ -17,24 +17,24 @@ #pragma once #include -#include "cryptonote_basic_impl.h" +#include "CryptoNoteBasicImpl.h" #include namespace CryptoNote { - class checkpoints + class Checkpoints { public: - checkpoints(Logging::ILogger& log); + Checkpoints(Logging::ILogger& log); - bool add_checkpoint(uint64_t height, const std::string& hash_str); - bool is_in_checkpoint_zone(uint64_t height) const; - bool check_block(uint64_t height, const crypto::hash& h) const; - bool check_block(uint64_t height, const crypto::hash& h, bool& is_a_checkpoint) const; - bool is_alternative_block_allowed(uint64_t blockchain_height, uint64_t block_height) const; + bool add_checkpoint(uint32_t height, const std::string& hash_str); + bool is_in_checkpoint_zone(uint32_t height) const; + bool check_block(uint32_t height, const Crypto::Hash& h) const; + bool check_block(uint32_t height, const Crypto::Hash& h, bool& is_a_checkpoint) const; + bool is_alternative_block_allowed(uint32_t blockchain_height, uint32_t block_height) const; private: - std::map m_points; + std::map m_points; Logging::LoggerRef logger; }; } diff --git a/src/CryptoNoteCore/Core.cpp b/src/CryptoNoteCore/Core.cpp new file mode 100755 index 00000000..3e51e7cc --- /dev/null +++ b/src/CryptoNoteCore/Core.cpp @@ -0,0 +1,1031 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Core.h" + +#include +#include +#include "../CryptoNoteConfig.h" +#include "../Common/CommandLine.h" +#include "../Common/Util.h" +#include "../Common/StringTools.h" +#include "../crypto/crypto.h" +#include "../CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h" +#include "../Logging/LoggerRef.h" +#include "../Rpc/CoreRpcServerCommandsDefinitions.h" +#include "CryptoNoteFormatUtils.h" +#include "CryptoNoteTools.h" +#include "CryptoNoteStatInfo.h" +#include "Miner.h" +#include "TransactionExtra.h" +#include "IBlock.h" +#undef ERROR + +using namespace Logging; +#include "CryptoNoteCore/CoreConfig.h" + +using namespace Common; + +namespace CryptoNote { + +class BlockWithTransactions : public IBlock { +public: + virtual const Block& getBlock() const override { + return block; + } + + virtual size_t getTransactionCount() const override { + return transactions.size(); + } + + virtual const Transaction& getTransaction(size_t index) const override { + assert(index < transactions.size()); + return transactions[index]; + } + +private: + Block block; + std::vector transactions; + + friend class core; +}; + +core::core(const Currency& currency, i_cryptonote_protocol* pprotocol, Logging::ILogger& logger) : +m_currency(currency), +logger(logger, "core"), +m_mempool(currency, m_blockchain, m_timeProvider, logger), +m_blockchain(currency, m_mempool, logger), +m_miner(new miner(currency, *this, logger)), +m_starter_message_showed(false) { + set_cryptonote_protocol(pprotocol); + m_blockchain.addObserver(this); + m_mempool.addObserver(this); + } + //----------------------------------------------------------------------------------------------- + core::~core() { + m_blockchain.removeObserver(this); +} + +void core::set_cryptonote_protocol(i_cryptonote_protocol* pprotocol) { + if (pprotocol) + m_pprotocol = pprotocol; + else + m_pprotocol = &m_protocol_stub; +} +//----------------------------------------------------------------------------------- +void core::set_checkpoints(Checkpoints&& chk_pts) { + m_blockchain.setCheckpoints(std::move(chk_pts)); +} +//----------------------------------------------------------------------------------- +void core::init_options(boost::program_options::options_description& /*desc*/) { +} + +bool core::handle_command_line(const boost::program_options::variables_map& vm) { + m_config_folder = command_line::get_arg(vm, command_line::arg_data_dir); + return true; +} + +bool core::is_ready() { + return !m_blockchain.isStoringBlockchain(); +} + + +uint32_t core::get_current_blockchain_height() { + return m_blockchain.getCurrentBlockchainHeight(); +} + +void core::get_blockchain_top(uint32_t& height, Crypto::Hash& top_id) { + assert(m_blockchain.getCurrentBlockchainHeight() > 0); + top_id = m_blockchain.getTailId(height); +} + +bool core::get_blocks(uint32_t start_offset, uint32_t count, std::list& blocks, std::list& txs) { + return m_blockchain.getBlocks(start_offset, count, blocks, txs); +} + +bool core::get_blocks(uint32_t start_offset, uint32_t count, std::list& blocks) { + return m_blockchain.getBlocks(start_offset, count, blocks); +} +void core::getTransactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs, bool checkTxPool) { + m_blockchain.getTransactions(txs_ids, txs, missed_txs, checkTxPool); +} + +bool core::get_alternative_blocks(std::list& blocks) { + return m_blockchain.getAlternativeBlocks(blocks); +} + +size_t core::get_alternative_blocks_count() { + return m_blockchain.getAlternativeBlocksCount(); + } + //----------------------------------------------------------------------------------------------- + bool core::init(const CoreConfig& config, const MinerConfig& minerConfig, bool load_existing) { + m_config_folder = config.configFolder; + bool r = m_mempool.init(m_config_folder); + if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize memory pool"; return false; } + + r = m_blockchain.init(m_config_folder, load_existing); + if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize blockchain storage"; return false; } + + r = m_miner->init(minerConfig); + if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize blockchain storage"; return false; } + + return load_state_data(); +} + +bool core::set_genesis_block(const Block& b) { + return m_blockchain.resetAndSetGenesisBlock(b); +} + +bool core::load_state_data() { + // may be some code later + return true; +} + +bool core::deinit() { + m_miner->stop(); + m_mempool.deinit(); + m_blockchain.deinit(); + return true; +} + +size_t core::addChain(const std::vector& chain) { + size_t blocksCounter = 0; + + for (const IBlock* block : chain) { + bool allTransactionsAdded = true; + for (size_t txNumber = 0; txNumber < block->getTransactionCount(); ++txNumber) { + const Transaction& tx = block->getTransaction(txNumber); + + Crypto::Hash txHash = NULL_HASH; + size_t blobSize = 0; + getObjectHash(tx, txHash, blobSize); + tx_verification_context tvc = boost::value_initialized(); + + if (!handleIncomingTransaction(tx, txHash, blobSize, tvc, true)) { + logger(ERROR, BRIGHT_RED) << "core::addChain() failed to handle transaction " << txHash << " from block " << blocksCounter << "/" << chain.size(); + allTransactionsAdded = false; + break; + } + } + + if (!allTransactionsAdded) { + break; + } + + block_verification_context bvc = boost::value_initialized(); + m_blockchain.addNewBlock(block->getBlock(), bvc); + if (bvc.m_marked_as_orphaned || bvc.m_verifivation_failed) { + logger(ERROR, BRIGHT_RED) << "core::addChain() failed to handle incoming block " << get_block_hash(block->getBlock()) << + ", " << blocksCounter << "/" << chain.size(); + break; + } + + ++blocksCounter; + // TODO m_dispatcher.yield()? + } + + return blocksCounter; +} + +bool core::handle_incoming_tx(const BinaryArray& tx_blob, tx_verification_context& tvc, bool keeped_by_block) { //Deprecated. Should be removed with CryptoNoteProtocolHandler. + tvc = boost::value_initialized(); + //want to process all transactions sequentially + + if (tx_blob.size() > m_currency.maxTxSize()) { + logger(INFO) << "WRONG TRANSACTION BLOB, too big size " << tx_blob.size() << ", rejected"; + tvc.m_verifivation_failed = true; + return false; + } + + Crypto::Hash tx_hash = NULL_HASH; + Crypto::Hash tx_prefixt_hash = NULL_HASH; + Transaction tx; + + if (!parse_tx_from_blob(tx, tx_hash, tx_prefixt_hash, tx_blob)) { + logger(INFO) << "WRONG TRANSACTION BLOB, Failed to parse, rejected"; + tvc.m_verifivation_failed = true; + return false; + } + //std::cout << "!"<< tx.inputs.size() << std::endl; + + return handleIncomingTransaction(tx, tx_hash, tx_blob.size(), tvc, keeped_by_block); +} + +bool core::get_stat_info(core_stat_info& st_inf) { + st_inf.mining_speed = m_miner->get_speed(); + st_inf.alternative_blocks = m_blockchain.getAlternativeBlocksCount(); + st_inf.blockchain_height = m_blockchain.getCurrentBlockchainHeight(); + st_inf.tx_pool_size = m_mempool.get_transactions_count(); + st_inf.top_block_id_str = Common::podToHex(m_blockchain.getTailId()); + return true; +} + + +bool core::check_tx_semantic(const Transaction& tx, bool keeped_by_block) { + if (!tx.inputs.size()) { + logger(ERROR) << "tx with empty inputs, rejected for tx id= " << getObjectHash(tx); + return false; + } + + if (!check_inputs_types_supported(tx)) { + logger(ERROR) << "unsupported input types for tx id= " << getObjectHash(tx); + return false; + } + + std::string errmsg; + if (!check_outs_valid(tx, &errmsg)) { + logger(ERROR) << "tx with invalid outputs, rejected for tx id= " << getObjectHash(tx) << ": " << errmsg; + return false; + } + + if (!check_money_overflow(tx)) { + logger(ERROR) << "tx have money overflow, rejected for tx id= " << getObjectHash(tx); + return false; + } + + uint64_t amount_in = 0; + get_inputs_money_amount(tx, amount_in); + uint64_t amount_out = get_outs_money_amount(tx); + + if (amount_in <= amount_out) { + logger(ERROR) << "tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << getObjectHash(tx); + return false; + } + + //check if tx use different key images + if (!check_tx_inputs_keyimages_diff(tx)) { + logger(ERROR) << "tx has a few inputs with identical keyimages"; + return false; + } + + if (!checkMultisignatureInputsDiff(tx)) { + logger(ERROR) << "tx has a few multisignature inputs with identical output indexes"; + return false; + } + + return true; +} + +bool core::check_tx_inputs_keyimages_diff(const Transaction& tx) { + std::unordered_set ki; + for (const auto& in : tx.inputs) { + if (in.type() == typeid(KeyInput)) { + if (!ki.insert(boost::get(in).keyImage).second) + return false; + } + } + return true; +} + +size_t core::get_blockchain_total_transactions() { + return m_blockchain.getTotalTransactions(); +} + +//bool core::get_outs(uint64_t amount, std::list& pkeys) +//{ +// return m_blockchain.get_outs(amount, pkeys); +//} + +bool core::add_new_tx(const Transaction& tx, const Crypto::Hash& tx_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block) { + //Locking on m_mempool and m_blockchain closes possibility to add tx to memory pool which is already in blockchain + std::lock_guard lk(m_mempool); + LockedBlockchainStorage lbs(m_blockchain); + + if (m_blockchain.haveTransaction(tx_hash)) { + logger(TRACE) << "tx " << tx_hash << " is already in blockchain"; + return true; + } + + if (m_mempool.have_tx(tx_hash)) { + logger(TRACE) << "tx " << tx_hash << " is already in transaction pool"; + return true; + } + + return m_mempool.add_tx(tx, tx_hash, blob_size, tvc, keeped_by_block); +} + +bool core::get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint32_t& height, const BinaryArray& ex_nonce) { + size_t median_size; + uint64_t already_generated_coins; + + { + LockedBlockchainStorage blockchainLock(m_blockchain); + height = m_blockchain.getCurrentBlockchainHeight(); + diffic = m_blockchain.getDifficultyForNextBlock(); + if (!(diffic)) { + logger(ERROR, BRIGHT_RED) << "difficulty overhead."; + return false; + } + + b = boost::value_initialized(); + b.majorVersion = m_blockchain.getBlockMajorVersionForHeight(height); + + if (BLOCK_MAJOR_VERSION_1 == b.majorVersion) { + b.minorVersion = BLOCK_MINOR_VERSION_1; + } else if (BLOCK_MAJOR_VERSION_2 == b.majorVersion) { + b.minorVersion = BLOCK_MINOR_VERSION_0; + + b.parentBlock.majorVersion = BLOCK_MAJOR_VERSION_1; + b.parentBlock.majorVersion = BLOCK_MINOR_VERSION_0; + b.parentBlock.transactionCount = 1; + TransactionExtraMergeMiningTag mm_tag = boost::value_initialized(); + + if (!appendMergeMiningTagToExtra(b.parentBlock.baseTransaction.extra, mm_tag)) { + logger(ERROR, BRIGHT_RED) << "Failed to append merge mining tag to extra of the parent block miner transaction"; + return false; + } + } + + b.previousBlockHash = get_tail_id(); + b.timestamp = time(NULL); + + median_size = m_blockchain.getCurrentCumulativeBlocksizeLimit() / 2; + already_generated_coins = m_blockchain.getCoinsInCirculation(); + } + + size_t txs_size; + uint64_t fee; + if (!m_mempool.fill_block_template(b, median_size, m_currency.maxBlockCumulativeSize(height), already_generated_coins, + txs_size, fee)) { + return false; + } + + /* + two-phase miner transaction generation: we don't know exact block size until we prepare block, but we don't know reward until we know + block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size + */ + //make blocks coin-base tx looks close to real coinbase tx to get truthful blob size + bool penalizeFee = b.majorVersion > BLOCK_MAJOR_VERSION_1; + bool r = m_currency.constructMinerTx(height, median_size, already_generated_coins, txs_size, fee, adr, b.baseTransaction, ex_nonce, 11, penalizeFee); + if (!r) { + logger(ERROR, BRIGHT_RED) << "Failed to construct miner tx, first chance"; + return false; + } + + size_t cumulative_size = txs_size + getObjectBinarySize(b.baseTransaction); + for (size_t try_count = 0; try_count != 10; ++try_count) { + r = m_currency.constructMinerTx(height, median_size, already_generated_coins, cumulative_size, fee, adr, b.baseTransaction, ex_nonce, 11, penalizeFee); + + if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to construct miner tx, second chance"; return false; } + size_t coinbase_blob_size = getObjectBinarySize(b.baseTransaction); + if (coinbase_blob_size > cumulative_size - txs_size) { + cumulative_size = txs_size + coinbase_blob_size; + continue; + } + + if (coinbase_blob_size < cumulative_size - txs_size) { + size_t delta = cumulative_size - txs_size - coinbase_blob_size; + b.baseTransaction.extra.insert(b.baseTransaction.extra.end(), delta, 0); + //here could be 1 byte difference, because of extra field counter is varint, and it can become from 1-byte len to 2-bytes len. + if (cumulative_size != txs_size + getObjectBinarySize(b.baseTransaction)) { + if (!(cumulative_size + 1 == txs_size + getObjectBinarySize(b.baseTransaction))) { logger(ERROR, BRIGHT_RED) << "unexpected case: cumulative_size=" << cumulative_size << " + 1 is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.baseTransaction)=" << getObjectBinarySize(b.baseTransaction); return false; } + b.baseTransaction.extra.resize(b.baseTransaction.extra.size() - 1); + if (cumulative_size != txs_size + getObjectBinarySize(b.baseTransaction)) { + //fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size + logger(TRACE, BRIGHT_RED) << + "Miner tx creation have no luck with delta_extra size = " << delta << " and " << delta - 1; + cumulative_size += delta - 1; + continue; + } + logger(DEBUGGING, BRIGHT_GREEN) << + "Setting extra for block: " << b.baseTransaction.extra.size() << ", try_count=" << try_count; + } + } + if (!(cumulative_size == txs_size + getObjectBinarySize(b.baseTransaction))) { logger(ERROR, BRIGHT_RED) << "unexpected case: cumulative_size=" << cumulative_size << " is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.baseTransaction)=" << getObjectBinarySize(b.baseTransaction); return false; } + return true; + } + + logger(ERROR, BRIGHT_RED) << + "Failed to create_block_template with " << 10 << " tries"; + return false; +} + +std::vector core::findBlockchainSupplement(const std::vector& remoteBlockIds, size_t maxCount, + uint32_t& totalBlockCount, uint32_t& startBlockIndex) { + + assert(!remoteBlockIds.empty()); + assert(remoteBlockIds.back() == m_blockchain.getBlockIdByHeight(0)); + + return m_blockchain.findBlockchainSupplement(remoteBlockIds, maxCount, totalBlockCount, startBlockIndex); +} + +void core::print_blockchain(uint32_t start_index, uint32_t end_index) { + m_blockchain.print_blockchain(start_index, end_index); +} + +void core::print_blockchain_index() { + m_blockchain.print_blockchain_index(); +} + +void core::print_blockchain_outs(const std::string& file) { + m_blockchain.print_blockchain_outs(file); +} + +bool core::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) { + return m_blockchain.getRandomOutsByAmount(req, res); +} + +bool core::get_tx_outputs_gindexs(const Crypto::Hash& tx_id, std::vector& indexs) { + return m_blockchain.getTransactionOutputGlobalIndexes(tx_id, indexs); +} + +bool core::getOutByMSigGIndex(uint64_t amount, uint64_t gindex, MultisignatureOutput& out) { + return m_blockchain.get_out_by_msig_gindex(amount, gindex, out); +} + +void core::pause_mining() { + m_miner->pause(); +} + +void core::update_block_template_and_resume_mining() { + update_miner_block_template(); + m_miner->resume(); +} + +bool core::handle_block_found(Block& b) { + block_verification_context bvc = boost::value_initialized(); + handle_incoming_block(b, bvc, true, true); + + if (bvc.m_verifivation_failed) { + logger(ERROR) << "mined block failed verification"; + } + + return bvc.m_added_to_main_chain; +} + +void core::on_synchronized() { + m_miner->on_synchronized(); +} + +bool core::getPoolChanges(const Crypto::Hash& tailBlockId, const std::vector& knownTxsIds, + std::vector& addedTxs, std::vector& deletedTxsIds) { + if (tailBlockId != m_blockchain.getTailId()) { + return false; + } + + getPoolChanges(knownTxsIds, addedTxs, deletedTxsIds); + return true; +} + +bool core::getPoolChangesLite(const Crypto::Hash& tailBlockId, const std::vector& knownTxsIds, + std::vector& addedTxs, std::vector& deletedTxsIds) { + std::vector added; + if (!getPoolChanges(tailBlockId, knownTxsIds, added, deletedTxsIds)) { + return false; + } + + for (const auto& tx: added) { + TransactionPrefixInfo tpi; + tpi.txPrefix = tx; + tpi.txHash = getObjectHash(tx); + + addedTxs.push_back(std::move(tpi)); + } + + return true; +} + +void core::getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, + std::vector& deletedTxsIds) { + std::vector addedTxsIds; + m_mempool.get_difference(knownTxsIds, addedTxsIds, deletedTxsIds); + std::vector misses; + m_mempool.getTransactions(addedTxsIds, addedTxs, misses); + assert(misses.empty()); +} + +bool core::handle_incoming_block_blob(const BinaryArray& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block) { + if (block_blob.size() > m_currency.maxBlockBlobSize()) { + logger(INFO) << "WRONG BLOCK BLOB, too big size " << block_blob.size() << ", rejected"; + bvc.m_verifivation_failed = true; + return false; + } + + Block b; + if (!fromBinaryArray(b, block_blob)) { + logger(INFO) << "Failed to parse and validate new block"; + bvc.m_verifivation_failed = true; + return false; + } + + return handle_incoming_block(b, bvc, control_miner, relay_block); +} + +bool core::handle_incoming_block(const Block& b, block_verification_context& bvc, bool control_miner, bool relay_block) { + if (control_miner) { + pause_mining(); + } + + m_blockchain.addNewBlock(b, bvc); + + if (control_miner) { + update_block_template_and_resume_mining(); + } + + if (relay_block && bvc.m_added_to_main_chain) { + std::list missed_txs; + std::list txs; + m_blockchain.getTransactions(b.transactionHashes, txs, missed_txs); + if (!missed_txs.empty() && getBlockIdByHeight(get_block_height(b)) != get_block_hash(b)) { + logger(INFO) << "Block added, but it seems that reorganize just happened after that, do not relay this block"; + } else { + if (!(txs.size() == b.transactionHashes.size() && missed_txs.empty())) { + logger(ERROR, BRIGHT_RED) << "can't find some transactions in found block:" << + get_block_hash(b) << " txs.size()=" << txs.size() << ", b.transactionHashes.size()=" << b.transactionHashes.size() << ", missed_txs.size()" << missed_txs.size(); return false; + } + + NOTIFY_NEW_BLOCK::request arg; + arg.hop = 0; + arg.current_blockchain_height = m_blockchain.getCurrentBlockchainHeight(); + BinaryArray blockBa; + bool r = toBinaryArray(b, blockBa); + if (!(r)) { logger(ERROR, BRIGHT_RED) << "failed to serialize block"; return false; } + arg.b.block = asString(blockBa); + for (auto& tx : txs) { + arg.b.txs.push_back(asString(toBinaryArray(tx))); + } + + m_pprotocol->relay_block(arg); + } + } + + return true; +} + +Crypto::Hash core::get_tail_id() { + return m_blockchain.getTailId(); +} + +size_t core::get_pool_transactions_count() { + return m_mempool.get_transactions_count(); +} + +bool core::have_block(const Crypto::Hash& id) { + return m_blockchain.haveBlock(id); +} + +bool core::parse_tx_from_blob(Transaction& tx, Crypto::Hash& tx_hash, Crypto::Hash& tx_prefix_hash, const BinaryArray& blob) { + return parseAndValidateTransactionFromBinaryArray(blob, tx, tx_hash, tx_prefix_hash); +} + +bool core::check_tx_syntax(const Transaction& tx) { + return true; +} + +std::vector core::getPoolTransactions() { + std::list txs; + m_mempool.get_transactions(txs); + + std::vector result; + for (auto& tx : txs) { + result.emplace_back(std::move(tx)); + } + return result; +} + +std::vector core::buildSparseChain() { + assert(m_blockchain.getCurrentBlockchainHeight() != 0); + return m_blockchain.buildSparseChain(); +} + +std::vector core::buildSparseChain(const Crypto::Hash& startBlockId) { + LockedBlockchainStorage lbs(m_blockchain); + assert(m_blockchain.haveBlock(startBlockId)); + return m_blockchain.buildSparseChain(startBlockId); +} + +bool core::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) { //Deprecated. Should be removed with CryptoNoteProtocolHandler. + return m_blockchain.handleGetObjects(arg, rsp); +} + +Crypto::Hash core::getBlockIdByHeight(uint32_t height) { + LockedBlockchainStorage lbs(m_blockchain); + if (height < m_blockchain.getCurrentBlockchainHeight()) { + return m_blockchain.getBlockIdByHeight(height); + } else { + return NULL_HASH; + } +} + +bool core::getBlockByHash(const Crypto::Hash &h, Block &blk) { + return m_blockchain.getBlockByHash(h, blk); +} + +bool core::getBlockHeight(const Crypto::Hash& blockId, uint32_t& blockHeight) { + return m_blockchain.getBlockHeight(blockId, blockHeight); +} + +//void core::get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid) { +// m_blockchain.get_all_known_block_ids(main, alt, invalid); +//} + +std::string core::print_pool(bool short_format) { + return m_mempool.print_pool(short_format); +} + +bool core::update_miner_block_template() { + m_miner->on_block_chain_update(); + return true; +} + +bool core::on_idle() { + if (!m_starter_message_showed) { + logger(INFO) << ENDL << "**********************************************************************" << ENDL + << "The daemon will start synchronizing with the network. It may take up to several hours." << ENDL + << ENDL + << "You can set the level of process detailization* through \"set_log \" command*, where is between 0 (no details) and 4 (very verbose)." << ENDL + << ENDL + << "Use \"help\" command to see the list of available commands." << ENDL + << ENDL + << "Note: in case you need to interrupt the process, use \"exit\" command. Otherwise, the current progress won't be saved." << ENDL + << "**********************************************************************"; + m_starter_message_showed = true; + } + + m_miner->on_idle(); + m_mempool.on_idle(); + return true; +} + +bool core::addObserver(ICoreObserver* observer) { + return m_observerManager.add(observer); +} + +bool core::removeObserver(ICoreObserver* observer) { + return m_observerManager.remove(observer); +} + +void core::blockchainUpdated() { + m_observerManager.notify(&ICoreObserver::blockchainUpdated); +} + +void core::txDeletedFromPool() { + poolUpdated(); +} + +void core::poolUpdated() { + m_observerManager.notify(&ICoreObserver::poolUpdated); +} + +bool core::queryBlocks(const std::vector& knownBlockIds, uint64_t timestamp, + uint32_t& resStartHeight, uint32_t& resCurrentHeight, uint32_t& resFullOffset, std::vector& entries) { + + LockedBlockchainStorage lbs(m_blockchain); + + uint32_t currentHeight = lbs->getCurrentBlockchainHeight(); + uint32_t startOffset = 0; + uint32_t startFullOffset = 0; + + if (!findStartAndFullOffsets(knownBlockIds, timestamp, startOffset, startFullOffset)) { + return false; + } + + resFullOffset = startFullOffset; + std::vector blockIds = findIdsForShortBlocks(startOffset, startFullOffset); + entries.reserve(blockIds.size()); + + for (const auto& id : blockIds) { + entries.push_back(BlockFullInfo()); + entries.back().block_id = id; + } + + resCurrentHeight = currentHeight; + resStartHeight = startOffset; + + uint32_t blocksLeft = static_cast(std::min(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT - entries.size(), size_t(BLOCKS_SYNCHRONIZING_DEFAULT_COUNT))); + + if (blocksLeft == 0) { + return true; + } + + std::list blocks; + lbs->getBlocks(startFullOffset, blocksLeft, blocks); + + for (auto& b : blocks) { + BlockFullInfo item; + + item.block_id = get_block_hash(b); + + if (b.timestamp >= timestamp) { + // query transactions + std::list txs; + std::list missedTxs; + lbs->getTransactions(b.transactionHashes, txs, missedTxs); + + // fill data + block_complete_entry& completeEntry = item; + completeEntry.block = asString(toBinaryArray(b)); + for (auto& tx : txs) { + completeEntry.txs.push_back(asString(toBinaryArray(tx))); + } + } + + entries.push_back(std::move(item)); + } + + return true; +} + +bool core::findStartAndFullOffsets(const std::vector& knownBlockIds, uint64_t timestamp, uint32_t& startOffset, uint32_t& startFullOffset) { + LockedBlockchainStorage lbs(m_blockchain); + + if (knownBlockIds.empty()) { + logger(ERROR, BRIGHT_RED) << "knownBlockIds is empty"; + return false; + } + + if (knownBlockIds.back() != m_blockchain.getBlockIdByHeight(0)) { + logger(ERROR, BRIGHT_RED) << "knownBlockIds doesn't end with genesis block hash: " << knownBlockIds.back(); + return false; + } + + startOffset = lbs->findBlockchainSupplement(knownBlockIds); + if (!lbs->getLowerBound(timestamp, startOffset, startFullOffset)) { + startFullOffset = startOffset; + } + + return true; +} + +std::vector core::findIdsForShortBlocks(uint32_t startOffset, uint32_t startFullOffset) { + assert(startOffset <= startFullOffset); + + LockedBlockchainStorage lbs(m_blockchain); + + std::vector result; + if (startOffset < startFullOffset) { + result = lbs->getBlockIds(startOffset, std::min(static_cast(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT), startFullOffset - startOffset)); + } + + return result; +} + +bool core::queryBlocksLite(const std::vector& knownBlockIds, uint64_t timestamp, uint32_t& resStartHeight, + uint32_t& resCurrentHeight, uint32_t& resFullOffset, std::vector& entries) { + LockedBlockchainStorage lbs(m_blockchain); + + resCurrentHeight = lbs->getCurrentBlockchainHeight(); + resStartHeight = 0; + resFullOffset = 0; + + if (!findStartAndFullOffsets(knownBlockIds, timestamp, resStartHeight, resFullOffset)) { + return false; + } + + std::vector blockIds = findIdsForShortBlocks(resStartHeight, resFullOffset); + entries.reserve(blockIds.size()); + + for (const auto& id : blockIds) { + entries.push_back(BlockShortInfo()); + entries.back().blockId = id; + } + + uint32_t blocksLeft = static_cast(std::min(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT - entries.size(), size_t(BLOCKS_SYNCHRONIZING_DEFAULT_COUNT))); + + if (blocksLeft == 0) { + return true; + } + + std::list blocks; + lbs->getBlocks(resFullOffset, blocksLeft, blocks); + + for (auto& b : blocks) { + BlockShortInfo item; + + item.blockId = get_block_hash(b); + + if (b.timestamp >= timestamp) { + std::list txs; + std::list missedTxs; + lbs->getTransactions(b.transactionHashes, txs, missedTxs); + + item.block = asString(toBinaryArray(b)); + + for (const auto& tx: txs) { + TransactionPrefixInfo info; + info.txPrefix = tx; + info.txHash = getObjectHash(tx); + + item.txPrefixes.push_back(std::move(info)); + } + } + + entries.push_back(std::move(item)); + } + + return true; +} + +bool core::getBackwardBlocksSizes(uint32_t fromHeight, std::vector& sizes, size_t count) { + return m_blockchain.getBackwardBlocksSize(fromHeight, sizes, count); +} + +bool core::getBlockSize(const Crypto::Hash& hash, size_t& size) { + return m_blockchain.getBlockSize(hash, size); +} + +bool core::getAlreadyGeneratedCoins(const Crypto::Hash& hash, uint64_t& generatedCoins) { + return m_blockchain.getAlreadyGeneratedCoins(hash, generatedCoins); +} + +bool core::getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, + bool penalizeFee, uint64_t& reward, int64_t& emissionChange) { + return m_currency.getBlockReward(medianSize, currentBlockSize, alreadyGeneratedCoins, fee, penalizeFee, reward, emissionChange); +} + +bool core::scanOutputkeysForIndices(const KeyInput& txInToKey, std::list>& outputReferences) { + struct outputs_visitor + { + std::list>& m_resultsCollector; + outputs_visitor(std::list>& resultsCollector):m_resultsCollector(resultsCollector){} + bool handle_output(const Transaction& tx, const TransactionOutput& out, size_t transactionOutputIndex) + { + m_resultsCollector.push_back(std::make_pair(getObjectHash(tx), transactionOutputIndex)); + return true; + } + }; + + outputs_visitor vi(outputReferences); + + return m_blockchain.scanOutputKeysForIndexes(txInToKey, vi); +} + +bool core::getBlockDifficulty(uint32_t height, difficulty_type& difficulty) { + difficulty = m_blockchain.blockDifficulty(height); + return true; +} + +bool core::getBlockContainingTx(const Crypto::Hash& txId, Crypto::Hash& blockId, uint32_t& blockHeight) { + return m_blockchain.getBlockContainingTransaction(txId, blockId, blockHeight); +} + +bool core::getMultisigOutputReference(const MultisignatureInput& txInMultisig, std::pair& outputReference) { + return m_blockchain.getMultisigOutputReference(txInMultisig, outputReference); +} + +bool core::getGeneratedTransactionsNumber(uint32_t height, uint64_t& generatedTransactions) { + return m_blockchain.getGeneratedTransactionsNumber(height, generatedTransactions); +} + +bool core::getOrphanBlocksByHeight(uint32_t height, std::vector& blocks) { + std::vector blockHashes; + if (!m_blockchain.getOrphanBlockIdsByHeight(height, blockHashes)) { + return false; + } + for (const Crypto::Hash& hash : blockHashes) { + Block blk; + if (!getBlockByHash(hash, blk)) { + return false; + } + blocks.push_back(std::move(blk)); + } + return true; +} + +bool core::getBlocksByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps) { + std::vector blockHashes; + if (!m_blockchain.getBlockIdsByTimestamp(timestampBegin, timestampEnd, blocksNumberLimit, blockHashes, blocksNumberWithinTimestamps)) { + return false; + } + for (const Crypto::Hash& hash : blockHashes) { + Block blk; + if (!getBlockByHash(hash, blk)) { + return false; + } + blocks.push_back(std::move(blk)); + } + return true; +} + +bool core::getPoolTransactionsByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps) { + std::vector poolTransactionHashes; + if (!m_mempool.getTransactionIdsByTimestamp(timestampBegin, timestampEnd, transactionsNumberLimit, poolTransactionHashes, transactionsNumberWithinTimestamps)) { + return false; + } + std::list txs; + std::list missed_txs; + + getTransactions(poolTransactionHashes, txs, missed_txs, true); + if (missed_txs.size() > 0) { + return false; + } + + transactions.insert(transactions.end(), txs.begin(), txs.end()); + return true; +} + +bool core::getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions) { + std::vector blockchainTransactionHashes; + if (!m_blockchain.getTransactionIdsByPaymentId(paymentId, blockchainTransactionHashes)) { + return false; + } + std::vector poolTransactionHashes; + if (!m_mempool.getTransactionIdsByPaymentId(paymentId, poolTransactionHashes)) { + return false; + } + std::list txs; + std::list missed_txs; + blockchainTransactionHashes.insert(blockchainTransactionHashes.end(), poolTransactionHashes.begin(), poolTransactionHashes.end()); + + getTransactions(blockchainTransactionHashes, txs, missed_txs, true); + if (missed_txs.size() > 0) { + return false; + } + + transactions.insert(transactions.end(), txs.begin(), txs.end()); + return true; +} + +std::error_code core::executeLocked(const std::function& func) { + std::lock_guard lk(m_mempool); + LockedBlockchainStorage lbs(m_blockchain); + + return func(); +} + +uint64_t core::getNextBlockDifficulty() { + return m_blockchain.getDifficultyForNextBlock(); +} + +uint64_t core::getTotalGeneratedAmount() { + return m_blockchain.getCoinsInCirculation(); +} + +bool core::handleIncomingTransaction(const Transaction& tx, const Crypto::Hash& txHash, size_t blobSize, tx_verification_context& tvc, bool keptByBlock) { + if (!check_tx_syntax(tx)) { + logger(INFO) << "WRONG TRANSACTION BLOB, Failed to check tx " << txHash << " syntax, rejected"; + tvc.m_verifivation_failed = true; + return false; + } + + if (!check_tx_semantic(tx, keptByBlock)) { + logger(INFO) << "WRONG TRANSACTION BLOB, Failed to check tx " << txHash << " semantic, rejected"; + tvc.m_verifivation_failed = true; + return false; + } + + bool r = add_new_tx(tx, txHash, blobSize, tvc, keptByBlock); + if (tvc.m_verifivation_failed) { + if (!tvc.m_tx_fee_too_small) { + logger(ERROR) << "Transaction verification failed: " << txHash; + } else { + logger(INFO) << "Transaction verification failed: " << txHash; + } + } else if (tvc.m_verifivation_impossible) { + logger(ERROR) << "Transaction verification impossible: " << txHash; + } + + if (tvc.m_added_to_pool) { + logger(DEBUGGING) << "tx added: " << txHash; + poolUpdated(); + } + + return r; +} + +std::unique_ptr core::getBlock(const Crypto::Hash& blockId) { + std::lock_guard lk(m_mempool); + LockedBlockchainStorage lbs(m_blockchain); + + std::unique_ptr blockPtr(new BlockWithTransactions()); + if (!lbs->getBlockByHash(blockId, blockPtr->block)) { + logger(DEBUGGING) << "Can't find block: " << blockId; + return std::unique_ptr(nullptr); + } + + blockPtr->transactions.reserve(blockPtr->block.transactionHashes.size()); + std::vector missedTxs; + lbs->getTransactions(blockPtr->block.transactionHashes, blockPtr->transactions, missedTxs, true); + assert(missedTxs.empty() || !lbs->isBlockInMainChain(blockId)); //if can't find transaction for blockchain block -> error + + if (!missedTxs.empty()) { + logger(DEBUGGING) << "Can't find transactions for block: " << blockId; + return std::unique_ptr(nullptr); + } + + return std::move(blockPtr); +} + +bool core::addMessageQueue(MessageQueue& messageQueue) { + return m_blockchain.addMessageQueue(messageQueue); +} + +bool core::removeMessageQueue(MessageQueue& messageQueue) { + return m_blockchain.removeMessageQueue(messageQueue); +} + +} diff --git a/src/CryptoNoteCore/Core.h b/src/CryptoNoteCore/Core.h new file mode 100755 index 00000000..d880fd9b --- /dev/null +++ b/src/CryptoNoteCore/Core.h @@ -0,0 +1,194 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +#include "P2p/NetNodeCommon.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolHandlerCommon.h" +#include "Currency.h" +#include "TransactionPool.h" +#include "Blockchain.h" +#include "CryptoNoteCore/IMinerHandler.h" +#include "CryptoNoteCore/MinerConfig.h" +#include "ICore.h" +#include "ICoreObserver.h" +#include "Common/ObserverManager.h" + +#include "System/Dispatcher.h" +#include "CryptoNoteCore/MessageQueue.h" +#include "CryptoNoteCore/BlockchainMessages.h" + +#include + +namespace CryptoNote { + + struct core_stat_info; + class miner; + class CoreConfig; + + class core : public ICore, public IMinerHandler, public IBlockchainStorageObserver, public ITxPoolObserver { + public: + core(const Currency& currency, i_cryptonote_protocol* pprotocol, Logging::ILogger& logger); + ~core(); + + bool on_idle(); + virtual bool handle_incoming_tx(const BinaryArray& tx_blob, tx_verification_context& tvc, bool keeped_by_block); //Deprecated. Should be removed with CryptoNoteProtocolHandler. + bool handle_incoming_block_blob(const BinaryArray& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block); + virtual i_cryptonote_protocol* get_protocol(){return m_pprotocol;} + const Currency& currency() const { return m_currency; } + + //-------------------- IMinerHandler ----------------------- + virtual bool handle_block_found(Block& b); + virtual bool get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint32_t& height, const BinaryArray& ex_nonce); + + bool addObserver(ICoreObserver* observer); + bool removeObserver(ICoreObserver* observer); + + miner& get_miner() { return *m_miner; } + static void init_options(boost::program_options::options_description& desc); + bool init(const CoreConfig& config, const MinerConfig& minerConfig, bool load_existing); + bool set_genesis_block(const Block& b); + bool deinit(); + + // ICore + virtual size_t addChain(const std::vector& chain) override; + virtual bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp) override; //Deprecated. Should be removed with CryptoNoteProtocolHandler. + virtual bool getBackwardBlocksSizes(uint32_t fromHeight, std::vector& sizes, size_t count) override; + virtual bool getBlockSize(const Crypto::Hash& hash, size_t& size) override; + virtual bool getAlreadyGeneratedCoins(const Crypto::Hash& hash, uint64_t& generatedCoins) override; + virtual bool getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, + bool penalizeFee, uint64_t& reward, int64_t& emissionChange) override; + virtual bool scanOutputkeysForIndices(const KeyInput& txInToKey, std::list>& outputReferences) override; + virtual bool getBlockDifficulty(uint32_t height, difficulty_type& difficulty) override; + virtual bool getBlockContainingTx(const Crypto::Hash& txId, Crypto::Hash& blockId, uint32_t& blockHeight) override; + virtual bool getMultisigOutputReference(const MultisignatureInput& txInMultisig, std::pair& output_reference) override; + virtual bool getGeneratedTransactionsNumber(uint32_t height, uint64_t& generatedTransactions) override; + virtual bool getOrphanBlocksByHeight(uint32_t height, std::vector& blocks) override; + virtual bool getBlocksByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps) override; + virtual bool getPoolTransactionsByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps) override; + virtual bool getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions) override; + virtual bool getOutByMSigGIndex(uint64_t amount, uint64_t gindex, MultisignatureOutput& out) override; + virtual std::unique_ptr getBlock(const Crypto::Hash& blocksId) override; + virtual bool handleIncomingTransaction(const Transaction& tx, const Crypto::Hash& txHash, size_t blobSize, tx_verification_context& tvc, bool keptByBlock) override; + virtual std::error_code executeLocked(const std::function& func) override; + + virtual bool addMessageQueue(MessageQueue& messageQueue) override; + virtual bool removeMessageQueue(MessageQueue& messageQueue) override; + + uint32_t get_current_blockchain_height(); + bool have_block(const Crypto::Hash& id); + std::vector buildSparseChain() override; + std::vector buildSparseChain(const Crypto::Hash& startBlockId) override; + void on_synchronized(); + bool is_ready() override; + + virtual void get_blockchain_top(uint32_t& height, Crypto::Hash& top_id); + bool get_blocks(uint32_t start_offset, uint32_t count, std::list& blocks, std::list& txs); + bool get_blocks(uint32_t start_offset, uint32_t count, std::list& blocks); + template + bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) + { + return m_blockchain.getBlocks(block_ids, blocks, missed_bs); + } + virtual bool queryBlocks(const std::vector& block_ids, uint64_t timestamp, + uint32_t& start_height, uint32_t& current_height, uint32_t& full_offset, std::vector& entries) override; + virtual bool queryBlocksLite(const std::vector& knownBlockIds, uint64_t timestamp, + uint32_t& resStartHeight, uint32_t& resCurrentHeight, uint32_t& resFullOffset, std::vector& entries) override; + virtual Crypto::Hash getBlockIdByHeight(uint32_t height) override; + void getTransactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs, bool checkTxPool = false) override; + virtual bool getBlockByHash(const Crypto::Hash &h, Block &blk) override; + virtual bool getBlockHeight(const Crypto::Hash& blockId, uint32_t& blockHeight) override; + //void get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid); + + bool get_alternative_blocks(std::list& blocks); + size_t get_alternative_blocks_count(); + + void set_cryptonote_protocol(i_cryptonote_protocol* pprotocol); + void set_checkpoints(Checkpoints&& chk_pts); + + std::vector getPoolTransactions() override; + size_t get_pool_transactions_count(); + size_t get_blockchain_total_transactions(); + //bool get_outs(uint64_t amount, std::list& pkeys); + virtual std::vector findBlockchainSupplement(const std::vector& remoteBlockIds, size_t maxCount, + uint32_t& totalBlockCount, uint32_t& startBlockIndex) override; + bool get_stat_info(core_stat_info& st_inf); + + virtual bool get_tx_outputs_gindexs(const Crypto::Hash& tx_id, std::vector& indexs); + Crypto::Hash get_tail_id(); + virtual bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res); + void pause_mining(); + void update_block_template_and_resume_mining(); + //Blockchain& get_blockchain_storage(){return m_blockchain;} + //debug functions + void print_blockchain(uint32_t start_index, uint32_t end_index); + void print_blockchain_index(); + std::string print_pool(bool short_format); + void print_blockchain_outs(const std::string& file); + virtual bool getPoolChanges(const Crypto::Hash& tailBlockId, const std::vector& knownTxsIds, + std::vector& addedTxs, std::vector& deletedTxsIds) override; + virtual bool getPoolChangesLite(const Crypto::Hash& tailBlockId, const std::vector& knownTxsIds, + std::vector& addedTxs, std::vector& deletedTxsIds) override; + virtual void getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, + std::vector& deletedTxsIds) override; + + uint64_t getNextBlockDifficulty(); + uint64_t getTotalGeneratedAmount(); + + private: + bool add_new_tx(const Transaction& tx, const Crypto::Hash& tx_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block); + bool load_state_data(); + bool parse_tx_from_blob(Transaction& tx, Crypto::Hash& tx_hash, Crypto::Hash& tx_prefix_hash, const BinaryArray& blob); + bool handle_incoming_block(const Block& b, block_verification_context& bvc, bool control_miner, bool relay_block); + + bool check_tx_syntax(const Transaction& tx); + //check correct values, amounts and all lightweight checks not related with database + bool check_tx_semantic(const Transaction& tx, bool keeped_by_block); + //check if tx already in memory pool or in main blockchain + + bool is_key_image_spent(const Crypto::KeyImage& key_im); + + bool check_tx_ring_signature(const KeyInput& tx, const Crypto::Hash& tx_prefix_hash, const std::vector& sig); + bool is_tx_spendtime_unlocked(uint64_t unlock_time); + bool update_miner_block_template(); + bool handle_command_line(const boost::program_options::variables_map& vm); + bool on_update_blocktemplate_interval(); + bool check_tx_inputs_keyimages_diff(const Transaction& tx); + virtual void blockchainUpdated() override; + virtual void txDeletedFromPool() override; + void poolUpdated(); + + bool findStartAndFullOffsets(const std::vector& knownBlockIds, uint64_t timestamp, uint32_t& startOffset, uint32_t& startFullOffset); + std::vector findIdsForShortBlocks(uint32_t startOffset, uint32_t startFullOffset); + + const Currency& m_currency; + Logging::LoggerRef logger; + CryptoNote::RealTimeProvider m_timeProvider; + tx_memory_pool m_mempool; + Blockchain m_blockchain; + i_cryptonote_protocol* m_pprotocol; + std::unique_ptr m_miner; + std::string m_config_folder; + cryptonote_protocol_stub m_protocol_stub; + friend class tx_validate_inputs; + std::atomic m_starter_message_showed; + Tools::ObserverManager m_observerManager; + }; +} diff --git a/src/cryptonote_core/CoreConfig.cpp b/src/CryptoNoteCore/CoreConfig.cpp old mode 100644 new mode 100755 similarity index 91% rename from src/cryptonote_core/CoreConfig.cpp rename to src/CryptoNoteCore/CoreConfig.cpp index b4deb2fb..9bcf5239 --- a/src/cryptonote_core/CoreConfig.cpp +++ b/src/CryptoNoteCore/CoreConfig.cpp @@ -17,13 +17,13 @@ #include "CoreConfig.h" -#include "Common/util.h" -#include "Common/command_line.h" +#include "Common/Util.h" +#include "Common/CommandLine.h" namespace CryptoNote { CoreConfig::CoreConfig() { - configFolder = tools::get_default_data_dir(); + configFolder = Tools::getDefaultDataDirectory(); } void CoreConfig::init(const boost::program_options::variables_map& options) { diff --git a/src/cryptonote_core/CoreConfig.h b/src/CryptoNoteCore/CoreConfig.h similarity index 100% rename from src/cryptonote_core/CoreConfig.h rename to src/CryptoNoteCore/CoreConfig.h diff --git a/src/payment_service/WalletServiceErrorCodes.cpp b/src/CryptoNoteCore/CryptoNoteBasic.cpp old mode 100644 new mode 100755 similarity index 79% rename from src/payment_service/WalletServiceErrorCodes.cpp rename to src/CryptoNoteCore/CryptoNoteBasic.cpp index 61ef02f1..6a56862c --- a/src/payment_service/WalletServiceErrorCodes.cpp +++ b/src/CryptoNoteCore/CryptoNoteBasic.cpp @@ -15,12 +15,15 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "WalletServiceErrorCodes.h" +#include "CryptoNoteBasic.h" +#include "crypto/crypto.h" -namespace PaymentService { -namespace error { +namespace CryptoNote { -WalletServiceErrorCategory WalletServiceErrorCategory::INSTANCE; +KeyPair generateKeyPair() { + KeyPair k; + Crypto::generate_keys(k.publicKey, k.secretKey); + return k; +} -} //namespace error -} //namespace PaymentService +} diff --git a/src/CryptoNoteCore/CryptoNoteBasic.h b/src/CryptoNoteCore/CryptoNoteBasic.h new file mode 100755 index 00000000..2230a309 --- /dev/null +++ b/src/CryptoNoteCore/CryptoNoteBasic.h @@ -0,0 +1,46 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +namespace CryptoNote { + const Crypto::Hash NULL_HASH = boost::value_initialized(); + const Crypto::PublicKey NULL_PUBLIC_KEY = boost::value_initialized(); + + KeyPair generateKeyPair(); + + struct ParentBlockSerializer { + ParentBlockSerializer(ParentBlock& parentBlock, uint64_t& timestamp, uint32_t& nonce, bool hashingSerialization, bool headerOnly) : + m_parentBlock(parentBlock), m_timestamp(timestamp), m_nonce(nonce), m_hashingSerialization(hashingSerialization), m_headerOnly(headerOnly) { + } + + ParentBlock& m_parentBlock; + uint64_t& m_timestamp; + uint32_t& m_nonce; + bool m_hashingSerialization; + bool m_headerOnly; + }; + + inline ParentBlockSerializer makeParentBlockSerializer(const Block& b, bool hashingSerialization, bool headerOnly) { + Block& blockRef = const_cast(b); + return ParentBlockSerializer(blockRef.parentBlock, blockRef.timestamp, blockRef.nonce, hashingSerialization, headerOnly); + } + +} diff --git a/src/cryptonote_core/cryptonote_basic_impl.cpp b/src/CryptoNoteCore/CryptoNoteBasicImpl.cpp similarity index 76% rename from src/cryptonote_core/cryptonote_basic_impl.cpp rename to src/CryptoNoteCore/CryptoNoteBasicImpl.cpp index 567333a5..8192b26d 100644 --- a/src/cryptonote_core/cryptonote_basic_impl.cpp +++ b/src/CryptoNoteCore/CryptoNoteBasicImpl.cpp @@ -15,18 +15,22 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "cryptonote_basic_impl.h" -#include "serialization/binary_utils.h" -#include "serialization/vector.h" -#include "cryptonote_format_utils.h" -#include "Common/base58.h" +#include "CryptoNoteBasicImpl.h" +#include "CryptoNoteFormatUtils.h" +#include "CryptoNoteTools.h" +#include "CryptoNoteSerialization.h" + +#include "Common/Base58.h" #include "crypto/hash.h" #include "Common/int-util.h" +using namespace Crypto; +using namespace Common; + namespace CryptoNote { /************************************************************************/ - /* Cryptonote helper functions */ + /* CryptoNote helper functions */ /************************************************************************/ //----------------------------------------------------------------------------------------------- uint64_t getPenalizedAmount(uint64_t amount, size_t medianSize, size_t currentBlockSize) { @@ -58,18 +62,18 @@ namespace CryptoNote { } //----------------------------------------------------------------------- std::string getAccountAddressAsStr(uint64_t prefix, const AccountPublicAddress& adr) { - blobdata blob; - bool r = t_serializable_object_to_blob(adr, blob); + BinaryArray ba; + bool r = toBinaryArray(adr, ba); assert(r); - return tools::base58::encode_addr(prefix, blob); + return Tools::Base58::encode_addr(prefix, Common::asString(ba)); } //----------------------------------------------------------------------- bool is_coinbase(const Transaction& tx) { - if(tx.vin.size() != 1) { + if(tx.inputs.size() != 1) { return false; } - if(tx.vin[0].type() != typeid(TransactionInputGenerate)) { + if(tx.inputs[0].type() != typeid(BaseInput)) { return false; } @@ -77,17 +81,18 @@ namespace CryptoNote { } //----------------------------------------------------------------------- bool parseAccountAddressString(uint64_t& prefix, AccountPublicAddress& adr, const std::string& str) { - blobdata data; + std::string data; return - tools::base58::decode_addr(str, prefix, data) && - ::serialization::parse_binary(data, adr) && - crypto::check_key(adr.m_spendPublicKey) && - crypto::check_key(adr.m_viewPublicKey); + Tools::Base58::decode_addr(str, prefix, data) && + fromBinaryArray(adr, asBinaryArray(data)) && + // ::serialization::parse_binary(data, adr) && + check_key(adr.spendPublicKey) && + check_key(adr.viewPublicKey); } //----------------------------------------------------------------------- bool operator ==(const CryptoNote::Transaction& a, const CryptoNote::Transaction& b) { - return CryptoNote::get_transaction_hash(a) == CryptoNote::get_transaction_hash(b); + return getObjectHash(a) == getObjectHash(b); } //----------------------------------------------------------------------- bool operator ==(const CryptoNote::Block& a, const CryptoNote::Block& b) { @@ -96,10 +101,6 @@ namespace CryptoNote { } //-------------------------------------------------------------------------------- -bool parse_hash256(const std::string& str_hash, crypto::hash& hash) { - if (!Common::podFromHex(str_hash, hash)) { - std::cout << "invalid hash format: <" << str_hash << '>' << std::endl; - return false; - } - return true; +bool parse_hash256(const std::string& str_hash, Crypto::Hash& hash) { + return Common::podFromHex(str_hash, hash); } diff --git a/src/cryptonote_core/cryptonote_basic_impl.h b/src/CryptoNoteCore/CryptoNoteBasicImpl.h old mode 100644 new mode 100755 similarity index 68% rename from src/cryptonote_core/cryptonote_basic_impl.h rename to src/CryptoNoteCore/CryptoNoteBasicImpl.h index ed7b9369..3dce9262 --- a/src/cryptonote_core/cryptonote_basic_impl.h +++ b/src/CryptoNoteCore/CryptoNoteBasicImpl.h @@ -20,7 +20,7 @@ #include "Common/StringTools.h" #include "crypto/crypto.h" #include "crypto/hash.h" -#include "cryptonote_core/cryptonote_basic.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" namespace CryptoNote { @@ -28,16 +28,16 @@ namespace CryptoNote { /* */ /************************************************************************/ template - struct array_hasher: std::unary_function + struct array_hasher: std::unary_function { - std::size_t operator()(const t_array& val) const + size_t operator()(const t_array& val) const { return boost::hash_range(&val.data[0], &val.data[sizeof(val.data)]); } }; /************************************************************************/ - /* Cryptonote helper functions */ + /* CryptoNote helper functions */ /************************************************************************/ uint64_t getPenalizedAmount(uint64_t amount, size_t medianSize, size_t currentBlockSize); std::string getAccountAddressAsStr(uint64_t prefix, const AccountPublicAddress& adr); @@ -53,13 +53,13 @@ std::ostream &print256(std::ostream &o, const T &v) { return o << "<" << Common::podToHex(v) << ">"; } -bool parse_hash256(const std::string& str_hash, crypto::hash& hash); +bool parse_hash256(const std::string& str_hash, Crypto::Hash& hash); -namespace crypto { - inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { return print256(o, v); } +namespace Crypto { + inline std::ostream &operator <<(std::ostream &o, const Crypto::PublicKey &v) { return print256(o, v); } + inline std::ostream &operator <<(std::ostream &o, const Crypto::SecretKey &v) { return print256(o, v); } + inline std::ostream &operator <<(std::ostream &o, const Crypto::KeyDerivation &v) { return print256(o, v); } + inline std::ostream &operator <<(std::ostream &o, const Crypto::KeyImage &v) { return print256(o, v); } + inline std::ostream &operator <<(std::ostream &o, const Crypto::Signature &v) { return print256(o, v); } + inline std::ostream &operator <<(std::ostream &o, const Crypto::Hash &v) { return print256(o, v); } } diff --git a/src/CryptoNoteCore/CryptoNoteFormatUtils.cpp b/src/CryptoNoteCore/CryptoNoteFormatUtils.cpp new file mode 100644 index 00000000..b60c6a96 --- /dev/null +++ b/src/CryptoNoteCore/CryptoNoteFormatUtils.cpp @@ -0,0 +1,550 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "CryptoNoteFormatUtils.h" + +#include +#include +#include + +#include "Serialization/BinaryOutputStreamSerializer.h" +#include "Serialization/BinaryInputStreamSerializer.h" + +#include "Account.h" +#include "CryptoNoteBasicImpl.h" +#include "CryptoNoteSerialization.h" +#include "TransactionExtra.h" +#include "CryptoNoteTools.h" + +#include "CryptoNoteConfig.h" + +using namespace Logging; +using namespace Crypto; +using namespace Common; + +namespace CryptoNote { + +bool parseAndValidateTransactionFromBinaryArray(const BinaryArray& tx_blob, Transaction& tx, Hash& tx_hash, Hash& tx_prefix_hash) { + if (!fromBinaryArray(tx, tx_blob)) { + return false; + } + + //TODO: validate tx + cn_fast_hash(tx_blob.data(), tx_blob.size(), tx_hash); + getObjectHash(*static_cast(&tx), tx_prefix_hash); + return true; +} + +bool generate_key_image_helper(const AccountKeys& ack, const PublicKey& tx_public_key, size_t real_output_index, KeyPair& in_ephemeral, KeyImage& ki) { + KeyDerivation recv_derivation; + bool r = generate_key_derivation(tx_public_key, ack.viewSecretKey, recv_derivation); + + assert(r && "key image helper: failed to generate_key_derivation"); + + if (!r) { + return false; + } + + r = derive_public_key(recv_derivation, real_output_index, ack.address.spendPublicKey, in_ephemeral.publicKey); + + assert(r && "key image helper: failed to derive_public_key"); + + if (!r) { + return false; + } + + derive_secret_key(recv_derivation, real_output_index, ack.spendSecretKey, in_ephemeral.secretKey); + generate_key_image(in_ephemeral.publicKey, in_ephemeral.secretKey, ki); + return true; +} + +uint64_t power_integral(uint64_t a, uint64_t b) { + if (b == 0) + return 1; + uint64_t total = a; + for (uint64_t i = 1; i != b; i++) + total *= a; + return total; +} + +bool get_tx_fee(const Transaction& tx, uint64_t & fee) { + uint64_t amount_in = 0; + uint64_t amount_out = 0; + + for (const auto& in : tx.inputs) { + if (in.type() == typeid(KeyInput)) { + amount_in += boost::get(in).amount; + } else if (in.type() == typeid(MultisignatureInput)) { + amount_in += boost::get(in).amount; + } + } + + for (const auto& o : tx.outputs) { + amount_out += o.amount; + } + + if (!(amount_in >= amount_out)) { + return false; + } + + fee = amount_in - amount_out; + return true; +} + +uint64_t get_tx_fee(const Transaction& tx) { + uint64_t r = 0; + if (!get_tx_fee(tx, r)) + return 0; + return r; +} + + +bool constructTransaction( + const AccountKeys& sender_account_keys, + const std::vector& sources, + const std::vector& destinations, + std::vector extra, + Transaction& tx, + uint64_t unlock_time, + Logging::ILogger& log) { + LoggerRef logger(log, "construct_tx"); + + tx.inputs.clear(); + tx.outputs.clear(); + tx.signatures.clear(); + + tx.version = CURRENT_TRANSACTION_VERSION; + tx.unlockTime = unlock_time; + + tx.extra = extra; + KeyPair txkey = generateKeyPair(); + addTransactionPublicKeyToExtra(tx.extra, txkey.publicKey); + + struct input_generation_context_data { + KeyPair in_ephemeral; + }; + + std::vector in_contexts; + uint64_t summary_inputs_money = 0; + //fill inputs + for (const TransactionSourceEntry& src_entr : sources) { + if (src_entr.realOutput >= src_entr.outputs.size()) { + logger(ERROR) << "real_output index (" << src_entr.realOutput << ")bigger than output_keys.size()=" << src_entr.outputs.size(); + return false; + } + summary_inputs_money += src_entr.amount; + + //KeyDerivation recv_derivation; + in_contexts.push_back(input_generation_context_data()); + KeyPair& in_ephemeral = in_contexts.back().in_ephemeral; + KeyImage img; + if (!generate_key_image_helper(sender_account_keys, src_entr.realTransactionPublicKey, src_entr.realOutputIndexInTransaction, in_ephemeral, img)) + return false; + + //check that derived key is equal with real output key + if (!(in_ephemeral.publicKey == src_entr.outputs[src_entr.realOutput].second)) { + logger(ERROR) << "derived public key mismatch with output public key! " << ENDL << "derived_key:" + << Common::podToHex(in_ephemeral.publicKey) << ENDL << "real output_public_key:" + << Common::podToHex(src_entr.outputs[src_entr.realOutput].second); + return false; + } + + //put key image into tx input + KeyInput input_to_key; + input_to_key.amount = src_entr.amount; + input_to_key.keyImage = img; + + //fill outputs array and use relative offsets + for (const TransactionSourceEntry::OutputEntry& out_entry : src_entr.outputs) { + input_to_key.outputIndexes.push_back(out_entry.first); + } + + input_to_key.outputIndexes = absolute_output_offsets_to_relative(input_to_key.outputIndexes); + tx.inputs.push_back(input_to_key); + } + + // "Shuffle" outs + std::vector shuffled_dsts(destinations); + std::sort(shuffled_dsts.begin(), shuffled_dsts.end(), [](const TransactionDestinationEntry& de1, const TransactionDestinationEntry& de2) { return de1.amount < de2.amount; }); + + uint64_t summary_outs_money = 0; + //fill outputs + size_t output_index = 0; + for (const TransactionDestinationEntry& dst_entr : shuffled_dsts) { + if (!(dst_entr.amount > 0)) { + logger(ERROR, BRIGHT_RED) << "Destination with wrong amount: " << dst_entr.amount; + return false; + } + KeyDerivation derivation; + PublicKey out_eph_public_key; + bool r = generate_key_derivation(dst_entr.addr.viewPublicKey, txkey.secretKey, derivation); + + if (!(r)) { + logger(ERROR, BRIGHT_RED) + << "at creation outs: failed to generate_key_derivation(" + << dst_entr.addr.viewPublicKey << ", " << txkey.secretKey << ")"; + return false; + } + + r = derive_public_key(derivation, output_index, + dst_entr.addr.spendPublicKey, + out_eph_public_key); + if (!(r)) { + logger(ERROR, BRIGHT_RED) + << "at creation outs: failed to derive_public_key(" << derivation + << ", " << output_index << ", " << dst_entr.addr.spendPublicKey + << ")"; + return false; + } + + TransactionOutput out; + out.amount = dst_entr.amount; + KeyOutput tk; + tk.key = out_eph_public_key; + out.target = tk; + tx.outputs.push_back(out); + output_index++; + summary_outs_money += dst_entr.amount; + } + + //check money + if (summary_outs_money > summary_inputs_money) { + logger(ERROR) << "Transaction inputs money (" << summary_inputs_money << ") less than outputs money (" << summary_outs_money << ")"; + return false; + } + + //generate ring signatures + Hash tx_prefix_hash; + getObjectHash(*static_cast(&tx), tx_prefix_hash); + + size_t i = 0; + for (const TransactionSourceEntry& src_entr : sources) { + std::vector keys_ptrs; + for (const TransactionSourceEntry::OutputEntry& o : src_entr.outputs) { + keys_ptrs.push_back(&o.second); + } + + tx.signatures.push_back(std::vector()); + std::vector& sigs = tx.signatures.back(); + sigs.resize(src_entr.outputs.size()); + generate_ring_signature(tx_prefix_hash, boost::get(tx.inputs[i]).keyImage, keys_ptrs, + in_contexts[i].in_ephemeral.secretKey, src_entr.realOutput, sigs.data()); + i++; + } + + return true; +} + +bool get_inputs_money_amount(const Transaction& tx, uint64_t& money) { + money = 0; + + for (const auto& in : tx.inputs) { + uint64_t amount = 0; + + if (in.type() == typeid(KeyInput)) { + amount = boost::get(in).amount; + } else if (in.type() == typeid(MultisignatureInput)) { + amount = boost::get(in).amount; + } + + money += amount; + } + return true; +} + +uint32_t get_block_height(const Block& b) { + if (b.baseTransaction.inputs.size() != 1) { + return 0; + } + const auto& in = b.baseTransaction.inputs[0]; + if (in.type() != typeid(BaseInput)) { + return 0; + } + return boost::get(in).blockIndex; +} + +bool check_inputs_types_supported(const TransactionPrefix& tx) { + for (const auto& in : tx.inputs) { + if (in.type() != typeid(KeyInput) && in.type() != typeid(MultisignatureInput)) { + return false; + } + } + + return true; +} + +bool check_outs_valid(const TransactionPrefix& tx, std::string* error) { + for (const TransactionOutput& out : tx.outputs) { + if (out.target.type() == typeid(KeyOutput)) { + if (out.amount == 0) { + if (error) { + *error = "Zero amount ouput"; + } + return false; + } + + if (!check_key(boost::get(out.target).key)) { + if (error) { + *error = "Output with invalid key"; + } + return false; + } + } else if (out.target.type() == typeid(MultisignatureOutput)) { + const MultisignatureOutput& multisignatureOutput = ::boost::get(out.target); + if (multisignatureOutput.requiredSignatureCount > multisignatureOutput.keys.size()) { + if (error) { + *error = "Multisignature output with invalid required signature count"; + } + return false; + } + for (const PublicKey& key : multisignatureOutput.keys) { + if (!check_key(key)) { + if (error) { + *error = "Multisignature output with invalid public key"; + } + return false; + } + } + } else { + if (error) { + *error = "Output with invalid type"; + } + return false; + } + } + + return true; +} + +bool checkMultisignatureInputsDiff(const TransactionPrefix& tx) { + std::set> inputsUsage; + for (const auto& inv : tx.inputs) { + if (inv.type() == typeid(MultisignatureInput)) { + const MultisignatureInput& in = ::boost::get(inv); + if (!inputsUsage.insert(std::make_pair(in.amount, in.outputIndex)).second) { + return false; + } + } + } + return true; +} + +bool check_money_overflow(const TransactionPrefix &tx) { + return check_inputs_overflow(tx) && check_outs_overflow(tx); +} + +bool check_inputs_overflow(const TransactionPrefix &tx) { + uint64_t money = 0; + + for (const auto &in : tx.inputs) { + uint64_t amount = 0; + + if (in.type() == typeid(KeyInput)) { + amount = boost::get(in).amount; + } else if (in.type() == typeid(MultisignatureInput)) { + amount = boost::get(in).amount; + } + + if (money > amount + money) + return false; + + money += amount; + } + return true; +} + +bool check_outs_overflow(const TransactionPrefix& tx) { + uint64_t money = 0; + for (const auto& o : tx.outputs) { + if (money > o.amount + money) + return false; + money += o.amount; + } + return true; +} + +uint64_t get_outs_money_amount(const Transaction& tx) { + uint64_t outputs_amount = 0; + for (const auto& o : tx.outputs) { + outputs_amount += o.amount; + } + return outputs_amount; +} + +std::string short_hash_str(const Hash& h) { + std::string res = Common::podToHex(h); + + if (res.size() == 64) { + auto erased_pos = res.erase(8, 48); + res.insert(8, "...."); + } + + return res; +} + +bool is_out_to_acc(const AccountKeys& acc, const KeyOutput& out_key, const KeyDerivation& derivation, size_t keyIndex) { + PublicKey pk; + derive_public_key(derivation, keyIndex, acc.address.spendPublicKey, pk); + return pk == out_key.key; +} + +bool is_out_to_acc(const AccountKeys& acc, const KeyOutput& out_key, const PublicKey& tx_pub_key, size_t keyIndex) { + KeyDerivation derivation; + generate_key_derivation(tx_pub_key, acc.viewSecretKey, derivation); + return is_out_to_acc(acc, out_key, derivation, keyIndex); +} + +bool lookup_acc_outs(const AccountKeys& acc, const Transaction& tx, std::vector& outs, uint64_t& money_transfered) { + PublicKey transactionPublicKey = getTransactionPublicKeyFromExtra(tx.extra); + if (transactionPublicKey == NULL_PUBLIC_KEY) + return false; + return lookup_acc_outs(acc, tx, transactionPublicKey, outs, money_transfered); +} + +bool lookup_acc_outs(const AccountKeys& acc, const Transaction& tx, const PublicKey& tx_pub_key, std::vector& outs, uint64_t& money_transfered) { + money_transfered = 0; + size_t keyIndex = 0; + size_t outputIndex = 0; + + KeyDerivation derivation; + generate_key_derivation(tx_pub_key, acc.viewSecretKey, derivation); + + for (const TransactionOutput& o : tx.outputs) { + assert(o.target.type() == typeid(KeyOutput) || o.target.type() == typeid(MultisignatureOutput)); + if (o.target.type() == typeid(KeyOutput)) { + if (is_out_to_acc(acc, boost::get(o.target), derivation, keyIndex)) { + outs.push_back(outputIndex); + money_transfered += o.amount; + } + + ++keyIndex; + } else if (o.target.type() == typeid(MultisignatureOutput)) { + keyIndex += boost::get(o.target).keys.size(); + } + + ++outputIndex; + } + return true; +} + +bool get_block_hashing_blob(const Block& b, BinaryArray& ba) { + if (!toBinaryArray(static_cast(b), ba)) { + return false; + } + + Hash treeRootHash = get_tx_tree_hash(b); + ba.insert(ba.end(), treeRootHash.data, treeRootHash.data + 32); + auto transactionCount = asBinaryArray(Tools::get_varint_data(b.transactionHashes.size() + 1)); + ba.insert(ba.end(), transactionCount.begin(), transactionCount.end()); + return true; +} + +bool get_parent_block_hashing_blob(const Block& b, BinaryArray& blob) { + auto serializer = makeParentBlockSerializer(b, true, true); + return toBinaryArray(serializer, blob); +} + +bool get_block_hash(const Block& b, Hash& res) { + BinaryArray ba; + if (!get_block_hashing_blob(b, ba)) { + return false; + } + + if (BLOCK_MAJOR_VERSION_2 <= b.majorVersion) { + BinaryArray parent_blob; + auto serializer = makeParentBlockSerializer(b, true, false); + if (!toBinaryArray(serializer, parent_blob)) + return false; + + ba.insert(ba.end(), parent_blob.begin(), parent_blob.end()); + } + + return getObjectHash(ba, res); +} + +Hash get_block_hash(const Block& b) { + Hash p = NULL_HASH; + get_block_hash(b, p); + return p; +} + +bool get_aux_block_header_hash(const Block& b, Hash& res) { + BinaryArray blob; + if (!get_block_hashing_blob(b, blob)) { + return false; + } + + return getObjectHash(blob, res); +} + +bool get_block_longhash(cn_context &context, const Block& b, Hash& res) { + BinaryArray bd; + if (b.majorVersion == BLOCK_MAJOR_VERSION_1) { + if (!get_block_hashing_blob(b, bd)) { + return false; + } + } else if (b.majorVersion == BLOCK_MAJOR_VERSION_2) { + if (!get_parent_block_hashing_blob(b, bd)) { + return false; + } + } else { + return false; + } + cn_slow_hash(context, bd.data(), bd.size(), res); + return true; +} + +std::vector relative_output_offsets_to_absolute(const std::vector& off) { + std::vector res = off; + for (size_t i = 1; i < res.size(); i++) + res[i] += res[i - 1]; + return res; +} + +std::vector absolute_output_offsets_to_relative(const std::vector& off) { + std::vector res = off; + if (!off.size()) + return res; + std::sort(res.begin(), res.end());//just to be sure, actually it is already should be sorted + for (size_t i = res.size() - 1; i != 0; i--) + res[i] -= res[i - 1]; + + return res; +} + +void get_tx_tree_hash(const std::vector& tx_hashes, Hash& h) { + tree_hash(tx_hashes.data(), tx_hashes.size(), h); +} + +Hash get_tx_tree_hash(const std::vector& tx_hashes) { + Hash h = NULL_HASH; + get_tx_tree_hash(tx_hashes, h); + return h; +} + +Hash get_tx_tree_hash(const Block& b) { + std::vector txs_ids; + Hash h = NULL_HASH; + getObjectHash(b.baseTransaction, h); + txs_ids.push_back(h); + for (auto& th : b.transactionHashes) { + txs_ids.push_back(th); + } + return get_tx_tree_hash(txs_ids); +} + +} diff --git a/src/CryptoNoteCore/CryptoNoteFormatUtils.h b/src/CryptoNoteCore/CryptoNoteFormatUtils.h new file mode 100755 index 00000000..7809a0d6 --- /dev/null +++ b/src/CryptoNoteCore/CryptoNoteFormatUtils.h @@ -0,0 +1,128 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +#include "CryptoNoteBasic.h" +#include "CryptoNoteSerialization.h" + +#include "Serialization/BinaryOutputStreamSerializer.h" +#include "Serialization/BinaryInputStreamSerializer.h" + +namespace Logging { +class ILogger; +} + +namespace CryptoNote { + +bool parseAndValidateTransactionFromBinaryArray(const BinaryArray& transactionBinaryArray, Transaction& transaction, Crypto::Hash& transactionHash, Crypto::Hash& transactionPrefixHash); + +struct TransactionSourceEntry { + typedef std::pair OutputEntry; + + std::vector outputs; //index + key + size_t realOutput; //index in outputs vector of real output_entry + Crypto::PublicKey realTransactionPublicKey; //incoming real tx public key + size_t realOutputIndexInTransaction; //index in transaction outputs vector + uint64_t amount; //money +}; + +struct TransactionDestinationEntry { + uint64_t amount; //money + AccountPublicAddress addr; //destination address + + TransactionDestinationEntry() : amount(0), addr(boost::value_initialized()) {} + TransactionDestinationEntry(uint64_t amount, const AccountPublicAddress &addr) : amount(amount), addr(addr) {} +}; + + +bool constructTransaction( + const AccountKeys& senderAccountKeys, + const std::vector& sources, + const std::vector& destinations, + std::vector extra, Transaction& transaction, uint64_t unlock_time, Logging::ILogger& log); + + +bool is_out_to_acc(const AccountKeys& acc, const KeyOutput& out_key, const Crypto::PublicKey& tx_pub_key, size_t keyIndex); +bool is_out_to_acc(const AccountKeys& acc, const KeyOutput& out_key, const Crypto::KeyDerivation& derivation, size_t keyIndex); +bool lookup_acc_outs(const AccountKeys& acc, const Transaction& tx, const Crypto::PublicKey& tx_pub_key, std::vector& outs, uint64_t& money_transfered); +bool lookup_acc_outs(const AccountKeys& acc, const Transaction& tx, std::vector& outs, uint64_t& money_transfered); +bool get_tx_fee(const Transaction& tx, uint64_t & fee); +uint64_t get_tx_fee(const Transaction& tx); +bool generate_key_image_helper(const AccountKeys& ack, const Crypto::PublicKey& tx_public_key, size_t real_output_index, KeyPair& in_ephemeral, Crypto::KeyImage& ki); +std::string short_hash_str(const Crypto::Hash& h); + +bool get_block_hashing_blob(const Block& b, BinaryArray& blob); +bool get_parent_block_hashing_blob(const Block& b, BinaryArray& blob); +bool get_aux_block_header_hash(const Block& b, Crypto::Hash& res); +bool get_block_hash(const Block& b, Crypto::Hash& res); +Crypto::Hash get_block_hash(const Block& b); +bool get_block_longhash(Crypto::cn_context &context, const Block& b, Crypto::Hash& res); +bool get_inputs_money_amount(const Transaction& tx, uint64_t& money); +uint64_t get_outs_money_amount(const Transaction& tx); +bool check_inputs_types_supported(const TransactionPrefix& tx); +bool check_outs_valid(const TransactionPrefix& tx, std::string* error = 0); +bool checkMultisignatureInputsDiff(const TransactionPrefix& tx); + +bool check_money_overflow(const TransactionPrefix& tx); +bool check_outs_overflow(const TransactionPrefix& tx); +bool check_inputs_overflow(const TransactionPrefix& tx); +uint32_t get_block_height(const Block& b); +std::vector relative_output_offsets_to_absolute(const std::vector& off); +std::vector absolute_output_offsets_to_relative(const std::vector& off); + + +// 62387455827 -> 455827 + 7000000 + 80000000 + 300000000 + 2000000000 + 60000000000, where 455827 <= dust_threshold +template +void decompose_amount_into_digits(uint64_t amount, uint64_t dust_threshold, const chunk_handler_t& chunk_handler, const dust_handler_t& dust_handler) { + if (0 == amount) { + return; + } + + bool is_dust_handled = false; + uint64_t dust = 0; + uint64_t order = 1; + while (0 != amount) { + uint64_t chunk = (amount % 10) * order; + amount /= 10; + order *= 10; + + if (dust + chunk <= dust_threshold) { + dust += chunk; + } else { + if (!is_dust_handled && 0 != dust) { + dust_handler(dust); + is_dust_handled = true; + } + if (0 != chunk) { + chunk_handler(chunk); + } + } + } + + if (!is_dust_handled && 0 != dust) { + dust_handler(dust); + } +} + +void get_tx_tree_hash(const std::vector& tx_hashes, Crypto::Hash& h); +Crypto::Hash get_tx_tree_hash(const std::vector& tx_hashes); +Crypto::Hash get_tx_tree_hash(const Block& b); + +} diff --git a/src/cryptonote_core/cryptonote_serialization.cpp b/src/CryptoNoteCore/CryptoNoteSerialization.cpp similarity index 55% rename from src/cryptonote_core/cryptonote_serialization.cpp rename to src/CryptoNoteCore/CryptoNoteSerialization.cpp index d3c995f6..cc7e9a43 100644 --- a/src/cryptonote_core/cryptonote_serialization.cpp +++ b/src/CryptoNoteCore/CryptoNoteSerialization.cpp @@ -15,34 +15,52 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "cryptonote_serialization.h" -#include "account.h" +#include "CryptoNoteSerialization.h" -#include "serialization/ISerializer.h" -#include "serialization/SerializationOverloads.h" -#include "serialization/BinaryInputStreamSerializer.h" -#include "serialization/BinaryOutputStreamSerializer.h" -#include "crypto/crypto.h" -#include "cryptonote_config.h" - -#include #include +#include +#include #include #include +#include "Serialization/ISerializer.h" +#include "Serialization/SerializationOverloads.h" +#include "Serialization/BinaryInputStreamSerializer.h" +#include "Serialization/BinaryOutputStreamSerializer.h" + +#include "Common/StringOutputStream.h" +#include "crypto/crypto.h" + +#include "Account.h" +#include "CryptoNoteConfig.h" +#include "CryptoNoteFormatUtils.h" +#include "CryptoNoteTools.h" +#include "TransactionExtra.h" + +using namespace Common; + namespace { +using namespace CryptoNote; +using namespace Common; + +size_t getSignaturesCount(const TransactionInput& input) { + struct txin_signature_size_visitor : public boost::static_visitor < size_t > { + size_t operator()(const BaseInput& txin) const { return 0; } + size_t operator()(const KeyInput& txin) const { return txin.outputIndexes.size(); } + size_t operator()(const MultisignatureInput& txin) const { return txin.signatureCount; } + }; + + return boost::apply_visitor(txin_signature_size_visitor(), input); +} + struct BinaryVariantTagGetter: boost::static_visitor { - uint8_t operator()(const CryptoNote::TransactionInputGenerate) { return 0xff; } - uint8_t operator()(const CryptoNote::TransactionInputToScript) { return 0x0; } - uint8_t operator()(const CryptoNote::TransactionInputToScriptHash) { return 0x1; } - uint8_t operator()(const CryptoNote::TransactionInputToKey) { return 0x2; } - uint8_t operator()(const CryptoNote::TransactionInputMultisignature) { return 0x3; } - uint8_t operator()(const CryptoNote::TransactionOutputToScript) { return 0x0; } - uint8_t operator()(const CryptoNote::TransactionOutputToScriptHash) { return 0x1; } - uint8_t operator()(const CryptoNote::TransactionOutputToKey) { return 0x2; } - uint8_t operator()(const CryptoNote::TransactionOutputMultisignature) { return 0x3; } + uint8_t operator()(const CryptoNote::BaseInput) { return 0xff; } + uint8_t operator()(const CryptoNote::KeyInput) { return 0x2; } + uint8_t operator()(const CryptoNote::MultisignatureInput) { return 0x3; } + uint8_t operator()(const CryptoNote::KeyOutput) { return 0x2; } + uint8_t operator()(const CryptoNote::MultisignatureOutput) { return 0x3; } uint8_t operator()(const CryptoNote::Transaction) { return 0xcc; } uint8_t operator()(const CryptoNote::Block) { return 0xbb; } }; @@ -50,49 +68,30 @@ struct BinaryVariantTagGetter: boost::static_visitor { struct VariantSerializer : boost::static_visitor<> { VariantSerializer(CryptoNote::ISerializer& serializer, const std::string& name) : s(serializer), name(name) {} - void operator() (CryptoNote::TransactionInputGenerate& param) { s(param, name); } - void operator() (CryptoNote::TransactionInputToScript& param) { s(param, name); } - void operator() (CryptoNote::TransactionInputToScriptHash& param) { s(param, name); } - void operator() (CryptoNote::TransactionInputToKey& param) { s(param, name); } - void operator() (CryptoNote::TransactionInputMultisignature& param) { s(param, name); } - void operator() (CryptoNote::TransactionOutputToScript& param) { s(param, name); } - void operator() (CryptoNote::TransactionOutputToScriptHash& param) { s(param, name); } - void operator() (CryptoNote::TransactionOutputToKey& param) { s(param, name); } - void operator() (CryptoNote::TransactionOutputMultisignature& param) { s(param, name); } + template + void operator() (T& param) { s(param, name); } CryptoNote::ISerializer& s; - const std::string& name; + std::string name; }; void getVariantValue(CryptoNote::ISerializer& serializer, uint8_t tag, CryptoNote::TransactionInput& in) { switch(tag) { case 0xff: { - CryptoNote::TransactionInputGenerate v; - serializer(v, "data"); - in = v; - break; - } - case 0x0: { - CryptoNote::TransactionInputToScript v; - serializer(v, "data"); - in = v; - break; - } - case 0x1: { - CryptoNote::TransactionInputToScriptHash v; - serializer(v, "data"); + CryptoNote::BaseInput v; + serializer(v, "value"); in = v; break; } case 0x2: { - CryptoNote::TransactionInputToKey v; - serializer(v, "data"); + CryptoNote::KeyInput v; + serializer(v, "value"); in = v; break; } case 0x3: { - CryptoNote::TransactionInputMultisignature v; - serializer(v, "data"); + CryptoNote::MultisignatureInput v; + serializer(v, "value"); in = v; break; } @@ -103,26 +102,14 @@ void getVariantValue(CryptoNote::ISerializer& serializer, uint8_t tag, CryptoNot void getVariantValue(CryptoNote::ISerializer& serializer, uint8_t tag, CryptoNote::TransactionOutputTarget& out) { switch(tag) { - case 0x0: { - CryptoNote::TransactionOutputToScript v; - serializer(v, "data"); - out = v; - break; - } - case 0x1: { - CryptoNote::TransactionOutputToScriptHash v; - serializer(v, "data"); - out = v; - break; - } case 0x2: { - CryptoNote::TransactionOutputToKey v; + CryptoNote::KeyOutput v; serializer(v, "data"); out = v; break; } case 0x3: { - CryptoNote::TransactionOutputMultisignature v; + CryptoNote::MultisignatureOutput v; serializer(v, "data"); out = v; break; @@ -137,8 +124,8 @@ bool serializePod(T& v, Common::StringView name, CryptoNote::ISerializer& serial return serializer.binary(&v, sizeof(v), name); } -bool serializeVarintVector(std::vector& vector, CryptoNote::ISerializer& serializer, Common::StringView name) { - std::size_t size = vector.size(); +bool serializeVarintVector(std::vector& vector, CryptoNote::ISerializer& serializer, Common::StringView name) { + size_t size = vector.size(); if (!serializer.beginArray(size, name)) { vector.clear(); @@ -157,21 +144,21 @@ bool serializeVarintVector(std::vector& vector, CryptoNote::ISerialize } -namespace crypto { +namespace Crypto { -bool serialize(public_key& pubKey, Common::StringView name, CryptoNote::ISerializer& serializer) { +bool serialize(PublicKey& pubKey, Common::StringView name, CryptoNote::ISerializer& serializer) { return serializePod(pubKey, name, serializer); } -bool serialize(secret_key& secKey, Common::StringView name, CryptoNote::ISerializer& serializer) { +bool serialize(SecretKey& secKey, Common::StringView name, CryptoNote::ISerializer& serializer) { return serializePod(secKey, name, serializer); } -bool serialize(hash& h, Common::StringView name, CryptoNote::ISerializer& serializer) { +bool serialize(Hash& h, Common::StringView name, CryptoNote::ISerializer& serializer) { return serializePod(h, name, serializer); } -bool serialize(key_image& keyImage, Common::StringView name, CryptoNote::ISerializer& serializer) { +bool serialize(KeyImage& keyImage, Common::StringView name, CryptoNote::ISerializer& serializer) { return serializePod(keyImage, name, serializer); } @@ -179,10 +166,17 @@ bool serialize(chacha8_iv& chacha, Common::StringView name, CryptoNote::ISeriali return serializePod(chacha, name, serializer); } -bool serialize(signature& sig, Common::StringView name, CryptoNote::ISerializer& serializer) { +bool serialize(Signature& sig, Common::StringView name, CryptoNote::ISerializer& serializer) { return serializePod(sig, name, serializer); } +bool serialize(EllipticCurveScalar& ecScalar, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializePod(ecScalar, name, serializer); +} + +bool serialize(EllipticCurvePoint& ecPoint, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializePod(ecPoint, name, serializer); +} } @@ -190,31 +184,35 @@ namespace CryptoNote { void serialize(TransactionPrefix& txP, ISerializer& serializer) { serializer(txP.version, "version"); + + if (CURRENT_TRANSACTION_VERSION < txP.version) { + throw std::runtime_error("Wrong transaction version"); + } + serializer(txP.unlockTime, "unlock_time"); - serializer(txP.vin, "vin"); - serializer(txP.vout, "vout"); + serializer(txP.inputs, "vin"); + serializer(txP.outputs, "vout"); serializeAsBinary(txP.extra, "extra", serializer); } void serialize(Transaction& tx, ISerializer& serializer) { - serializer(tx.version, "version"); - serializer(tx.unlockTime, "unlock_time"); - serializer(tx.vin, "vin"); - serializer(tx.vout, "vout"); - serializeAsBinary(tx.extra, "extra", serializer); + serialize(static_cast(tx), serializer); - std::size_t sigSize = tx.vin.size(); + size_t sigSize = tx.inputs.size(); //TODO: make arrays without sizes // serializer.beginArray(sigSize, "signatures"); - tx.signatures.resize(sigSize); + + if (serializer.type() == ISerializer::INPUT) { + tx.signatures.resize(sigSize); + } bool signaturesNotExpected = tx.signatures.empty(); - if (!signaturesNotExpected && tx.vin.size() != tx.signatures.size()) { + if (!signaturesNotExpected && tx.inputs.size() != tx.signatures.size()) { throw std::runtime_error("Serialization error: unexpected signatures size"); } - for (size_t i = 0; i < tx.vin.size(); ++i) { - size_t signatureSize = Transaction::getSignatureSize(tx.vin[i]); + for (size_t i = 0; i < tx.inputs.size(); ++i) { + size_t signatureSize = getSignaturesCount(tx.inputs[i]); if (signaturesNotExpected) { if (signatureSize == 0) { continue; @@ -227,12 +225,18 @@ void serialize(Transaction& tx, ISerializer& serializer) { if (signatureSize != tx.signatures[i].size()) { throw std::runtime_error("Serialization error: unexpected signatures size"); } - } else { - tx.signatures[i].resize(signatureSize); - } - for (crypto::signature& sig: tx.signatures[i]) { - serializePod(sig, "", serializer); + for (Crypto::Signature& sig : tx.signatures[i]) { + serializePod(sig, "", serializer); + } + + } else { + std::vector signatures(signatureSize); + for (Crypto::Signature& sig : signatures) { + serializePod(sig, "", serializer); + } + + tx.signatures[i] = std::move(signatures); } } // serializer.endArray(); @@ -254,22 +258,19 @@ void serialize(TransactionInput& in, ISerializer& serializer) { } } -void serialize(TransactionInputGenerate& gen, ISerializer& serializer) { - serializer(gen.height, "height"); +void serialize(BaseInput& gen, ISerializer& serializer) { + serializer(gen.blockIndex, "height"); } -void serialize(TransactionInputToScript& script, ISerializer& serializer) {} -void serialize(TransactionInputToScriptHash& scripthash, ISerializer& serializer) {} - -void serialize(TransactionInputToKey& key, ISerializer& serializer) { +void serialize(KeyInput& key, ISerializer& serializer) { serializer(key.amount, "amount"); - serializeVarintVector(key.keyOffsets, serializer, "key_offsets"); + serializeVarintVector(key.outputIndexes, serializer, "key_offsets"); serializer(key.keyImage, "k_image"); } -void serialize(TransactionInputMultisignature& multisignature, ISerializer& serializer) { +void serialize(MultisignatureInput& multisignature, ISerializer& serializer) { serializer(multisignature.amount, "amount"); - serializer(multisignature.signatures, "signatures"); + serializer(multisignature.signatureCount, "signatures"); serializer(multisignature.outputIndex, "outputIndex"); } @@ -294,16 +295,13 @@ void serialize(TransactionOutputTarget& output, ISerializer& serializer) { } } -void serialize(TransactionOutputToScript& script, ISerializer& serializer) {} -void serialize(TransactionOutputToScriptHash& scripthash, ISerializer& serializer) {} - -void serialize(TransactionOutputToKey& key, ISerializer& serializer) { +void serialize(KeyOutput& key, ISerializer& serializer) { serializer(key.key, "key"); } -void serialize(TransactionOutputMultisignature& multisignature, ISerializer& serializer) { +void serialize(MultisignatureOutput& multisignature, ISerializer& serializer) { serializer(multisignature.keys, "keys"); - serializer(multisignature.requiredSignatures, "required_signatures"); + serializer(multisignature.requiredSignatureCount, "required_signatures"); } void serialize(ParentBlockSerializer& pbs, ISerializer& serializer) { @@ -315,25 +313,25 @@ void serialize(ParentBlockSerializer& pbs, ISerializer& serializer) { serializer(pbs.m_parentBlock.minorVersion, "minorVersion"); serializer(pbs.m_timestamp, "timestamp"); - serializer(pbs.m_parentBlock.prevId, "prevId"); + serializer(pbs.m_parentBlock.previousBlockHash, "prevId"); serializer.binary(&pbs.m_nonce, sizeof(pbs.m_nonce), "nonce"); if (pbs.m_hashingSerialization) { - crypto::hash minerTxHash; - if (!get_transaction_hash(pbs.m_parentBlock.minerTx, minerTxHash)) { + Crypto::Hash minerTxHash; + if (!getObjectHash(pbs.m_parentBlock.baseTransaction, minerTxHash)) { throw std::runtime_error("Get transaction hash error"); } - crypto::hash merkleRoot; - crypto::tree_hash_from_branch(pbs.m_parentBlock.minerTxBranch.data(), pbs.m_parentBlock.minerTxBranch.size(), minerTxHash, 0, merkleRoot); + Crypto::Hash merkleRoot; + Crypto::tree_hash_from_branch(pbs.m_parentBlock.baseTransactionBranch.data(), pbs.m_parentBlock.baseTransactionBranch.size(), minerTxHash, 0, merkleRoot); serializer(merkleRoot, "merkleRoot"); } - uint64_t txNum = static_cast(pbs.m_parentBlock.numberOfTransactions); + uint64_t txNum = static_cast(pbs.m_parentBlock.transactionCount); serializer(txNum, "numberOfTransactions"); - pbs.m_parentBlock.numberOfTransactions = static_cast(txNum); - if (pbs.m_parentBlock.numberOfTransactions < 1) { + pbs.m_parentBlock.transactionCount = static_cast(txNum); + if (pbs.m_parentBlock.transactionCount < 1) { throw std::runtime_error("Wrong transactions number"); } @@ -341,29 +339,29 @@ void serialize(ParentBlockSerializer& pbs, ISerializer& serializer) { return; } - size_t branchSize = crypto::tree_depth(pbs.m_parentBlock.numberOfTransactions); + size_t branchSize = Crypto::tree_depth(pbs.m_parentBlock.transactionCount); if (serializer.type() == ISerializer::OUTPUT) { - if (pbs.m_parentBlock.minerTxBranch.size() != branchSize) { + if (pbs.m_parentBlock.baseTransactionBranch.size() != branchSize) { throw std::runtime_error("Wrong miner transaction branch size"); } } else { - pbs.m_parentBlock.minerTxBranch.resize(branchSize); + pbs.m_parentBlock.baseTransactionBranch.resize(branchSize); } -// serializer(m_parentBlock.minerTxBranch, "minerTxBranch"); +// serializer(m_parentBlock.baseTransactionBranch, "baseTransactionBranch"); //TODO: Make arrays with computable size! This code won't work with json serialization! - for (crypto::hash& hash: pbs.m_parentBlock.minerTxBranch) { + for (Crypto::Hash& hash: pbs.m_parentBlock.baseTransactionBranch) { serializer(hash, ""); } - serializer(pbs.m_parentBlock.minerTx, "minerTx"); + serializer(pbs.m_parentBlock.baseTransaction, "minerTx"); - tx_extra_merge_mining_tag mmTag; - if (!get_mm_tag_from_extra(pbs.m_parentBlock.minerTx.extra, mmTag)) { + TransactionExtraMergeMiningTag mmTag; + if (!getMergeMiningTagFromExtra(pbs.m_parentBlock.baseTransaction.extra, mmTag)) { throw std::runtime_error("Can't get extra merge mining tag"); } - if (mmTag.depth > 8 * sizeof(crypto::hash)) { + if (mmTag.depth > 8 * sizeof(Crypto::Hash)) { throw std::runtime_error("Wrong merge mining tag depth"); } @@ -377,7 +375,7 @@ void serialize(ParentBlockSerializer& pbs, ISerializer& serializer) { // serializer(m_parentBlock.blockchainBranch, "blockchainBranch"); //TODO: Make arrays with computable size! This code won't work with json serialization! - for (crypto::hash& hash: pbs.m_parentBlock.blockchainBranch) { + for (Crypto::Hash& hash: pbs.m_parentBlock.blockchainBranch) { serializer(hash, ""); } } @@ -391,10 +389,10 @@ void serializeBlockHeader(BlockHeader& header, ISerializer& serializer) { serializer(header.minorVersion, "minor_version"); if (header.majorVersion == BLOCK_MAJOR_VERSION_1) { serializer(header.timestamp, "timestamp"); - serializer(header.prevId, "prev_id"); + serializer(header.previousBlockHash, "prev_id"); serializer.binary(&header.nonce, sizeof(header.nonce), "nonce"); } else if (header.majorVersion == BLOCK_MAJOR_VERSION_2) { - serializer(header.prevId, "prev_id"); + serializer(header.previousBlockHash, "prev_id"); } else { throw std::runtime_error("Wrong major version"); } @@ -412,42 +410,48 @@ void serialize(Block& block, ISerializer& serializer) { serializer(parentBlockSerializer, "parent_block"); } - serializer(block.minerTx, "miner_tx"); - serializer(block.txHashes, "tx_hashes"); + serializer(block.baseTransaction, "miner_tx"); + serializer(block.transactionHashes, "tx_hashes"); } void serialize(AccountPublicAddress& address, ISerializer& serializer) { - serializer(address.m_spendPublicKey, "m_spend_public_key"); - serializer(address.m_viewPublicKey, "m_view_public_key"); + serializer(address.spendPublicKey, "m_spend_public_key"); + serializer(address.viewPublicKey, "m_view_public_key"); } -void serialize(account_keys& keys, ISerializer& s) { - s(keys.m_account_address, "m_account_address"); - s(keys.m_spend_secret_key, "m_spend_secret_key"); - s(keys.m_view_secret_key, "m_view_secret_key"); +void serialize(AccountKeys& keys, ISerializer& s) { + s(keys.address, "m_account_address"); + s(keys.spendSecretKey, "m_spend_secret_key"); + s(keys.viewSecretKey, "m_view_secret_key"); } -void doSerialize(tx_extra_merge_mining_tag& tag, ISerializer& serializer) { +void doSerialize(TransactionExtraMergeMiningTag& tag, ISerializer& serializer) { uint64_t depth = static_cast(tag.depth); serializer(depth, "depth"); tag.depth = static_cast(depth); - serializer(tag.merkle_root, "merkle_root"); + serializer(tag.merkleRoot, "merkle_root"); } -void serialize(tx_extra_merge_mining_tag& tag, ISerializer& serializer) { +void serialize(TransactionExtraMergeMiningTag& tag, ISerializer& serializer) { if (serializer.type() == ISerializer::OUTPUT) { - std::stringstream stream; - BinaryOutputStreamSerializer output(stream); + std::string field; + StringOutputStream os(field); + BinaryOutputStreamSerializer output(os); doSerialize(tag, output); - std::string field = stream.str(); serializer(field, ""); } else { std::string field; serializer(field, ""); - std::stringstream stream(field); + MemoryInputStream stream(field.data(), field.size()); BinaryInputStreamSerializer input(stream); doSerialize(tag, input); } } +void serialize(KeyPair& keyPair, ISerializer& serializer) { + serializer(keyPair.secretKey, "secret_key"); + serializer(keyPair.publicKey, "public_key"); +} + + } //namespace CryptoNote diff --git a/src/cryptonote_core/cryptonote_serialization.h b/src/CryptoNoteCore/CryptoNoteSerialization.h old mode 100644 new mode 100755 similarity index 55% rename from src/cryptonote_core/cryptonote_serialization.h rename to src/CryptoNoteCore/CryptoNoteSerialization.h index ecf3c8ec..49ee1ad3 --- a/src/cryptonote_core/cryptonote_serialization.h +++ b/src/CryptoNoteCore/CryptoNoteSerialization.h @@ -17,47 +17,51 @@ #pragma once -#include "cryptonote_basic.h" -#include "serialization/ISerializer.h" +#include "CryptoNoteBasic.h" +#include "crypto/chacha8.h" +#include "Serialization/ISerializer.h" +#include "crypto/crypto.h" -namespace crypto { +namespace Crypto { -bool serialize(public_key& pubKey, Common::StringView name, CryptoNote::ISerializer& serializer); -bool serialize(secret_key& secKey, Common::StringView name, CryptoNote::ISerializer& serializer); -bool serialize(hash& h, Common::StringView name, CryptoNote::ISerializer& serializer); +bool serialize(PublicKey& pubKey, Common::StringView name, CryptoNote::ISerializer& serializer); +bool serialize(SecretKey& secKey, Common::StringView name, CryptoNote::ISerializer& serializer); +bool serialize(Hash& h, Common::StringView name, CryptoNote::ISerializer& serializer); bool serialize(chacha8_iv& chacha, Common::StringView name, CryptoNote::ISerializer& serializer); -bool serialize(key_image& keyImage, Common::StringView name, CryptoNote::ISerializer& serializer); -bool serialize(signature& sig, Common::StringView name, CryptoNote::ISerializer& serializer); +bool serialize(KeyImage& keyImage, Common::StringView name, CryptoNote::ISerializer& serializer); +bool serialize(Signature& sig, Common::StringView name, CryptoNote::ISerializer& serializer); +bool serialize(EllipticCurveScalar& ecScalar, Common::StringView name, CryptoNote::ISerializer& serializer); +bool serialize(EllipticCurvePoint& ecPoint, Common::StringView name, CryptoNote::ISerializer& serializer); -} //namespace crypto +} namespace CryptoNote { -void serialize(ParentBlockSerializer& pbs, ISerializer& serializer); + +struct AccountKeys; +struct TransactionExtraMergeMiningTag; + void serialize(TransactionPrefix& txP, ISerializer& serializer); void serialize(Transaction& tx, ISerializer& serializer); void serialize(TransactionInput& in, ISerializer& serializer); void serialize(TransactionOutput& in, ISerializer& serializer); -void serialize(TransactionInputGenerate& gen, ISerializer& serializer); -void serialize(TransactionInputToScript& script, ISerializer& serializer); -void serialize(TransactionInputToScriptHash& scripthash, ISerializer& serializer); -void serialize(TransactionInputToKey& key, ISerializer& serializer); -void serialize(TransactionInputMultisignature& multisignature, ISerializer& serializer); +void serialize(BaseInput& gen, ISerializer& serializer); +void serialize(KeyInput& key, ISerializer& serializer); +void serialize(MultisignatureInput& multisignature, ISerializer& serializer); void serialize(TransactionOutput& output, ISerializer& serializer); - void serialize(TransactionOutputTarget& output, ISerializer& serializer); +void serialize(KeyOutput& key, ISerializer& serializer); +void serialize(MultisignatureOutput& multisignature, ISerializer& serializer); -void serialize(TransactionOutputToScript& script, ISerializer& serializer); -void serialize(TransactionOutputToScriptHash& scripthash, ISerializer& serializer); -void serialize(TransactionOutputToKey& key, ISerializer& serializer); -void serialize(TransactionOutputMultisignature& multisignature, ISerializer& serializer); void serialize(BlockHeader& header, ISerializer& serializer); void serialize(Block& block, ISerializer& serializer); -void serialize(tx_extra_merge_mining_tag& tag, ISerializer& serializer); +void serialize(ParentBlockSerializer& pbs, ISerializer& serializer); +void serialize(TransactionExtraMergeMiningTag& tag, ISerializer& serializer); void serialize(AccountPublicAddress& address, ISerializer& serializer); -void serialize(account_keys& keys, ISerializer& s); +void serialize(AccountKeys& keys, ISerializer& s); +void serialize(KeyPair& keyPair, ISerializer& serializer); -} //namespace CryptoNote +} diff --git a/src/cryptonote_core/cryptonote_stat_info.h b/src/CryptoNoteCore/CryptoNoteStatInfo.h old mode 100644 new mode 100755 similarity index 96% rename from src/cryptonote_core/cryptonote_stat_info.h rename to src/CryptoNoteCore/CryptoNoteStatInfo.h index 0c7bec64..5fd0ec7b --- a/src/cryptonote_core/cryptonote_stat_info.h +++ b/src/CryptoNoteCore/CryptoNoteStatInfo.h @@ -17,7 +17,7 @@ #pragma once -#include "serialization/ISerializer.h" +#include "Serialization/ISerializer.h" namespace CryptoNote { diff --git a/src/CryptoNoteCore/CryptoNoteTools.cpp b/src/CryptoNoteCore/CryptoNoteTools.cpp new file mode 100755 index 00000000..e1c406ee --- /dev/null +++ b/src/CryptoNoteCore/CryptoNoteTools.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "CryptoNoteTools.h" +#include "CryptoNoteFormatUtils.h" + +namespace CryptoNote { +template<> +bool toBinaryArray(const BinaryArray& object, BinaryArray& binaryArray) { + try { + Common::VectorOutputStream stream(binaryArray); + BinaryOutputStreamSerializer serializer(stream); + std::string oldBlob = Common::asString(object); + serializer(oldBlob, ""); + } catch (std::exception&) { + return false; + } + + return true; +} + +void getBinaryArrayHash(const BinaryArray& binaryArray, Crypto::Hash& hash) { + cn_fast_hash(binaryArray.data(), binaryArray.size(), hash); +} + +Crypto::Hash getBinaryArrayHash(const BinaryArray& binaryArray) { + Crypto::Hash hash; + getBinaryArrayHash(binaryArray, hash); + return hash; +} + +uint64_t getInputAmount(const Transaction& transaction) { + uint64_t amount = 0; + for (auto& input : transaction.inputs) { + if (input.type() == typeid(KeyInput)) { + amount += boost::get(input).amount; + } else if (input.type() == typeid(MultisignatureInput)) { + amount += boost::get(input).amount; + } + } + + return amount; +} + +uint64_t getOutputAmount(const Transaction& transaction) { + uint64_t amount = 0; + for (auto& output : transaction.outputs) { + amount += output.amount; + } + + return amount; +} + +void decomposeAmount(uint64_t amount, uint64_t dustThreshold, std::vector& decomposedAmounts) { + decompose_amount_into_digits(amount, dustThreshold, + [&](uint64_t amount) { + decomposedAmounts.push_back(amount); + }, + [&](uint64_t dust) { + decomposedAmounts.push_back(dust); + } + ); +} + +} diff --git a/src/CryptoNoteCore/CryptoNoteTools.h b/src/CryptoNoteCore/CryptoNoteTools.h new file mode 100755 index 00000000..79850634 --- /dev/null +++ b/src/CryptoNoteCore/CryptoNoteTools.h @@ -0,0 +1,125 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include "Common/MemoryInputStream.h" +#include "Common/StringTools.h" +#include "Common/VectorOutputStream.h" +#include "Serialization/BinaryOutputStreamSerializer.h" +#include "Serialization/BinaryInputStreamSerializer.h" +#include "CryptoNoteSerialization.h" + +namespace CryptoNote { + +void getBinaryArrayHash(const BinaryArray& binaryArray, Crypto::Hash& hash); +Crypto::Hash getBinaryArrayHash(const BinaryArray& binaryArray); + +template +bool toBinaryArray(const T& object, BinaryArray& binaryArray) { + try { + ::Common::VectorOutputStream stream(binaryArray); + BinaryOutputStreamSerializer serializer(stream); + serialize(const_cast(object), serializer); + } catch (std::exception&) { + return false; + } + + return true; +} + +template<> +bool toBinaryArray(const BinaryArray& object, BinaryArray& binaryArray); + +template +BinaryArray toBinaryArray(const T& object) { + BinaryArray ba; + toBinaryArray(object, ba); + return ba; +} + +template +bool fromBinaryArray(T& object, const BinaryArray& binaryArray) { + bool result = false; + try { + Common::MemoryInputStream stream(binaryArray.data(), binaryArray.size()); + BinaryInputStreamSerializer serializer(stream); + serialize(object, serializer); + result = stream.endOfStream(); // check that all data was consumed + } catch (std::exception&) { + } + + return result; +} + +template +bool getObjectBinarySize(const T& object, size_t& size) { + BinaryArray ba; + if (!toBinaryArray(object, ba)) { + size = (std::numeric_limits::max)(); + return false; + } + + size = ba.size(); + return true; +} + +template +size_t getObjectBinarySize(const T& object) { + size_t size; + getObjectBinarySize(object, size); + return size; +} + +template +bool getObjectHash(const T& object, Crypto::Hash& hash) { + BinaryArray ba; + if (!toBinaryArray(object, ba)) { + hash = NULL_HASH; + return false; + } + + hash = getBinaryArrayHash(ba); + return true; +} + +template +bool getObjectHash(const T& object, Crypto::Hash& hash, size_t& size) { + BinaryArray ba; + if (!toBinaryArray(object, ba)) { + hash = NULL_HASH; + size = (std::numeric_limits::max)(); + return false; + } + + size = ba.size(); + hash = getBinaryArrayHash(ba); + return true; +} + +template +Crypto::Hash getObjectHash(const T& object) { + Crypto::Hash hash; + getObjectHash(object, hash); + return hash; +} + +uint64_t getInputAmount(const Transaction& transaction); +uint64_t getOutputAmount(const Transaction& transaction); +void decomposeAmount(uint64_t amount, uint64_t dustThreshold, std::vector& decomposedAmounts); +} diff --git a/src/cryptonote_core/Currency.cpp b/src/CryptoNoteCore/Currency.cpp old mode 100644 new mode 100755 similarity index 86% rename from src/cryptonote_core/Currency.cpp rename to src/CryptoNoteCore/Currency.cpp index c75f2b39..73590e89 --- a/src/cryptonote_core/Currency.cpp +++ b/src/CryptoNoteCore/Currency.cpp @@ -19,15 +19,21 @@ #include #include #include -#include "../Common/base58.h" +#include "../Common/Base58.h" #include "../Common/int-util.h" -#include "account.h" -#include "cryptonote_basic_impl.h" -#include "cryptonote_format_utils.h" +#include "../Common/StringTools.h" + +#include "Account.h" +#include "CryptoNoteBasicImpl.h" +#include "CryptoNoteFormatUtils.h" +#include "CryptoNoteTools.h" +#include "TransactionExtra.h" #include "UpgradeDetector.h" + #undef ERROR using namespace Logging; +using namespace Common; namespace CryptoNote { @@ -48,6 +54,7 @@ bool Currency::init() { m_blocksCacheFileName = "testnet_" + m_blocksCacheFileName; m_blockIndexesFileName = "testnet_" + m_blockIndexesFileName; m_txPoolFileName = "testnet_" + m_txPoolFileName; + m_blockchinIndicesFileName = "testnet_" + m_blockchinIndicesFileName; } return true; @@ -58,17 +65,17 @@ bool Currency::generateGenesisBlock() { //account_public_address ac = boost::value_initialized(); //std::vector sz; - //constructMinerTx(0, 0, 0, 0, 0, ac, m_genesisBlock.minerTx); // zero fee in genesis - //blobdata txb = tx_to_blob(m_genesisBlock.minerTx); + //constructMinerTx(0, 0, 0, 0, 0, ac, m_genesisBlock.baseTransaction); // zero fee in genesis + //BinaryArray txb = toBinaryArray(m_genesisBlock.baseTransaction); //std::string hex_tx_represent = Common::toHex(txb); // Hard code coinbase tx in genesis block, because through generating tx use random, but genesis should be always the same std::string genesisCoinbaseTxHex = "010a01ff0001ffffffffffff0f029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121013c086a48c15fb637a96991bc6d53caf77068b5ba6eeb3c82357228c49790584a"; - blobdata minerTxBlob; + BinaryArray minerTxBlob; bool r = - hexToBlob(genesisCoinbaseTxHex, minerTxBlob) && - parse_and_validate_tx_from_blob(minerTxBlob, m_genesisBlock.minerTx); + fromHex(genesisCoinbaseTxHex, minerTxBlob) && + fromBinaryArray(m_genesisBlock.baseTransaction, minerTxBlob); if (!r) { logger(ERROR, BRIGHT_RED) << "failed to parse coinbase tx from hard coded blob"; @@ -122,22 +129,22 @@ size_t Currency::maxBlockCumulativeSize(uint64_t height) const { bool Currency::constructMinerTx(uint32_t height, size_t medianSize, uint64_t alreadyGeneratedCoins, size_t currentBlockSize, uint64_t fee, const AccountPublicAddress& minerAddress, Transaction& tx, - const blobdata& extraNonce/* = blobdata()*/, size_t maxOuts/* = 1*/, + const BinaryArray& extraNonce/* = BinaryArray()*/, size_t maxOuts/* = 1*/, bool penalizeFee/* = false*/) const { - tx.vin.clear(); - tx.vout.clear(); + tx.inputs.clear(); + tx.outputs.clear(); tx.extra.clear(); - KeyPair txkey = KeyPair::generate(); - add_tx_pub_key_to_extra(tx, txkey.pub); + KeyPair txkey = generateKeyPair(); + addTransactionPublicKeyToExtra(tx.extra, txkey.publicKey); if (!extraNonce.empty()) { - if (!add_extra_nonce_to_tx_extra(tx.extra, extraNonce)) { + if (!addExtraNonceToTransactionExtra(tx.extra, extraNonce)) { return false; } } - TransactionInputGenerate in; - in.height = height; + BaseInput in; + in.blockIndex = height; uint64_t blockReward; int64_t emissionChange; @@ -145,9 +152,6 @@ bool Currency::constructMinerTx(uint32_t height, size_t medianSize, uint64_t alr logger(INFO) << "Block is too big"; return false; } -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - logger(DEBUGGING) << "Creating block template: reward " << blockReward << ", fee " << fee; -#endif std::vector outAmounts; decompose_amount_into_digits(blockReward, m_defaultDustThreshold, @@ -162,35 +166,35 @@ bool Currency::constructMinerTx(uint32_t height, size_t medianSize, uint64_t alr uint64_t summaryAmounts = 0; for (size_t no = 0; no < outAmounts.size(); no++) { - crypto::key_derivation derivation = boost::value_initialized(); - crypto::public_key outEphemeralPubKey = boost::value_initialized(); + Crypto::KeyDerivation derivation = boost::value_initialized(); + Crypto::PublicKey outEphemeralPubKey = boost::value_initialized(); - bool r = crypto::generate_key_derivation(minerAddress.m_viewPublicKey, txkey.sec, derivation); + bool r = Crypto::generate_key_derivation(minerAddress.viewPublicKey, txkey.secretKey, derivation); if (!(r)) { logger(ERROR, BRIGHT_RED) << "while creating outs: failed to generate_key_derivation(" - << minerAddress.m_viewPublicKey << ", " << txkey.sec << ")"; + << minerAddress.viewPublicKey << ", " << txkey.secretKey << ")"; return false; } - r = crypto::derive_public_key(derivation, no, minerAddress.m_spendPublicKey, outEphemeralPubKey); + r = Crypto::derive_public_key(derivation, no, minerAddress.spendPublicKey, outEphemeralPubKey); if (!(r)) { logger(ERROR, BRIGHT_RED) << "while creating outs: failed to derive_public_key(" << derivation << ", " << no << ", " - << minerAddress.m_spendPublicKey << ")"; + << minerAddress.spendPublicKey << ")"; return false; } - TransactionOutputToKey tk; + KeyOutput tk; tk.key = outEphemeralPubKey; TransactionOutput out; summaryAmounts += out.amount = outAmounts[no]; out.target = tk; - tx.vout.push_back(out); + tx.outputs.push_back(out); } if (!(summaryAmounts == blockReward)) { @@ -201,12 +205,12 @@ bool Currency::constructMinerTx(uint32_t height, size_t medianSize, uint64_t alr tx.version = CURRENT_TRANSACTION_VERSION; //lock tx.unlockTime = height + m_minedMoneyUnlockWindow; - tx.vin.push_back(in); + tx.inputs.push_back(in); return true; } -std::string Currency::accountAddressAsString(const account_base& account) const { - return getAccountAddressAsStr(m_publicAddressBase58Prefix, account.get_keys().m_account_address); +std::string Currency::accountAddressAsString(const AccountBase& account) const { + return getAccountAddressAsStr(m_publicAddressBase58Prefix, account.getAccountKeys().address); } std::string Currency::accountAddressAsString(const AccountPublicAddress& accountPublicAddress) const { @@ -316,8 +320,8 @@ difficulty_type Currency::nextDifficulty(std::vector timestamps, return (low + timeSpan - 1) / timeSpan; } -bool Currency::checkProofOfWorkV1(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, - crypto::hash& proofOfWork) const { +bool Currency::checkProofOfWorkV1(Crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, + Crypto::Hash& proofOfWork) const { if (BLOCK_MAJOR_VERSION_1 != block.majorVersion) { return false; } @@ -329,8 +333,8 @@ bool Currency::checkProofOfWorkV1(crypto::cn_context& context, const Block& bloc return check_hash(proofOfWork, currentDiffic); } -bool Currency::checkProofOfWorkV2(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, - crypto::hash& proofOfWork) const { +bool Currency::checkProofOfWorkV2(Crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, + Crypto::Hash& proofOfWork) const { if (BLOCK_MAJOR_VERSION_2 != block.majorVersion) { return false; } @@ -343,8 +347,8 @@ bool Currency::checkProofOfWorkV2(crypto::cn_context& context, const Block& bloc return false; } - tx_extra_merge_mining_tag mmTag; - if (!get_mm_tag_from_extra(block.parentBlock.minerTx.extra, mmTag)) { + TransactionExtraMergeMiningTag mmTag; + if (!getMergeMiningTagFromExtra(block.parentBlock.baseTransaction.extra, mmTag)) { logger(ERROR) << "merge mining tag wasn't found in extra of the parent block miner transaction"; return false; } @@ -353,16 +357,16 @@ bool Currency::checkProofOfWorkV2(crypto::cn_context& context, const Block& bloc return false; } - crypto::hash auxBlockHeaderHash; + Crypto::Hash auxBlockHeaderHash; if (!get_aux_block_header_hash(block, auxBlockHeaderHash)) { return false; } - crypto::hash auxBlocksMerkleRoot; - crypto::tree_hash_from_branch(block.parentBlock.blockchainBranch.data(), block.parentBlock.blockchainBranch.size(), + Crypto::Hash auxBlocksMerkleRoot; + Crypto::tree_hash_from_branch(block.parentBlock.blockchainBranch.data(), block.parentBlock.blockchainBranch.size(), auxBlockHeaderHash, &m_genesisBlockHash, auxBlocksMerkleRoot); - if (auxBlocksMerkleRoot != mmTag.merkle_root) { + if (auxBlocksMerkleRoot != mmTag.merkleRoot) { logger(ERROR, BRIGHT_YELLOW) << "Aux block hash wasn't found in merkle tree"; return false; } @@ -370,7 +374,7 @@ bool Currency::checkProofOfWorkV2(crypto::cn_context& context, const Block& bloc return true; } -bool Currency::checkProofOfWork(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, crypto::hash& proofOfWork) const { +bool Currency::checkProofOfWork(Crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, Crypto::Hash& proofOfWork) const { switch (block.majorVersion) { case BLOCK_MAJOR_VERSION_1: return checkProofOfWorkV1(context, block, currentDiffic, proofOfWork); case BLOCK_MAJOR_VERSION_2: return checkProofOfWorkV2(context, block, currentDiffic, proofOfWork); @@ -427,6 +431,7 @@ CurrencyBuilder::CurrencyBuilder(Logging::ILogger& log) : m_currency(log) { blocksCacheFileName(parameters::CRYPTONOTE_BLOCKSCACHE_FILENAME); blockIndexesFileName(parameters::CRYPTONOTE_BLOCKINDEXES_FILENAME); txPoolFileName(parameters::CRYPTONOTE_POOLDATA_FILENAME); + blockchinIndicesFileName(parameters::CRYPTONOTE_BLOCKCHAIN_INDICES_FILENAME); testnet(false); } diff --git a/src/cryptonote_core/Currency.h b/src/CryptoNoteCore/Currency.h old mode 100644 new mode 100755 similarity index 91% rename from src/cryptonote_core/Currency.h rename to src/CryptoNoteCore/Currency.h index d4009f0f..7394e48f --- a/src/cryptonote_core/Currency.h +++ b/src/CryptoNoteCore/Currency.h @@ -21,15 +21,16 @@ #include #include #include -#include "../cryptonote_config.h" +#include "../CryptoNoteConfig.h" #include "../crypto/hash.h" -#include "../cryptonote_protocol/blobdatatype.h" #include "../Logging/LoggerRef.h" -#include "cryptonote_basic.h" -#include "difficulty.h" +#include "CryptoNoteBasic.h" +#include "Difficulty.h" namespace CryptoNote { +class AccountBase; + class Currency { public: uint64_t maxBlockHeight() const { return m_maxBlockHeight; } @@ -83,11 +84,12 @@ public: const std::string& blocksCacheFileName() const { return m_blocksCacheFileName; } const std::string& blockIndexesFileName() const { return m_blockIndexesFileName; } const std::string& txPoolFileName() const { return m_txPoolFileName; } + const std::string& blockchinIndicesFileName() const { return m_blockchinIndicesFileName; } bool isTestnet() const { return m_testnet; } const Block& genesisBlock() const { return m_genesisBlock; } - const crypto::hash& genesisBlockHash() const { return m_genesisBlockHash; } + const Crypto::Hash& genesisBlockHash() const { return m_genesisBlockHash; } bool getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, bool penalizeFee, uint64_t& reward, int64_t& emissionChange) const; @@ -95,9 +97,9 @@ public: bool constructMinerTx(uint32_t height, size_t medianSize, uint64_t alreadyGeneratedCoins, size_t currentBlockSize, uint64_t fee, const AccountPublicAddress& minerAddress, Transaction& tx, - const blobdata& extraNonce = blobdata(), size_t maxOuts = 1, bool penalizeFee = false) const; + const BinaryArray& extraNonce = BinaryArray(), size_t maxOuts = 1, bool penalizeFee = false) const; - std::string accountAddressAsString(const account_base& account) const; + std::string accountAddressAsString(const AccountBase& account) const; std::string accountAddressAsString(const AccountPublicAddress& accountPublicAddress) const; bool parseAccountAddressString(const std::string& str, AccountPublicAddress& addr) const; @@ -106,9 +108,9 @@ public: difficulty_type nextDifficulty(std::vector timestamps, std::vector cumulativeDifficulties) const; - bool checkProofOfWorkV1(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, crypto::hash& proofOfWork) const; - bool checkProofOfWorkV2(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, crypto::hash& proofOfWork) const; - bool checkProofOfWork(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, crypto::hash& proofOfWork) const; + bool checkProofOfWorkV1(Crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, Crypto::Hash& proofOfWork) const; + bool checkProofOfWorkV2(Crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, Crypto::Hash& proofOfWork) const; + bool checkProofOfWork(Crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, Crypto::Hash& proofOfWork) const; private: Currency(Logging::ILogger& log) : logger(log, "currency") { @@ -166,11 +168,12 @@ private: std::string m_blocksCacheFileName; std::string m_blockIndexesFileName; std::string m_txPoolFileName; + std::string m_blockchinIndicesFileName; bool m_testnet; Block m_genesisBlock; - crypto::hash m_genesisBlockHash; + Crypto::Hash m_genesisBlockHash; Logging::LoggerRef logger; @@ -234,7 +237,8 @@ public: CurrencyBuilder& blocksCacheFileName(const std::string& val) { m_currency.m_blocksCacheFileName = val; return *this; } CurrencyBuilder& blockIndexesFileName(const std::string& val) { m_currency.m_blockIndexesFileName = val; return *this; } CurrencyBuilder& txPoolFileName(const std::string& val) { m_currency.m_txPoolFileName = val; return *this; } - + CurrencyBuilder& blockchinIndicesFileName(const std::string& val) { m_currency.m_blockchinIndicesFileName = val; return *this; } + CurrencyBuilder& testnet(bool val) { m_currency.m_testnet = val; return *this; } private: diff --git a/src/cryptonote_core/difficulty.cpp b/src/CryptoNoteCore/Difficulty.cpp old mode 100644 new mode 100755 similarity index 91% rename from src/cryptonote_core/difficulty.cpp rename to src/CryptoNoteCore/Difficulty.cpp index bc7e8bc4..8b37efd4 --- a/src/cryptonote_core/difficulty.cpp +++ b/src/CryptoNoteCore/Difficulty.cpp @@ -23,24 +23,15 @@ #include "Common/int-util.h" #include "crypto/hash.h" -#include "cryptonote_config.h" -#include "difficulty.h" +#include "CryptoNoteConfig.h" +#include "Difficulty.h" namespace CryptoNote { - using std::size_t; using std::uint64_t; using std::vector; -#if defined(_MSC_VER) -#include -#include - - static inline void mul(uint64_t a, uint64_t b, uint64_t &low, uint64_t &high) { - low = mul128(a, b, &high); - } - -#else +#if defined(__SIZEOF_INT128__) static inline void mul(uint64_t a, uint64_t b, uint64_t &low, uint64_t &high) { typedef unsigned __int128 uint128_t; @@ -49,6 +40,12 @@ namespace CryptoNote { high = (uint64_t) (res >> 64); } +#else + + static inline void mul(uint64_t a, uint64_t b, uint64_t &low, uint64_t &high) { + low = mul128(a, b, &high); + } + #endif static inline bool cadd(uint64_t a, uint64_t b) { @@ -59,7 +56,7 @@ namespace CryptoNote { return a + b < a || (c && a + b == (uint64_t) -1); } - bool check_hash(const crypto::hash &hash, difficulty_type difficulty) { + bool check_hash(const Crypto::Hash &hash, difficulty_type difficulty) { uint64_t low, high, top, cur; // First check the highest word, this will most likely fail for a random hash. mul(swap64le(((const uint64_t *) &hash)[3]), difficulty, top, high); diff --git a/src/cryptonote_core/difficulty.h b/src/CryptoNoteCore/Difficulty.h old mode 100644 new mode 100755 similarity index 93% rename from src/cryptonote_core/difficulty.h rename to src/CryptoNoteCore/Difficulty.h index 103c86e7..f59336a9 --- a/src/cryptonote_core/difficulty.h +++ b/src/CryptoNoteCore/Difficulty.h @@ -26,5 +26,5 @@ namespace CryptoNote { typedef std::uint64_t difficulty_type; - bool check_hash(const crypto::hash &hash, difficulty_type difficulty); + bool check_hash(const Crypto::Hash &hash, difficulty_type difficulty); } diff --git a/src/System/LatchGuard.cpp b/src/CryptoNoteCore/IBlock.cpp similarity index 79% rename from src/System/LatchGuard.cpp rename to src/CryptoNoteCore/IBlock.cpp index 15ba0a7d..a6336e6c 100644 --- a/src/System/LatchGuard.cpp +++ b/src/CryptoNoteCore/IBlock.cpp @@ -15,18 +15,9 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "LatchGuard.h" +#include "IBlock.h" -#include "System/Latch.h" - -namespace System { - -LatchGuard::LatchGuard(Latch& latch) : m_latch(latch) { - m_latch.increase(); +namespace CryptoNote { +IBlock::~IBlock() { } - -LatchGuard::~LatchGuard() { - m_latch.decrease(); -} - } diff --git a/src/CryptoNote/KeyOutput.cpp b/src/CryptoNoteCore/IBlock.h old mode 100755 new mode 100644 similarity index 75% rename from src/CryptoNote/KeyOutput.cpp rename to src/CryptoNoteCore/IBlock.h index 9a97a1e4..e86cdcd9 --- a/src/CryptoNote/KeyOutput.cpp +++ b/src/CryptoNoteCore/IBlock.h @@ -15,19 +15,17 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "KeyOutput.h" +#pragma once + +#include "CryptoNote.h" namespace CryptoNote { +class IBlock { +public: + virtual ~IBlock(); -KeyOutput::KeyOutput(uint64_t amount, const crypto::public_key& key) : amount(amount), key(key) { -} - -uint64_t KeyOutput::getAmount() const { - return amount; -} - -const crypto::public_key& KeyOutput::getKey() const { - return key; -} - + virtual const Block& getBlock() const = 0; + virtual size_t getTransactionCount() const = 0; + virtual const Transaction& getTransaction(size_t index) const = 0; +}; } diff --git a/src/cryptonote_core/IBlockchainStorageObserver.h b/src/CryptoNoteCore/IBlockchainStorageObserver.h similarity index 100% rename from src/cryptonote_core/IBlockchainStorageObserver.h rename to src/CryptoNoteCore/IBlockchainStorageObserver.h diff --git a/src/CryptoNoteCore/ICore.h b/src/CryptoNoteCore/ICore.h new file mode 100755 index 00000000..72b4460f --- /dev/null +++ b/src/CryptoNoteCore/ICore.h @@ -0,0 +1,122 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include "CryptoNoteCore/Difficulty.h" + +#include "CryptoNoteCore/MessageQueue.h" +#include "CryptoNoteCore/BlockchainMessages.h" + +namespace CryptoNote { + +struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request; +struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response; +struct NOTIFY_RESPONSE_GET_OBJECTS_request; +struct NOTIFY_REQUEST_GET_OBJECTS_request; + +class Currency; +class IBlock; +class ICoreObserver; +struct Block; +struct block_verification_context; +struct BlockFullInfo; +struct BlockShortInfo; +struct core_stat_info; +struct i_cryptonote_protocol; +struct Transaction; +struct MultisignatureInput; +struct KeyInput; +struct TransactionPrefixInfo; +struct tx_verification_context; + +class ICore { +public: + virtual ~ICore() {} + + virtual bool addObserver(ICoreObserver* observer) = 0; + virtual bool removeObserver(ICoreObserver* observer) = 0; + + virtual bool have_block(const Crypto::Hash& id) = 0; + virtual std::vector buildSparseChain() = 0; + virtual std::vector buildSparseChain(const Crypto::Hash& startBlockId) = 0; + virtual bool get_stat_info(CryptoNote::core_stat_info& st_inf) = 0; + virtual bool on_idle() = 0; + virtual void pause_mining() = 0; + virtual void update_block_template_and_resume_mining() = 0; + virtual bool handle_incoming_block_blob(const CryptoNote::BinaryArray& block_blob, CryptoNote::block_verification_context& bvc, bool control_miner, bool relay_block) = 0; + virtual bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp) = 0; //Deprecated. Should be removed with CryptoNoteProtocolHandler. + virtual void on_synchronized() = 0; + virtual bool is_ready() = 0; + virtual size_t addChain(const std::vector& chain) = 0; + + virtual void get_blockchain_top(uint32_t& height, Crypto::Hash& top_id) = 0; + virtual std::vector findBlockchainSupplement(const std::vector& remoteBlockIds, size_t maxCount, + uint32_t& totalBlockCount, uint32_t& startBlockIndex) = 0; + virtual bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res) = 0; + virtual bool get_tx_outputs_gindexs(const Crypto::Hash& tx_id, std::vector& indexs) = 0; + virtual bool getOutByMSigGIndex(uint64_t amount, uint64_t gindex, MultisignatureOutput& out) = 0; + virtual i_cryptonote_protocol* get_protocol() = 0; + virtual bool handle_incoming_tx(const BinaryArray& tx_blob, tx_verification_context& tvc, bool keeped_by_block) = 0; //Deprecated. Should be removed with CryptoNoteProtocolHandler. + virtual std::vector getPoolTransactions() = 0; + virtual bool getPoolChanges(const Crypto::Hash& tailBlockId, const std::vector& knownTxsIds, + std::vector& addedTxs, std::vector& deletedTxsIds) = 0; + virtual bool getPoolChangesLite(const Crypto::Hash& tailBlockId, const std::vector& knownTxsIds, + std::vector& addedTxs, std::vector& deletedTxsIds) = 0; + virtual void getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, + std::vector& deletedTxsIds) = 0; + virtual bool queryBlocks(const std::vector& block_ids, uint64_t timestamp, + uint32_t& start_height, uint32_t& current_height, uint32_t& full_offset, std::vector& entries) = 0; + virtual bool queryBlocksLite(const std::vector& block_ids, uint64_t timestamp, + uint32_t& start_height, uint32_t& current_height, uint32_t& full_offset, std::vector& entries) = 0; + + virtual Crypto::Hash getBlockIdByHeight(uint32_t height) = 0; + virtual bool getBlockByHash(const Crypto::Hash &h, Block &blk) = 0; + virtual bool getBlockHeight(const Crypto::Hash& blockId, uint32_t& blockHeight) = 0; + virtual void getTransactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs, bool checkTxPool = false) = 0; + virtual bool getBackwardBlocksSizes(uint32_t fromHeight, std::vector& sizes, size_t count) = 0; + virtual bool getBlockSize(const Crypto::Hash& hash, size_t& size) = 0; + virtual bool getAlreadyGeneratedCoins(const Crypto::Hash& hash, uint64_t& generatedCoins) = 0; + virtual bool getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, + bool penalizeFee, uint64_t& reward, int64_t& emissionChange) = 0; + virtual bool scanOutputkeysForIndices(const KeyInput& txInToKey, std::list>& outputReferences) = 0; + virtual bool getBlockDifficulty(uint32_t height, difficulty_type& difficulty) = 0; + virtual bool getBlockContainingTx(const Crypto::Hash& txId, Crypto::Hash& blockId, uint32_t& blockHeight) = 0; + virtual bool getMultisigOutputReference(const MultisignatureInput& txInMultisig, std::pair& outputReference) = 0; + + virtual bool getGeneratedTransactionsNumber(uint32_t height, uint64_t& generatedTransactions) = 0; + virtual bool getOrphanBlocksByHeight(uint32_t height, std::vector& blocks) = 0; + virtual bool getBlocksByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps) = 0; + virtual bool getPoolTransactionsByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps) = 0; + virtual bool getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions) = 0; + + virtual std::unique_ptr getBlock(const Crypto::Hash& blocksId) = 0; + virtual bool handleIncomingTransaction(const Transaction& tx, const Crypto::Hash& txHash, size_t blobSize, tx_verification_context& tvc, bool keptByBlock) = 0; + virtual std::error_code executeLocked(const std::function& func) = 0; + + virtual bool addMessageQueue(MessageQueue& messageQueue) = 0; + virtual bool removeMessageQueue(MessageQueue& messageQueue) = 0; +}; + +} //namespace CryptoNote diff --git a/src/cryptonote_core/ICoreObserver.h b/src/CryptoNoteCore/ICoreObserver.h similarity index 100% rename from src/cryptonote_core/ICoreObserver.h rename to src/CryptoNoteCore/ICoreObserver.h diff --git a/src/cryptonote_core/i_miner_handler.h b/src/CryptoNoteCore/IMinerHandler.h old mode 100644 new mode 100755 similarity index 86% rename from src/cryptonote_core/i_miner_handler.h rename to src/CryptoNoteCore/IMinerHandler.h index 8cf20598..0d1eaa78 --- a/src/cryptonote_core/i_miner_handler.h +++ b/src/CryptoNoteCore/IMinerHandler.h @@ -17,15 +17,15 @@ #pragma once -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/difficulty.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" +#include "CryptoNoteCore/Difficulty.h" namespace CryptoNote { - struct i_miner_handler { + struct IMinerHandler { virtual bool handle_block_found(Block& b) = 0; - virtual bool get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint32_t& height, const blobdata& ex_nonce) = 0; + virtual bool get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint32_t& height, const BinaryArray& ex_nonce) = 0; protected: - ~i_miner_handler(){}; + ~IMinerHandler(){}; }; } diff --git a/src/cryptonote_core/ITimeProvider.cpp b/src/CryptoNoteCore/ITimeProvider.cpp similarity index 100% rename from src/cryptonote_core/ITimeProvider.cpp rename to src/CryptoNoteCore/ITimeProvider.cpp diff --git a/src/cryptonote_core/ITimeProvider.h b/src/CryptoNoteCore/ITimeProvider.h similarity index 100% rename from src/cryptonote_core/ITimeProvider.h rename to src/CryptoNoteCore/ITimeProvider.h diff --git a/src/cryptonote_core/ITransactionValidator.h b/src/CryptoNoteCore/ITransactionValidator.h old mode 100644 new mode 100755 similarity index 85% rename from src/cryptonote_core/ITransactionValidator.h rename to src/CryptoNoteCore/ITransactionValidator.h index 825ed009..b85ab487 --- a/src/cryptonote_core/ITransactionValidator.h +++ b/src/CryptoNoteCore/ITransactionValidator.h @@ -17,13 +17,13 @@ #pragma once -#include "cryptonote_core/cryptonote_basic.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" namespace CryptoNote { struct BlockInfo { - uint64_t height; - crypto::hash id; + uint32_t height; + Crypto::Hash id; BlockInfo() { clear(); @@ -31,11 +31,11 @@ namespace CryptoNote { void clear() { height = 0; - id = CryptoNote::null_hash; + id = CryptoNote::NULL_HASH; } bool empty() const { - return id == CryptoNote::null_hash; + return id == CryptoNote::NULL_HASH; } }; @@ -46,6 +46,7 @@ namespace CryptoNote { virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock) = 0; virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed) = 0; virtual bool haveSpentKeyImages(const CryptoNote::Transaction& tx) = 0; + virtual bool checkTransactionSize(size_t blobSize) = 0; }; } diff --git a/src/cryptonote_core/ITxPoolObserver.h b/src/CryptoNoteCore/ITxPoolObserver.h similarity index 100% rename from src/cryptonote_core/ITxPoolObserver.h rename to src/CryptoNoteCore/ITxPoolObserver.h diff --git a/src/CryptoNoteCore/IntrusiveLinkedList.h b/src/CryptoNoteCore/IntrusiveLinkedList.h new file mode 100644 index 00000000..1f5320f9 --- /dev/null +++ b/src/CryptoNoteCore/IntrusiveLinkedList.h @@ -0,0 +1,211 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +namespace CryptoNote { + +//Value must have public method IntrusiveLinkedList::hook& getHook() +template class IntrusiveLinkedList { +public: + class hook { + public: + friend class IntrusiveLinkedList; + + hook(); + private: + Value* prev; + Value* next; + bool used; + }; + + class iterator { + public: + iterator(Value* value); + + bool operator!=(const iterator& other) const; + bool operator==(const iterator& other) const; + iterator& operator++(); + iterator operator++(int); + iterator& operator--(); + iterator operator--(int); + + Value& operator*() const; + Value* operator->() const; + + private: + Value* currentElement; + }; + + IntrusiveLinkedList(); + + bool insert(Value& value); + bool remove(Value& value); + + bool empty() const; + + iterator begin(); + iterator end(); +private: + Value* head; + Value* tail; +}; + +template +IntrusiveLinkedList::IntrusiveLinkedList() : head(nullptr), tail(nullptr) {} + +template +bool IntrusiveLinkedList::insert(Value& value) { + if (!value.getHook().used) { + if (head == nullptr) { + head = &value; + tail = head; + value.getHook().prev = nullptr; + } else { + tail->getHook().next = &value; + value.getHook().prev = tail; + tail = &value; + } + + value.getHook().next = nullptr; + value.getHook().used = true; + return true; + } else { + return false; + } +} + +template +bool IntrusiveLinkedList::remove(Value& value) { + if (value.getHook().used && head != nullptr) { + Value* toRemove = &value; + Value* current = head; + while (current->getHook().next != nullptr) { + if (toRemove == current) { + break; + } + + current = current->getHook().next; + } + + if (toRemove == current) { + if (current->getHook().prev == nullptr) { + assert(current == head); + head = current->getHook().next; + + if (head != nullptr) { + head->getHook().prev = nullptr; + } else { + tail = nullptr; + } + } else { + current->getHook().prev->getHook().next = current->getHook().next; + if (current->getHook().next != nullptr) { + current->getHook().next->getHook().prev = current->getHook().prev; + } else { + tail = current->getHook().prev; + } + } + + current->getHook().prev = nullptr; + current->getHook().next = nullptr; + current->getHook().used = false; + return true; + } else { + return false; + } + } else { + return false; + } +} + +template +bool IntrusiveLinkedList::empty() const { + return head == nullptr; +} + +template +typename IntrusiveLinkedList::iterator IntrusiveLinkedList::begin() { + return iterator(head); +} + +template +typename IntrusiveLinkedList::iterator IntrusiveLinkedList::end() { + return iterator(nullptr); +} + +template +IntrusiveLinkedList::hook::hook() : prev(nullptr), next(nullptr), used(false) {} + +template +IntrusiveLinkedList::iterator::iterator(Value* value) : currentElement(value) {} + +template +bool IntrusiveLinkedList::iterator::operator!=(const typename IntrusiveLinkedList::iterator& other) const { + return currentElement != other.currentElement; +} + +template +bool IntrusiveLinkedList::iterator::operator==(const typename IntrusiveLinkedList::iterator& other) const { + return currentElement == other.currentElement; +} + +template +typename IntrusiveLinkedList::iterator& IntrusiveLinkedList::iterator::operator++() { + assert(currentElement != nullptr); + currentElement = currentElement->getHook().next; + return *this; +} + +template +typename IntrusiveLinkedList::iterator IntrusiveLinkedList::iterator::operator++(int) { + IntrusiveLinkedList::iterator copy = *this; + + assert(currentElement != nullptr); + currentElement = currentElement->getHook().next; + return copy; +} + +template +typename IntrusiveLinkedList::iterator& IntrusiveLinkedList::iterator::operator--() { + assert(currentElement != nullptr); + currentElement = currentElement->getHook().prev; + return *this; +} + +template +typename IntrusiveLinkedList::iterator IntrusiveLinkedList::iterator::operator--(int) { + IntrusiveLinkedList::iterator copy = *this; + + assert(currentElement != nullptr); + currentElement = currentElement->getHook().prev; + return copy; +} + +template +Value& IntrusiveLinkedList::iterator::operator*() const { + assert(currentElement != nullptr); + + return *currentElement; +} + +template +Value* IntrusiveLinkedList::iterator::operator->() const { + return currentElement; +} + +} diff --git a/src/CryptoNoteCore/MessageQueue.h b/src/CryptoNoteCore/MessageQueue.h new file mode 100644 index 00000000..6be5f38f --- /dev/null +++ b/src/CryptoNoteCore/MessageQueue.h @@ -0,0 +1,114 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +#include "IntrusiveLinkedList.h" + +#include "System/Event.h" +#include "System/InterruptedException.h" + +namespace CryptoNote { + +template class MessageQueue { +public: + MessageQueue(System::Dispatcher& dispatcher); + + const MessageType& front(); + void pop(); + void push(const MessageType& message); + + void stop(); + + typename IntrusiveLinkedList>::hook& getHook(); + +private: + void wait(); + std::queue messageQueue; + System::Event event; + bool stopped; + + typename IntrusiveLinkedList>::hook hook; +}; + +template +class MesageQueueGuard { +public: + MesageQueueGuard(MessageQueueContainer& container, MessageQueue& messageQueue) : container(container), messageQueue(messageQueue) { + container.addMessageQueue(messageQueue); + } + + MesageQueueGuard(const MesageQueueGuard& other) = delete; + MesageQueueGuard& operator=(const MesageQueueGuard& other) = delete; + + ~MesageQueueGuard() { + container.removeMessageQueue(messageQueue); + } +private: + MessageQueueContainer& container; + MessageQueue& messageQueue; +}; + +template +MessageQueue::MessageQueue(System::Dispatcher& dispatcher) : event(dispatcher), stopped(false) {} + +template +void MessageQueue::wait() { + if (messageQueue.empty()) { + if (stopped) { + throw System::InterruptedException(); + } + + event.clear(); + while (!event.get()) { + event.wait(); + } + } +} + +template +const MessageType& MessageQueue::front() { + wait(); + return messageQueue.front(); +} + +template +void MessageQueue::pop() { + wait(); + messageQueue.pop(); +} + +template +void MessageQueue::push(const MessageType& message) { + messageQueue.push(message); + event.set(); +} + +template +void MessageQueue::stop() { + stopped = true; + event.set(); +} + +template +typename IntrusiveLinkedList>::hook& MessageQueue::getHook() { + return hook; +} + +} diff --git a/src/cryptonote_core/miner.cpp b/src/CryptoNoteCore/Miner.cpp similarity index 90% rename from src/cryptonote_core/miner.cpp rename to src/CryptoNoteCore/Miner.cpp index 8c7cddd0..e0831075 100644 --- a/src/cryptonote_core/miner.cpp +++ b/src/CryptoNoteCore/Miner.cpp @@ -15,7 +15,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "miner.h" +#include "Miner.h" #include #include @@ -29,16 +29,20 @@ #include #include -#include "cryptonote_format_utils.h" -#include "Common/command_line.h" -#include "serialization/SerializationTools.h" +#include "crypto/crypto.h" +#include "Common/CommandLine.h" +#include "Common/StringTools.h" +#include "Serialization/SerializationTools.h" + +#include "CryptoNoteFormatUtils.h" +#include "TransactionExtra.h" using namespace Logging; namespace CryptoNote { - miner::miner(const Currency& currency, i_miner_handler& handler, Logging::ILogger& log) : + miner::miner(const Currency& currency, IMinerHandler& handler, Logging::ILogger& log) : m_currency(currency), logger(log, "miner"), m_stop(true), @@ -69,21 +73,21 @@ namespace CryptoNote m_template = bl; if (BLOCK_MAJOR_VERSION_2 == m_template.majorVersion) { - CryptoNote::tx_extra_merge_mining_tag mm_tag; + CryptoNote::TransactionExtraMergeMiningTag mm_tag; mm_tag.depth = 0; - if (!CryptoNote::get_aux_block_header_hash(m_template, mm_tag.merkle_root)) { + if (!CryptoNote::get_aux_block_header_hash(m_template, mm_tag.merkleRoot)) { return false; } - m_template.parentBlock.minerTx.extra.clear(); - if (!CryptoNote::append_mm_tag_to_extra(m_template.parentBlock.minerTx.extra, mm_tag)) { + m_template.parentBlock.baseTransaction.extra.clear(); + if (!CryptoNote::appendMergeMiningTagToExtra(m_template.parentBlock.baseTransaction.extra, mm_tag)) { return false; } } m_diffic = di; ++m_template_no; - m_starter_nonce = crypto::rand(); + m_starter_nonce = Crypto::rand(); return true; } //----------------------------------------------------------------------------------------------------- @@ -99,7 +103,7 @@ namespace CryptoNote Block bl = boost::value_initialized(); difficulty_type di = 0; uint32_t height; - CryptoNote::blobdata extra_nonce; + CryptoNote::BinaryArray extra_nonce; if(m_extra_messages.size() && m_config.current_extra_message_index < m_extra_messages.size()) { extra_nonce = m_extra_messages[m_config.current_extra_message_index]; @@ -175,9 +179,9 @@ namespace CryptoNote boost::algorithm::trim(extra_vec[i]); if(!extra_vec[i].size()) continue; - std::string buff = Common::base64Decode(extra_vec[i]); + BinaryArray ba = Common::asBinaryArray(Common::base64Decode(extra_vec[i])); if(buff != "0") - m_extra_messages[i] = buff; + m_extra_messages[i] = ba; } m_config_folder_path = boost::filesystem::path(config.extraMessages).parent_path().string(); m_config = boost::value_initialized(); @@ -210,7 +214,7 @@ namespace CryptoNote return !m_stop; } //----------------------------------------------------------------------------------------------------- - bool miner::start(const AccountPublicAddress& adr, size_t threads_count, const boost::thread::attributes& attrs) + bool miner::start(const AccountPublicAddress& adr, size_t threads_count) { if (is_mining()) { logger(ERROR) << "Starting miner but it's already started"; @@ -226,7 +230,7 @@ namespace CryptoNote m_mine_address = adr; m_threads_total = static_cast(threads_count); - m_starter_nonce = crypto::rand(); + m_starter_nonce = Crypto::rand(); if (!m_template_no) { request_block_template(); //lets update block template @@ -235,7 +239,7 @@ namespace CryptoNote m_stop = false; for (uint32_t i = 0; i != threads_count; i++) { - m_threads.push_back(boost::thread(attrs, boost::bind(&miner::worker_thread, this, i))); + m_threads.push_back(std::thread(std::bind(&miner::worker_thread, this, i))); } logger(INFO) << "Mining has started with " << threads_count << " threads, good luck!"; @@ -272,7 +276,7 @@ namespace CryptoNote return true; } //----------------------------------------------------------------------------------------------------- - bool miner::find_nonce_for_given_block(crypto::cn_context &context, Block& bl, const difficulty_type& diffic) { + bool miner::find_nonce_for_given_block(Crypto::cn_context &context, Block& bl, const difficulty_type& diffic) { unsigned nthreads = std::thread::hardware_concurrency(); @@ -280,12 +284,12 @@ namespace CryptoNote std::vector> threads(nthreads); std::atomic foundNonce; std::atomic found(false); - uint32_t startNonce = crypto::rand(); + uint32_t startNonce = Crypto::rand(); for (unsigned i = 0; i < nthreads; ++i) { threads[i] = std::async(std::launch::async, [&, i]() { - crypto::cn_context localctx; - crypto::hash h; + Crypto::cn_context localctx; + Crypto::Hash h; Block lb(bl); // copy to local block @@ -316,7 +320,7 @@ namespace CryptoNote return found; } else { for (; bl.nonce != std::numeric_limits::max(); bl.nonce++) { - crypto::hash h; + Crypto::Hash h; if (!get_block_longhash(context, bl, h)) { return false; } @@ -332,12 +336,8 @@ namespace CryptoNote //----------------------------------------------------------------------------------------------------- void miner::on_synchronized() { - if(m_do_mining) - { - boost::thread::attributes attrs; - attrs.set_stack_size(THREAD_STACK_SIZE); - - start(m_mine_address, m_threads_total, attrs); + if(m_do_mining) { + start(m_mine_address, m_threads_total); } } //----------------------------------------------------------------------------------------------------- @@ -368,7 +368,7 @@ namespace CryptoNote uint32_t nonce = m_starter_nonce + th_local_index; difficulty_type local_diff = 0; uint32_t local_template_ver = 0; - crypto::cn_context context; + Crypto::cn_context context; Block b; while(!m_stop) @@ -397,7 +397,7 @@ namespace CryptoNote } b.nonce = nonce; - crypto::hash h; + Crypto::Hash h; if (!m_stop && !get_block_longhash(context, b, h)) { logger(ERROR) << "Failed to get block long hash"; m_stop = true; diff --git a/src/cryptonote_core/miner.h b/src/CryptoNoteCore/Miner.h old mode 100644 new mode 100755 similarity index 79% rename from src/cryptonote_core/miner.h rename to src/CryptoNoteCore/Miner.h index c52a195e..c40ee425 --- a/src/cryptonote_core/miner.h +++ b/src/CryptoNoteCore/Miner.h @@ -18,31 +18,31 @@ #pragma once #include +#include +#include +#include -#include -#include - -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/Currency.h" -#include "cryptonote_core/difficulty.h" -#include "cryptonote_core/i_miner_handler.h" -#include "cryptonote_core/MinerConfig.h" -#include "cryptonote_core/OnceInInterval.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" +#include "CryptoNoteCore/Currency.h" +#include "CryptoNoteCore/Difficulty.h" +#include "CryptoNoteCore/IMinerHandler.h" +#include "CryptoNoteCore/MinerConfig.h" +#include "CryptoNoteCore/OnceInInterval.h" #include -#include "serialization/ISerializer.h" +#include "Serialization/ISerializer.h" namespace CryptoNote { class miner { public: - miner(const Currency& currency, i_miner_handler& handler, Logging::ILogger& log); + miner(const Currency& currency, IMinerHandler& handler, Logging::ILogger& log); ~miner(); bool init(const MinerConfig& config); bool set_block_template(const Block& bl, const difficulty_type& diffic); bool on_block_chain_update(); - bool start(const AccountPublicAddress& adr, size_t threads_count, const boost::thread::attributes& attrs); + bool start(const AccountPublicAddress& adr, size_t threads_count); uint64_t get_speed(); void send_stop_signal(); bool stop(); @@ -50,7 +50,7 @@ namespace CryptoNote { bool on_idle(); void on_synchronized(); //synchronous analog (for fast calls) - static bool find_nonce_for_given_block(crypto::cn_context &context, Block& bl, const difficulty_type& diffic); + static bool find_nonce_for_given_block(Crypto::cn_context &context, Block& bl, const difficulty_type& diffic); void pause(); void resume(); void do_print_hashrate(bool do_hr); @@ -82,14 +82,14 @@ namespace CryptoNote { std::atomic m_pausers_count; std::mutex m_miners_count_lock; - std::list m_threads; + std::list m_threads; std::mutex m_threads_lock; - i_miner_handler& m_handler; + IMinerHandler& m_handler; AccountPublicAddress m_mine_address; OnceInInterval m_update_block_template_interval; OnceInInterval m_update_merge_hr_interval; - std::vector m_extra_messages; + std::vector m_extra_messages; miner_config m_config; std::string m_config_folder_path; std::atomic m_last_hr_merge_time; diff --git a/src/cryptonote_core/MinerConfig.cpp b/src/CryptoNoteCore/MinerConfig.cpp similarity index 97% rename from src/cryptonote_core/MinerConfig.cpp rename to src/CryptoNoteCore/MinerConfig.cpp index 7290f3d0..2ef6dfc9 100644 --- a/src/cryptonote_core/MinerConfig.cpp +++ b/src/CryptoNoteCore/MinerConfig.cpp @@ -17,7 +17,7 @@ #include "MinerConfig.h" -#include "Common/command_line.h" +#include "Common/CommandLine.h" namespace CryptoNote { @@ -51,4 +51,4 @@ void MinerConfig::init(const boost::program_options::variables_map& options) { } } -} //namespace cryptonote +} //namespace CryptoNote diff --git a/src/cryptonote_core/MinerConfig.h b/src/CryptoNoteCore/MinerConfig.h old mode 100644 new mode 100755 similarity index 97% rename from src/cryptonote_core/MinerConfig.h rename to src/CryptoNoteCore/MinerConfig.h index a220dd29..698888ad --- a/src/cryptonote_core/MinerConfig.h +++ b/src/CryptoNoteCore/MinerConfig.h @@ -36,4 +36,4 @@ public: uint32_t miningThreads; }; -} //namespace cryptonote +} //namespace CryptoNote diff --git a/src/cryptonote_core/OnceInInterval.h b/src/CryptoNoteCore/OnceInInterval.h similarity index 100% rename from src/cryptonote_core/OnceInInterval.h rename to src/CryptoNoteCore/OnceInInterval.h diff --git a/src/cryptonote_core/SwappedMap.cpp b/src/CryptoNoteCore/SwappedMap.cpp similarity index 94% rename from src/cryptonote_core/SwappedMap.cpp rename to src/CryptoNoteCore/SwappedMap.cpp index f7664278..9f4e97f6 100755 --- a/src/cryptonote_core/SwappedMap.cpp +++ b/src/CryptoNoteCore/SwappedMap.cpp @@ -16,3 +16,7 @@ // along with Bytecoin. If not, see . #include "SwappedMap.h" + +namespace { +char suppressMSVCWarningLNK4221; +} diff --git a/src/cryptonote_core/SwappedMap.h b/src/CryptoNoteCore/SwappedMap.h similarity index 97% rename from src/cryptonote_core/SwappedMap.h rename to src/CryptoNoteCore/SwappedMap.h index e9082c86..aba27ec5 100755 --- a/src/cryptonote_core/SwappedMap.h +++ b/src/CryptoNoteCore/SwappedMap.h @@ -109,6 +109,7 @@ private: std::unordered_map::iterator> m_cacheIterators; uint64_t m_cacheHits; uint64_t m_cacheMisses; + uint64_t descriptorsCounter; std::pair* prepare(const Key& key); const std::pair* load(const Key& key, uint64_t offset); @@ -125,6 +126,7 @@ template bool SwappedMap::open(const std::string& it if (poolSize == 0) { return false; } + descriptorsCounter = 0; m_itemsFile.open(itemFileName, std::ios::in | std::ios::out | std::ios::binary); m_indexesFile.open(indexFileName, std::ios::in | std::ios::out | std::ios::binary); @@ -160,7 +162,7 @@ template bool SwappedMap::open(const std::string& it Descriptor descriptor = { itemsFileSize, i }; descriptors.insert(std::make_pair(key, descriptor)); } - + descriptorsCounter++; itemsFileSize += itemSize; } @@ -233,6 +235,7 @@ template void SwappedMap::clear() { m_items.clear(); m_cache.clear(); m_cacheIterators.clear(); + descriptorsCounter = 0; } template void SwappedMap::erase(const_iterator iterator) { @@ -281,7 +284,7 @@ template std::pair::const_iterat throw std::runtime_error("SwappedMap::insert"); } - m_indexesFile.seekp(sizeof(uint64_t) + (sizeof(bool) + sizeof(Key) + sizeof(uint32_t)) * m_descriptors.size()); + m_indexesFile.seekp(sizeof(uint64_t) + (sizeof(bool) + sizeof(Key) + sizeof(uint32_t)) * descriptorsCounter); bool valid = true; m_indexesFile.write(reinterpret_cast(&valid), sizeof valid); if (!m_indexesFile) { @@ -300,17 +303,20 @@ template std::pair::const_iterat } m_indexesFile.seekp(0); - uint64_t count = m_descriptors.size() + 1; + uint64_t count = descriptorsCounter + 1; m_indexesFile.write(reinterpret_cast(&count), sizeof count); if (!m_indexesFile) { throw std::runtime_error("SwappedMap::insert"); } + } - Descriptor descriptor = { m_itemsFileSize, m_descriptors.size() }; + Descriptor descriptor = { m_itemsFileSize, descriptorsCounter }; auto descriptorsInsert = m_descriptors.insert(std::make_pair(value.first, descriptor)); m_itemsFileSize = itemsFileSize; + descriptorsCounter++; + T* newItem = &prepare(value.first)->second; *newItem = value.second; return std::make_pair(const_iterator(this, descriptorsInsert.first), true); diff --git a/src/cryptonote_core/SwappedVector.cpp b/src/CryptoNoteCore/SwappedVector.cpp similarity index 94% rename from src/cryptonote_core/SwappedVector.cpp rename to src/CryptoNoteCore/SwappedVector.cpp index 44a24553..2fb3778d 100755 --- a/src/cryptonote_core/SwappedVector.cpp +++ b/src/CryptoNoteCore/SwappedVector.cpp @@ -16,3 +16,7 @@ // along with Bytecoin. If not, see . #include "SwappedVector.h" + +namespace { +char suppressMSVCWarningLNK4221; +} diff --git a/src/cryptonote_core/SwappedVector.h b/src/CryptoNoteCore/SwappedVector.h similarity index 93% rename from src/cryptonote_core/SwappedVector.h rename to src/CryptoNoteCore/SwappedVector.h index 82e43180..88df10f8 100755 --- a/src/cryptonote_core/SwappedVector.h +++ b/src/CryptoNoteCore/SwappedVector.h @@ -18,6 +18,7 @@ #pragma once #include +#include #include #include #include @@ -25,7 +26,11 @@ #include #include #include -#include "serialization/binary_archive.h" + +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "Serialization/BinaryInputStreamSerializer.h" +#include "Serialization/BinaryOutputStreamSerializer.h" template class SwappedVector { public: @@ -42,7 +47,7 @@ public: const_iterator() { } - const_iterator(SwappedVector* swappedVector, std::size_t index) : m_swappedVector(swappedVector), m_index(index) { + const_iterator(SwappedVector* swappedVector, size_t index) : m_swappedVector(swappedVector), m_index(index) { } bool operator!=(const const_iterator& other) const { @@ -129,13 +134,13 @@ public: return (*m_swappedVector)[m_index + offset]; } - std::size_t index() const { + size_t index() const { return m_index; } private: SwappedVector* m_swappedVector; - std::size_t m_index; + size_t m_index; }; SwappedVector(); @@ -287,10 +292,10 @@ template const T& SwappedVector::operator[](uint64_t index) { m_itemsFile.seekg(m_offsets[index]); T tempItem; - binary_archive archive(m_itemsFile); - if (!do_serialize(archive, tempItem)) { - throw std::runtime_error("SwappedVector::operator[]"); - } + + Common::StdInputStream stream(m_itemsFile); + CryptoNote::BinaryInputStreamSerializer archive(stream); + serialize(tempItem, archive); T* item = prepare(index); std::swap(tempItem, *item); @@ -354,10 +359,10 @@ template void SwappedVector::push_back(const T& item) { } m_itemsFile.seekp(m_itemsFileSize); - binary_archive archive(m_itemsFile); - if (!do_serialize(archive, *const_cast(&item))) { - throw std::runtime_error("SwappedVector::push_back"); - } + + Common::StdOutputStream stream(m_itemsFile); + CryptoNote::BinaryOutputStreamSerializer archive(stream); + serialize(const_cast(item), archive); itemsFileSize = m_itemsFile.tellp(); } diff --git a/src/CryptoNoteCore/Transaction.cpp b/src/CryptoNoteCore/Transaction.cpp new file mode 100755 index 00000000..3af50e2d --- /dev/null +++ b/src/CryptoNoteCore/Transaction.cpp @@ -0,0 +1,532 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "ITransaction.h" +#include "TransactionApiExtra.h" +#include "TransactionUtils.h" + +#include "Account.h" +#include "CryptoNoteCore/CryptoNoteTools.h" +#include "CryptoNoteConfig.h" + +#include +#include +#include + +using namespace Crypto; + +namespace { + + using namespace CryptoNote; + + void derivePublicKey(const AccountPublicAddress& to, const SecretKey& txKey, size_t outputIndex, PublicKey& ephemeralKey) { + KeyDerivation derivation; + generate_key_derivation(to.viewPublicKey, txKey, derivation); + derive_public_key(derivation, outputIndex, to.spendPublicKey, ephemeralKey); + } + +} + +namespace CryptoNote { + + using namespace Crypto; + + //////////////////////////////////////////////////////////////////////// + // class Transaction declaration + //////////////////////////////////////////////////////////////////////// + + class TransactionImpl : public ITransaction { + public: + TransactionImpl(); + TransactionImpl(const BinaryArray& txblob); + TransactionImpl(const CryptoNote::Transaction& tx); + + // ITransactionReader + virtual Hash getTransactionHash() const override; + virtual Hash getTransactionPrefixHash() const override; + virtual PublicKey getTransactionPublicKey() const override; + virtual uint64_t getUnlockTime() const override; + virtual bool getPaymentId(Hash& hash) const override; + virtual bool getExtraNonce(BinaryArray& nonce) const override; + virtual BinaryArray getExtra() const override; + + // inputs + virtual size_t getInputCount() const override; + virtual uint64_t getInputTotalAmount() const override; + virtual TransactionTypes::InputType getInputType(size_t index) const override; + virtual void getInput(size_t index, KeyInput& input) const override; + virtual void getInput(size_t index, MultisignatureInput& input) const override; + + // outputs + virtual size_t getOutputCount() const override; + virtual uint64_t getOutputTotalAmount() const override; + virtual TransactionTypes::OutputType getOutputType(size_t index) const override; + virtual void getOutput(size_t index, KeyOutput& output, uint64_t& amount) const override; + virtual void getOutput(size_t index, MultisignatureOutput& output, uint64_t& amount) const override; + + virtual size_t getRequiredSignaturesCount(size_t index) const override; + virtual bool findOutputsToAccount(const AccountPublicAddress& addr, const SecretKey& viewSecretKey, std::vector& outs, uint64_t& outputAmount) const override; + + // various checks + virtual bool validateInputs() const override; + virtual bool validateOutputs() const override; + virtual bool validateSignatures() const override; + + // get serialized transaction + virtual BinaryArray getTransactionData() const override; + + // ITransactionWriter + + virtual void setUnlockTime(uint64_t unlockTime) override; + virtual void setPaymentId(const Hash& hash) override; + virtual void setExtraNonce(const BinaryArray& nonce) override; + virtual void appendExtra(const BinaryArray& extraData) override; + + // Inputs/Outputs + virtual size_t addInput(const KeyInput& input) override; + virtual size_t addInput(const MultisignatureInput& input) override; + virtual size_t addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, KeyPair& ephKeys) override; + + virtual size_t addOutput(uint64_t amount, const AccountPublicAddress& to) override; + virtual size_t addOutput(uint64_t amount, const std::vector& to, uint32_t requiredSignatures) override; + virtual size_t addOutput(uint64_t amount, const KeyOutput& out) override; + virtual size_t addOutput(uint64_t amount, const MultisignatureOutput& out) override; + + virtual void signInputKey(size_t input, const TransactionTypes::InputKeyInfo& info, const KeyPair& ephKeys) override; + virtual void signInputMultisignature(size_t input, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) override; + virtual void signInputMultisignature(size_t input, const KeyPair& ephemeralKeys) override; + + + // secret key + virtual bool getTransactionSecretKey(SecretKey& key) const override; + virtual void setTransactionSecretKey(const SecretKey& key) override; + + private: + + void invalidateHash(); + + std::vector& getSignatures(size_t input); + + const SecretKey& txSecretKey() const { + if (!secretKey) { + throw std::runtime_error("Operation requires transaction secret key"); + } + return *secretKey; + } + + void checkIfSigning() const { + if (!transaction.signatures.empty()) { + throw std::runtime_error("Cannot perform requested operation, since it will invalidate transaction signatures"); + } + } + + CryptoNote::Transaction transaction; + boost::optional secretKey; + mutable boost::optional transactionHash; + TransactionExtra extra; + }; + + + //////////////////////////////////////////////////////////////////////// + // class Transaction implementation + //////////////////////////////////////////////////////////////////////// + + std::unique_ptr createTransaction() { + return std::unique_ptr(new TransactionImpl()); + } + + std::unique_ptr createTransaction(const BinaryArray& transactionBlob) { + return std::unique_ptr(new TransactionImpl(transactionBlob)); + } + + std::unique_ptr createTransaction(const CryptoNote::Transaction& tx) { + return std::unique_ptr(new TransactionImpl(tx)); + } + + TransactionImpl::TransactionImpl() { + CryptoNote::KeyPair txKeys(CryptoNote::generateKeyPair()); + + TransactionExtraPublicKey pk = { txKeys.publicKey }; + extra.set(pk); + + transaction.version = CURRENT_TRANSACTION_VERSION; + transaction.unlockTime = 0; + transaction.extra = extra.serialize(); + + secretKey = txKeys.secretKey; + } + + TransactionImpl::TransactionImpl(const BinaryArray& ba) { + if (!fromBinaryArray(transaction, ba)) { + throw std::runtime_error("Invalid transaction data"); + } + + extra.parse(transaction.extra); + transactionHash = getBinaryArrayHash(ba); // avoid serialization if we already have blob + } + + TransactionImpl::TransactionImpl(const CryptoNote::Transaction& tx) : transaction(tx) { + extra.parse(transaction.extra); + } + + void TransactionImpl::invalidateHash() { + if (transactionHash.is_initialized()) { + transactionHash = decltype(transactionHash)(); + } + } + + Hash TransactionImpl::getTransactionHash() const { + if (!transactionHash.is_initialized()) { + transactionHash = getObjectHash(transaction); + } + + return transactionHash.get(); + } + + Hash TransactionImpl::getTransactionPrefixHash() const { + return getObjectHash(*static_cast(&transaction)); + } + + PublicKey TransactionImpl::getTransactionPublicKey() const { + PublicKey pk(NULL_PUBLIC_KEY); + extra.getPublicKey(pk); + return pk; + } + + uint64_t TransactionImpl::getUnlockTime() const { + return transaction.unlockTime; + } + + void TransactionImpl::setUnlockTime(uint64_t unlockTime) { + checkIfSigning(); + transaction.unlockTime = unlockTime; + invalidateHash(); + } + + bool TransactionImpl::getTransactionSecretKey(SecretKey& key) const { + if (!secretKey) { + return false; + } + key = reinterpret_cast(secretKey.get()); + return true; + } + + void TransactionImpl::setTransactionSecretKey(const SecretKey& key) { + const auto& sk = reinterpret_cast(key); + PublicKey pk; + PublicKey txPubKey; + + secret_key_to_public_key(sk, pk); + extra.getPublicKey(txPubKey); + + if (txPubKey != pk) { + throw std::runtime_error("Secret transaction key does not match public key"); + } + + secretKey = key; + } + + size_t TransactionImpl::addInput(const KeyInput& input) { + checkIfSigning(); + transaction.inputs.emplace_back(input); + invalidateHash(); + return transaction.inputs.size() - 1; + } + + size_t TransactionImpl::addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, KeyPair& ephKeys) { + checkIfSigning(); + KeyInput input; + input.amount = info.amount; + + generate_key_image_helper( + senderKeys, + info.realOutput.transactionPublicKey, + info.realOutput.outputInTransaction, + ephKeys, + input.keyImage); + + // fill outputs array and use relative offsets + for (const auto& out : info.outputs) { + input.outputIndexes.push_back(out.outputIndex); + } + + input.outputIndexes = absolute_output_offsets_to_relative(input.outputIndexes); + return addInput(input); + } + + size_t TransactionImpl::addInput(const MultisignatureInput& input) { + checkIfSigning(); + transaction.inputs.push_back(input); + invalidateHash(); + return transaction.inputs.size() - 1; + } + + size_t TransactionImpl::addOutput(uint64_t amount, const AccountPublicAddress& to) { + checkIfSigning(); + + KeyOutput outKey; + derivePublicKey(to, txSecretKey(), transaction.outputs.size(), outKey.key); + TransactionOutput out = { amount, outKey }; + transaction.outputs.emplace_back(out); + invalidateHash(); + + return transaction.outputs.size() - 1; + } + + size_t TransactionImpl::addOutput(uint64_t amount, const std::vector& to, uint32_t requiredSignatures) { + checkIfSigning(); + + const auto& txKey = txSecretKey(); + size_t outputIndex = transaction.outputs.size(); + MultisignatureOutput outMsig; + outMsig.requiredSignatureCount = requiredSignatures; + outMsig.keys.resize(to.size()); + + for (size_t i = 0; i < to.size(); ++i) { + derivePublicKey(to[i], txKey, outputIndex, outMsig.keys[i]); + } + + TransactionOutput out = { amount, outMsig }; + transaction.outputs.emplace_back(out); + invalidateHash(); + + return outputIndex; + } + + size_t TransactionImpl::addOutput(uint64_t amount, const KeyOutput& out) { + checkIfSigning(); + size_t outputIndex = transaction.outputs.size(); + TransactionOutput realOut = { amount, out }; + transaction.outputs.emplace_back(realOut); + invalidateHash(); + return outputIndex; + } + + size_t TransactionImpl::addOutput(uint64_t amount, const MultisignatureOutput& out) { + checkIfSigning(); + size_t outputIndex = transaction.outputs.size(); + TransactionOutput realOut = { amount, out }; + transaction.outputs.emplace_back(realOut); + invalidateHash(); + return outputIndex; + } + + void TransactionImpl::signInputKey(size_t index, const TransactionTypes::InputKeyInfo& info, const KeyPair& ephKeys) { + const auto& input = boost::get(getInputChecked(transaction, index, TransactionTypes::InputType::Key)); + Hash prefixHash = getTransactionPrefixHash(); + + std::vector signatures; + std::vector keysPtrs; + + for (const auto& o : info.outputs) { + keysPtrs.push_back(reinterpret_cast(&o.targetKey)); + } + + signatures.resize(keysPtrs.size()); + + generate_ring_signature( + reinterpret_cast(prefixHash), + reinterpret_cast(input.keyImage), + keysPtrs, + reinterpret_cast(ephKeys.secretKey), + info.realOutput.transactionIndex, + signatures.data()); + + getSignatures(index) = signatures; + invalidateHash(); + } + + void TransactionImpl::signInputMultisignature(size_t index, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) { + KeyDerivation derivation; + PublicKey ephemeralPublicKey; + SecretKey ephemeralSecretKey; + + generate_key_derivation( + reinterpret_cast(sourceTransactionKey), + reinterpret_cast(accountKeys.viewSecretKey), + derivation); + + derive_public_key(derivation, outputIndex, + reinterpret_cast(accountKeys.address.spendPublicKey), ephemeralPublicKey); + derive_secret_key(derivation, outputIndex, + reinterpret_cast(accountKeys.spendSecretKey), ephemeralSecretKey); + + Signature signature; + auto txPrefixHash = getTransactionPrefixHash(); + + generate_signature(reinterpret_cast(txPrefixHash), + ephemeralPublicKey, ephemeralSecretKey, signature); + + getSignatures(index).push_back(signature); + invalidateHash(); + } + + void TransactionImpl::signInputMultisignature(size_t index, const KeyPair& ephemeralKeys) { + Signature signature; + auto txPrefixHash = getTransactionPrefixHash(); + + generate_signature(txPrefixHash, ephemeralKeys.publicKey, ephemeralKeys.secretKey, signature); + + getSignatures(index).push_back(signature); + invalidateHash(); + } + + std::vector& TransactionImpl::getSignatures(size_t input) { + // update signatures container size if needed + if (transaction.signatures.size() < transaction.inputs.size()) { + transaction.signatures.resize(transaction.inputs.size()); + } + // check range + if (input >= transaction.signatures.size()) { + throw std::runtime_error("Invalid input index"); + } + + return transaction.signatures[input]; + } + + BinaryArray TransactionImpl::getTransactionData() const { + return toBinaryArray(transaction); + } + + void TransactionImpl::setPaymentId(const Hash& hash) { + checkIfSigning(); + BinaryArray paymentIdBlob; + setPaymentIdToTransactionExtraNonce(paymentIdBlob, reinterpret_cast(hash)); + setExtraNonce(paymentIdBlob); + } + + bool TransactionImpl::getPaymentId(Hash& hash) const { + BinaryArray nonce; + if (getExtraNonce(nonce)) { + Hash paymentId; + if (getPaymentIdFromTransactionExtraNonce(nonce, paymentId)) { + hash = reinterpret_cast(paymentId); + return true; + } + } + return false; + } + + void TransactionImpl::setExtraNonce(const BinaryArray& nonce) { + checkIfSigning(); + TransactionExtraNonce extraNonce = { nonce }; + extra.set(extraNonce); + transaction.extra = extra.serialize(); + invalidateHash(); + } + + void TransactionImpl::appendExtra(const BinaryArray& extraData) { + checkIfSigning(); + transaction.extra.insert( + transaction.extra.end(), extraData.begin(), extraData.end()); + } + + bool TransactionImpl::getExtraNonce(BinaryArray& nonce) const { + TransactionExtraNonce extraNonce; + if (extra.get(extraNonce)) { + nonce = extraNonce.nonce; + return true; + } + return false; + } + + BinaryArray TransactionImpl::getExtra() const { + return transaction.extra; + } + + size_t TransactionImpl::getInputCount() const { + return transaction.inputs.size(); + } + + uint64_t TransactionImpl::getInputTotalAmount() const { + return std::accumulate(transaction.inputs.begin(), transaction.inputs.end(), 0ULL, [](uint64_t val, const TransactionInput& in) { + return val + getTransactionInputAmount(in); }); + } + + TransactionTypes::InputType TransactionImpl::getInputType(size_t index) const { + return getTransactionInputType(getInputChecked(transaction, index)); + } + + void TransactionImpl::getInput(size_t index, KeyInput& input) const { + input = boost::get(getInputChecked(transaction, index, TransactionTypes::InputType::Key)); + } + + void TransactionImpl::getInput(size_t index, MultisignatureInput& input) const { + input = boost::get(getInputChecked(transaction, index, TransactionTypes::InputType::Multisignature)); + } + + size_t TransactionImpl::getOutputCount() const { + return transaction.outputs.size(); + } + + uint64_t TransactionImpl::getOutputTotalAmount() const { + return std::accumulate(transaction.outputs.begin(), transaction.outputs.end(), 0ULL, [](uint64_t val, const TransactionOutput& out) { + return val + out.amount; }); + } + + TransactionTypes::OutputType TransactionImpl::getOutputType(size_t index) const { + return getTransactionOutputType(getOutputChecked(transaction, index).target); + } + + void TransactionImpl::getOutput(size_t index, KeyOutput& output, uint64_t& amount) const { + const auto& out = getOutputChecked(transaction, index, TransactionTypes::OutputType::Key); + output = boost::get(out.target); + amount = out.amount; + } + + void TransactionImpl::getOutput(size_t index, MultisignatureOutput& output, uint64_t& amount) const { + const auto& out = getOutputChecked(transaction, index, TransactionTypes::OutputType::Multisignature); + output = boost::get(out.target); + amount = out.amount; + } + + bool TransactionImpl::findOutputsToAccount(const AccountPublicAddress& addr, const SecretKey& viewSecretKey, std::vector& out, uint64_t& amount) const { + return ::CryptoNote::findOutputsToAccount(transaction, addr, viewSecretKey, out, amount); + } + + size_t TransactionImpl::getRequiredSignaturesCount(size_t index) const { + return ::getRequiredSignaturesCount(getInputChecked(transaction, index)); + } + + bool TransactionImpl::validateInputs() const { + return + check_inputs_types_supported(transaction) && + check_inputs_overflow(transaction) && + checkInputsKeyimagesDiff(transaction) && + checkMultisignatureInputsDiff(transaction); + } + + bool TransactionImpl::validateOutputs() const { + return + check_outs_valid(transaction) && + check_outs_overflow(transaction); + } + + bool TransactionImpl::validateSignatures() const { + if (transaction.signatures.size() < transaction.inputs.size()) { + return false; + } + + for (size_t i = 0; i < transaction.inputs.size(); ++i) { + if (getRequiredSignaturesCount(i) > transaction.signatures[i].size()) { + return false; + } + } + + return true; + } +} diff --git a/src/cryptonote_core/TransactionApi.h b/src/CryptoNoteCore/TransactionApi.h old mode 100644 new mode 100755 similarity index 69% rename from src/cryptonote_core/TransactionApi.h rename to src/CryptoNoteCore/TransactionApi.h index c52eadf7..2753a2a3 --- a/src/cryptonote_core/TransactionApi.h +++ b/src/CryptoNoteCore/TransactionApi.h @@ -20,12 +20,11 @@ #include #include "ITransaction.h" -namespace CryptoNote { - struct Transaction; -} - namespace CryptoNote { std::unique_ptr createTransaction(); - std::unique_ptr createTransaction(const Blob& transactionBlob); - std::unique_ptr createTransaction(const CryptoNote::Transaction& tx); + std::unique_ptr createTransaction(const BinaryArray& transactionBlob); + std::unique_ptr createTransaction(const Transaction& tx); + + std::unique_ptr createTransactionPrefix(const TransactionPrefix& prefix, const Crypto::Hash& transactionHash); + std::unique_ptr createTransactionPrefix(const Transaction& fullTransaction); } diff --git a/src/cryptonote_core/TransactionExtra.h b/src/CryptoNoteCore/TransactionApiExtra.h old mode 100644 new mode 100755 similarity index 63% rename from src/cryptonote_core/TransactionExtra.h rename to src/CryptoNoteCore/TransactionApiExtra.h index 7b607bb9..07af1989 --- a/src/cryptonote_core/TransactionExtra.h +++ b/src/CryptoNoteCore/TransactionApiExtra.h @@ -17,17 +17,11 @@ #pragma once -#include "cryptonote_format_utils.h" +#include "CryptoNoteFormatUtils.h" +#include "TransactionExtra.h" namespace CryptoNote { - inline std::vector stringToVector(const std::string& s) { - std::vector vec( - reinterpret_cast(s.data()), - reinterpret_cast(s.data()) + s.size()); - return vec; - } - class TransactionExtra { public: TransactionExtra() {} @@ -37,7 +31,7 @@ namespace CryptoNote { bool parse(const std::vector& extra) { fields.clear(); - return CryptoNote::parse_tx_extra(extra, fields); + return CryptoNote::parseTransactionExtra(extra, fields); } template @@ -60,35 +54,37 @@ namespace CryptoNote { } } - bool getPublicKey(crypto::public_key& pk) const { - CryptoNote::tx_extra_pub_key extraPk; + template + void append(const T& value) { + fields.push_back(value); + } + + bool getPublicKey(Crypto::PublicKey& pk) const { + CryptoNote::TransactionExtraPublicKey extraPk; if (!get(extraPk)) { return false; } - pk = extraPk.pub_key; + pk = extraPk.publicKey; return true; } std::vector serialize() const { - std::ostringstream out; - binary_archive ar(out); - for (const auto& f : fields) { - ::do_serialize(ar, const_cast(f)); - } - return stringToVector(out.str()); + std::vector extra; + writeTransactionExtra(extra, fields); + return extra; } private: - std::vector::const_iterator find(const std::type_info& t) const { - return std::find_if(fields.begin(), fields.end(), [&t](const CryptoNote::tx_extra_field& f) { return t == f.type(); }); + std::vector::const_iterator find(const std::type_info& t) const { + return std::find_if(fields.begin(), fields.end(), [&t](const CryptoNote::TransactionExtraField& f) { return t == f.type(); }); } - std::vector::iterator find(const std::type_info& t) { - return std::find_if(fields.begin(), fields.end(), [&t](const CryptoNote::tx_extra_field& f) { return t == f.type(); }); + std::vector::iterator find(const std::type_info& t) { + return std::find_if(fields.begin(), fields.end(), [&t](const CryptoNote::TransactionExtraField& f) { return t == f.type(); }); } - std::vector fields; + std::vector fields; }; } diff --git a/src/CryptoNoteCore/TransactionExtra.cpp b/src/CryptoNoteCore/TransactionExtra.cpp new file mode 100755 index 00000000..cef60428 --- /dev/null +++ b/src/CryptoNoteCore/TransactionExtra.cpp @@ -0,0 +1,247 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TransactionExtra.h" + +#include "Common/MemoryInputStream.h" +#include "Common/StreamTools.h" +#include "Common/StringTools.h" +#include "CryptoNoteTools.h" +#include "Serialization/BinaryOutputStreamSerializer.h" +#include "Serialization/BinaryInputStreamSerializer.h" + +using namespace Crypto; +using namespace Common; + +namespace CryptoNote { + +bool parseTransactionExtra(const std::vector &transactionExtra, std::vector &transactionExtraFields) { + transactionExtraFields.clear(); + + if (transactionExtra.empty()) + return true; + + try { + MemoryInputStream iss(transactionExtra.data(), transactionExtra.size()); + BinaryInputStreamSerializer ar(iss); + + int c = 0; + + while (!iss.endOfStream()) { + c = read(iss); + switch (c) { + case TX_EXTRA_TAG_PADDING: { + size_t size = 1; + for (; !iss.endOfStream() && size <= TX_EXTRA_PADDING_MAX_COUNT; ++size) { + if (read(iss) != 0) { + return false; // all bytes should be zero + } + } + + if (size > TX_EXTRA_PADDING_MAX_COUNT) { + return false; + } + + transactionExtraFields.push_back(TransactionExtraPadding{ size }); + break; + } + + case TX_EXTRA_TAG_PUBKEY: { + TransactionExtraPublicKey extraPk; + ar(extraPk.publicKey, "public_key"); + transactionExtraFields.push_back(extraPk); + break; + } + + case TX_EXTRA_NONCE: { + TransactionExtraNonce extraNonce; + uint8_t size = read(iss); + if (size > 0) { + extraNonce.nonce.resize(size); + read(iss, extraNonce.nonce.data(), extraNonce.nonce.size()); + } + + transactionExtraFields.push_back(extraNonce); + break; + } + + case TX_EXTRA_MERGE_MINING_TAG: { + TransactionExtraMergeMiningTag mmTag; + ar(mmTag, "mm_tag"); + transactionExtraFields.push_back(mmTag); + break; + } + } + } + } catch (std::exception &) { + return false; + } + + return true; +} + +struct ExtraSerializerVisitor : public boost::static_visitor { + std::vector& extra; + + ExtraSerializerVisitor(std::vector& tx_extra) + : extra(tx_extra) {} + + bool operator()(const TransactionExtraPadding& t) { + if (t.size > TX_EXTRA_PADDING_MAX_COUNT) { + return false; + } + extra.insert(extra.end(), t.size, 0); + return true; + } + + bool operator()(const TransactionExtraPublicKey& t) { + return addTransactionPublicKeyToExtra(extra, t.publicKey); + } + + bool operator()(const TransactionExtraNonce& t) { + return addExtraNonceToTransactionExtra(extra, t.nonce); + } + + bool operator()(const TransactionExtraMergeMiningTag& t) { + return appendMergeMiningTagToExtra(extra, t); + } +}; + +bool writeTransactionExtra(std::vector& tx_extra, const std::vector& tx_extra_fields) { + ExtraSerializerVisitor visitor(tx_extra); + + for (const auto& tag : tx_extra_fields) { + if (!boost::apply_visitor(visitor, tag)) { + return false; + } + } + + return true; +} + +PublicKey getTransactionPublicKeyFromExtra(const std::vector& tx_extra) { + std::vector tx_extra_fields; + parseTransactionExtra(tx_extra, tx_extra_fields); + + TransactionExtraPublicKey pub_key_field; + if (!findTransactionExtraFieldByType(tx_extra_fields, pub_key_field)) + return boost::value_initialized(); + + return pub_key_field.publicKey; +} + +bool addTransactionPublicKeyToExtra(std::vector& tx_extra, const PublicKey& tx_pub_key) { + tx_extra.resize(tx_extra.size() + 1 + sizeof(PublicKey)); + tx_extra[tx_extra.size() - 1 - sizeof(PublicKey)] = TX_EXTRA_TAG_PUBKEY; + *reinterpret_cast(&tx_extra[tx_extra.size() - sizeof(PublicKey)]) = tx_pub_key; + return true; +} + + +bool addExtraNonceToTransactionExtra(std::vector& tx_extra, const BinaryArray& extra_nonce) { + if (extra_nonce.size() > TX_EXTRA_NONCE_MAX_COUNT) { + return false; + } + + size_t start_pos = tx_extra.size(); + tx_extra.resize(tx_extra.size() + 2 + extra_nonce.size()); + //write tag + tx_extra[start_pos] = TX_EXTRA_NONCE; + //write len + ++start_pos; + tx_extra[start_pos] = static_cast(extra_nonce.size()); + //write data + ++start_pos; + memcpy(&tx_extra[start_pos], extra_nonce.data(), extra_nonce.size()); + return true; +} + +bool appendMergeMiningTagToExtra(std::vector& tx_extra, const TransactionExtraMergeMiningTag& mm_tag) { + BinaryArray blob; + if (!toBinaryArray(mm_tag, blob)) { + return false; + } + + tx_extra.push_back(TX_EXTRA_MERGE_MINING_TAG); + std::copy(reinterpret_cast(blob.data()), reinterpret_cast(blob.data() + blob.size()), std::back_inserter(tx_extra)); + return true; +} + +bool getMergeMiningTagFromExtra(const std::vector& tx_extra, TransactionExtraMergeMiningTag& mm_tag) { + std::vector tx_extra_fields; + parseTransactionExtra(tx_extra, tx_extra_fields); + + return findTransactionExtraFieldByType(tx_extra_fields, mm_tag); +} + +void setPaymentIdToTransactionExtraNonce(std::vector& extra_nonce, const Hash& payment_id) { + extra_nonce.clear(); + extra_nonce.push_back(TX_EXTRA_NONCE_PAYMENT_ID); + const uint8_t* payment_id_ptr = reinterpret_cast(&payment_id); + std::copy(payment_id_ptr, payment_id_ptr + sizeof(payment_id), std::back_inserter(extra_nonce)); +} + +bool getPaymentIdFromTransactionExtraNonce(const std::vector& extra_nonce, Hash& payment_id) { + if (sizeof(Hash) + 1 != extra_nonce.size()) + return false; + if (TX_EXTRA_NONCE_PAYMENT_ID != extra_nonce[0]) + return false; + payment_id = *reinterpret_cast(extra_nonce.data() + 1); + return true; +} + +bool parsePaymentId(const std::string& paymentIdString, Hash& paymentId) { + return Common::podFromHex(paymentIdString, paymentId); +} + +bool createTxExtraWithPaymentId(const std::string& paymentIdString, std::vector& extra) { + Hash paymentIdBin; + + if (!parsePaymentId(paymentIdString, paymentIdBin)) { + return false; + } + + std::vector extraNonce; + CryptoNote::setPaymentIdToTransactionExtraNonce(extraNonce, paymentIdBin); + + if (!CryptoNote::addExtraNonceToTransactionExtra(extra, extraNonce)) { + return false; + } + + return true; +} + +bool getPaymentIdFromTxExtra(const std::vector& extra, Hash& paymentId) { + std::vector tx_extra_fields; + if (!parseTransactionExtra(extra, tx_extra_fields)) { + return false; + } + + TransactionExtraNonce extra_nonce; + if (findTransactionExtraFieldByType(tx_extra_fields, extra_nonce)) { + if (!getPaymentIdFromTransactionExtraNonce(extra_nonce.nonce, paymentId)) { + return false; + } + } else { + return false; + } + + return true; +} + + +} diff --git a/src/CryptoNoteCore/TransactionExtra.h b/src/CryptoNoteCore/TransactionExtra.h new file mode 100755 index 00000000..66cc1448 --- /dev/null +++ b/src/CryptoNoteCore/TransactionExtra.h @@ -0,0 +1,90 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +#include + +#define TX_EXTRA_PADDING_MAX_COUNT 255 +#define TX_EXTRA_NONCE_MAX_COUNT 255 + +#define TX_EXTRA_TAG_PADDING 0x00 +#define TX_EXTRA_TAG_PUBKEY 0x01 +#define TX_EXTRA_NONCE 0x02 +#define TX_EXTRA_MERGE_MINING_TAG 0x03 + +#define TX_EXTRA_NONCE_PAYMENT_ID 0x00 + +namespace CryptoNote { + +struct TransactionExtraPadding { + size_t size; +}; + +struct TransactionExtraPublicKey { + Crypto::PublicKey publicKey; +}; + +struct TransactionExtraNonce { + std::vector nonce; +}; + +struct TransactionExtraMergeMiningTag { + size_t depth; + Crypto::Hash merkleRoot; +}; + +// tx_extra_field format, except tx_extra_padding and tx_extra_pub_key: +// varint tag; +// varint size; +// varint data[]; +typedef boost::variant TransactionExtraField; + + + +template +bool findTransactionExtraFieldByType(const std::vector& tx_extra_fields, T& field) { + auto it = std::find_if(tx_extra_fields.begin(), tx_extra_fields.end(), + [](const TransactionExtraField& f) { return typeid(T) == f.type(); }); + + if (tx_extra_fields.end() == it) + return false; + + field = boost::get(*it); + return true; +} + +bool parseTransactionExtra(const std::vector& tx_extra, std::vector& tx_extra_fields); +bool writeTransactionExtra(std::vector& tx_extra, const std::vector& tx_extra_fields); + +Crypto::PublicKey getTransactionPublicKeyFromExtra(const std::vector& tx_extra); +bool addTransactionPublicKeyToExtra(std::vector& tx_extra, const Crypto::PublicKey& tx_pub_key); +bool addExtraNonceToTransactionExtra(std::vector& tx_extra, const BinaryArray& extra_nonce); +void setPaymentIdToTransactionExtraNonce(BinaryArray& extra_nonce, const Crypto::Hash& payment_id); +bool getPaymentIdFromTransactionExtraNonce(const BinaryArray& extra_nonce, Crypto::Hash& payment_id); +bool appendMergeMiningTagToExtra(std::vector& tx_extra, const TransactionExtraMergeMiningTag& mm_tag); +bool getMergeMiningTagFromExtra(const std::vector& tx_extra, TransactionExtraMergeMiningTag& mm_tag); + +bool createTxExtraWithPaymentId(const std::string& paymentIdString, std::vector& extra); +//returns false if payment id is not found or parse error +bool getPaymentIdFromTxExtra(const std::vector& extra, Crypto::Hash& paymentId); +bool parsePaymentId(const std::string& paymentIdString, Crypto::Hash& paymentId); + +} diff --git a/src/cryptonote_core/tx_pool.cpp b/src/CryptoNoteCore/TransactionPool.cpp similarity index 74% rename from src/cryptonote_core/tx_pool.cpp rename to src/CryptoNoteCore/TransactionPool.cpp index 5f5e8edf..4c74e070 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/CryptoNoteCore/TransactionPool.cpp @@ -15,7 +15,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "tx_pool.h" +#include "TransactionPool.h" #include #include @@ -24,13 +24,16 @@ #include -#include "Common/boost_serialization_helper.h" #include "Common/int-util.h" -#include "Common/util.h" +#include "Common/Util.h" #include "crypto/hash.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/cryptonote_boost_serialization.h" -#include "cryptonote_config.h" + +#include "Serialization/SerializationTools.h" +#include "Serialization/BinarySerializationTools.h" + +#include "CryptoNoteFormatUtils.h" +#include "CryptoNoteTools.h" +#include "CryptoNoteConfig.h" using namespace Logging; @@ -44,17 +47,17 @@ namespace CryptoNote { class BlockTemplate { public: - bool addTransaction(const crypto::hash& txid, const Transaction& tx) { + bool addTransaction(const Crypto::Hash& txid, const Transaction& tx) { if (!canAdd(tx)) return false; - for (const auto& in : tx.vin) { - if (in.type() == typeid(TransactionInputToKey)) { - auto r = m_keyImages.insert(boost::get(in).keyImage); + for (const auto& in : tx.inputs) { + if (in.type() == typeid(KeyInput)) { + auto r = m_keyImages.insert(boost::get(in).keyImage); (void)r; //just to make compiler to shut up assert(r.second); - } else if (in.type() == typeid(TransactionInputMultisignature)) { - const auto& msig = boost::get(in); + } else if (in.type() == typeid(MultisignatureInput)) { + const auto& msig = boost::get(in); auto r = m_usedOutputs.insert(std::make_pair(msig.amount, msig.outputIndex)); (void)r; //just to make compiler to shut up assert(r.second); @@ -65,20 +68,20 @@ namespace CryptoNote { return true; } - const std::vector& getTransactions() const { + const std::vector& getTransactions() const { return m_txHashes; } private: bool canAdd(const Transaction& tx) { - for (const auto& in : tx.vin) { - if (in.type() == typeid(TransactionInputToKey)) { - if (m_keyImages.count(boost::get(in).keyImage)) { + for (const auto& in : tx.inputs) { + if (in.type() == typeid(KeyInput)) { + if (m_keyImages.count(boost::get(in).keyImage)) { return false; } - } else if (in.type() == typeid(TransactionInputMultisignature)) { - const auto& msig = boost::get(in); + } else if (in.type() == typeid(MultisignatureInput)) { + const auto& msig = boost::get(in); if (m_usedOutputs.count(std::make_pair(msig.amount, msig.outputIndex))) { return false; } @@ -87,9 +90,9 @@ namespace CryptoNote { return true; } - std::unordered_set m_keyImages; + std::unordered_set m_keyImages; std::set> m_usedOutputs; - std::vector m_txHashes; + std::vector m_txHashes; }; using CryptoNote::BlockInfo; @@ -109,7 +112,7 @@ namespace CryptoNote { } //--------------------------------------------------------------------------------- - bool tx_memory_pool::add_tx(const Transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t blobSize, tx_verification_context& tvc, bool keptByBlock) { + bool tx_memory_pool::add_tx(const Transaction &tx, /*const Crypto::Hash& tx_prefix_hash,*/ const Crypto::Hash &id, size_t blobSize, tx_verification_context& tvc, bool keptByBlock) { if (!check_inputs_types_supported(tx)) { tvc.m_verifivation_failed = true; return false; @@ -165,6 +168,15 @@ namespace CryptoNote { tvc.m_verifivation_impossible = true; } + if (!keptByBlock) { + bool sizeValid = m_validator.checkTransactionSize(blobSize); + if (!sizeValid) { + logger(INFO) << "tx too big, rejected"; + tvc.m_verifivation_failed = true; + return false; + } + } + std::lock_guard lock(m_transactions_lock); if (!keptByBlock && m_recentlyDeletedTransactions.find(id) != m_recentlyDeletedTransactions.end()) { @@ -194,6 +206,9 @@ namespace CryptoNote { logger(ERROR, BRIGHT_RED) << "transaction already exists at inserting in memory pool"; return false; } + m_paymentIdIndex.add(txd.tx); + m_timestampIndex.add(txd.receiveTime, txd.id); + } tvc.m_added_to_pool = true; @@ -212,13 +227,13 @@ namespace CryptoNote { } //--------------------------------------------------------------------------------- bool tx_memory_pool::add_tx(const Transaction &tx, tx_verification_context& tvc, bool keeped_by_block) { - crypto::hash h = null_hash; + Crypto::Hash h = NULL_HASH; size_t blobSize = 0; - get_transaction_hash(tx, h, blobSize); + getObjectHash(tx, h, blobSize); return add_tx(tx, h, blobSize, tvc, keeped_by_block); } //--------------------------------------------------------------------------------- - bool tx_memory_pool::take_tx(const crypto::hash &id, Transaction &tx, size_t& blobSize, uint64_t& fee) { + bool tx_memory_pool::take_tx(const Crypto::Hash &id, Transaction &tx, size_t& blobSize, uint64_t& fee) { std::lock_guard lock(m_transactions_lock); auto it = m_transactions.find(id); if (it == m_transactions.end()) { @@ -247,9 +262,9 @@ namespace CryptoNote { } } //--------------------------------------------------------------------------------- - void tx_memory_pool::get_difference(const std::vector& known_tx_ids, std::vector& new_tx_ids, std::vector& deleted_tx_ids) const { + void tx_memory_pool::get_difference(const std::vector& known_tx_ids, std::vector& new_tx_ids, std::vector& deleted_tx_ids) const { std::lock_guard lock(m_transactions_lock); - std::unordered_set ready_tx_ids; + std::unordered_set ready_tx_ids; for (const auto& tx : m_transactions) { TransactionCheckInfo checkInfo(tx); if (is_transaction_ready_to_go(tx.tx, checkInfo)) { @@ -257,7 +272,7 @@ namespace CryptoNote { } } - std::unordered_set known_set(known_tx_ids.begin(), known_tx_ids.end()); + std::unordered_set known_set(known_tx_ids.begin(), known_tx_ids.end()); for (auto it = ready_tx_ids.begin(), e = ready_tx_ids.end(); it != e;) { auto known_it = known_set.find(*it); if (known_it != known_set.end()) { @@ -273,15 +288,15 @@ namespace CryptoNote { deleted_tx_ids.assign(known_set.begin(), known_set.end()); } //--------------------------------------------------------------------------------- - bool tx_memory_pool::on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id) { + bool tx_memory_pool::on_blockchain_inc(uint64_t new_block_height, const Crypto::Hash& top_block_id) { return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::on_blockchain_dec(uint64_t new_block_height, const crypto::hash& top_block_id) { + bool tx_memory_pool::on_blockchain_dec(uint64_t new_block_height, const Crypto::Hash& top_block_id) { return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::have_tx(const crypto::hash &id) const { + bool tx_memory_pool::have_tx(const Crypto::Hash &id) const { std::lock_guard lock(m_transactions_lock); if (m_transactions.count(id)) { return true; @@ -315,9 +330,11 @@ namespace CryptoNote { std::lock_guard lock(m_transactions_lock); for (const auto& txd : m_fee_index) { ss << "id: " << txd.id << std::endl; + if (!short_format) { - ss << obj_to_json_str(txd.tx) << std::endl; + ss << storeToJson(txd.tx) << std::endl; } + ss << "blobSize: " << txd.blobSize << std::endl << "fee: " << m_currency.formatAmount(txd.fee) << std::endl << "keptByBlock: " << (txd.keptByBlock ? 'T' : 'F') << std::endl @@ -364,7 +381,7 @@ namespace CryptoNote { } } - bl.txHashes = blockTemplate.getTransactions(); + bl.transactionHashes = blockTemplate.getTransactions(); return true; } //--------------------------------------------------------------------------------- @@ -377,13 +394,18 @@ namespace CryptoNote { if (!boost::filesystem::exists(state_file_path, ec)) { return true; } - bool res = tools::unserialize_obj_from_file(*this, state_file_path); - if (!res) { + + if (!loadFromBinaryFile(*this, state_file_path)) { logger(ERROR) << "Failed to load memory pool from file " << state_file_path; m_transactions.clear(); m_spent_key_images.clear(); m_spentOutputs.clear(); + + m_paymentIdIndex.clear(); + m_timestampIndex.clear(); + } else { + buildIndices(); } removeExpiredTransactions(); @@ -393,19 +415,63 @@ namespace CryptoNote { } //--------------------------------------------------------------------------------- bool tx_memory_pool::deinit() { - if (!tools::create_directories_if_necessary(m_config_folder)) { + if (!Tools::create_directories_if_necessary(m_config_folder)) { logger(INFO) << "Failed to create data directory: " << m_config_folder; return false; } std::string state_file_path = m_config_folder + "/" + m_currency.txPoolFileName(); - bool res = tools::serialize_obj_to_file(*this, state_file_path); - if (!res) { + + if (!storeToBinaryFile(*this, state_file_path)) { logger(INFO) << "Failed to serialize memory pool to file " << state_file_path; } + + m_paymentIdIndex.clear(); + m_timestampIndex.clear(); + return true; } +#define CURRENT_MEMPOOL_ARCHIVE_VER 1 + + void serialize(CryptoNote::tx_memory_pool::TransactionDetails& td, ISerializer& s) { + s(td.id, "id"); + s(td.blobSize, "blobSize"); + s(td.fee, "fee"); + s(td.tx, "tx"); + s(td.maxUsedBlock.height, "maxUsedBlock.height"); + s(td.maxUsedBlock.id, "maxUsedBlock.id"); + s(td.lastFailedBlock.height, "lastFailedBlock.height"); + s(td.lastFailedBlock.id, "lastFailedBlock.id"); + s(td.keptByBlock, "keptByBlock"); + s(reinterpret_cast(td.receiveTime), "receiveTime"); + } + + //--------------------------------------------------------------------------------- + void tx_memory_pool::serialize(ISerializer& s) { + + uint8_t version = CURRENT_MEMPOOL_ARCHIVE_VER; + + s(version, "version"); + + if (version != CURRENT_MEMPOOL_ARCHIVE_VER) { + return; + } + + std::lock_guard lock(m_transactions_lock); + + if (s.type() == ISerializer::INPUT) { + m_transactions.clear(); + readSequence(std::inserter(m_transactions, m_transactions.end()), "transactions", s); + } else { + writeSequence(m_transactions.begin(), m_transactions.end(), "transactions", s); + } + + KV_MEMBER(m_spent_key_images); + KV_MEMBER(m_spentOutputs); + KV_MEMBER(m_recentlyDeletedTransactions); + } + //--------------------------------------------------------------------------------- void tx_memory_pool::on_idle() { m_txCheckInterval.call([this](){ return removeExpiredTransactions(); }); @@ -452,17 +518,19 @@ namespace CryptoNote { tx_memory_pool::tx_container_t::iterator tx_memory_pool::removeTransaction(tx_memory_pool::tx_container_t::iterator i) { removeTransactionInputs(i->id, i->tx, i->keptByBlock); + m_paymentIdIndex.remove(i->tx); + m_timestampIndex.remove(i->receiveTime, i->id); return m_transactions.erase(i); } - bool tx_memory_pool::removeTransactionInputs(const crypto::hash& tx_id, const Transaction& tx, bool keptByBlock) { - for (const auto& in : tx.vin) { - if (in.type() == typeid(TransactionInputToKey)) { - const auto& txin = boost::get(in); + bool tx_memory_pool::removeTransactionInputs(const Crypto::Hash& tx_id, const Transaction& tx, bool keptByBlock) { + for (const auto& in : tx.inputs) { + if (in.type() == typeid(KeyInput)) { + const auto& txin = boost::get(in); auto it = m_spent_key_images.find(txin.keyImage); if (!(it != m_spent_key_images.end())) { logger(ERROR, BRIGHT_RED) << "failed to find transaction input in key images. img=" << txin.keyImage << std::endl << "transaction id = " << tx_id; return false; } - std::unordered_set& key_image_set = it->second; + std::unordered_set& key_image_set = it->second; if (!(!key_image_set.empty())) { logger(ERROR, BRIGHT_RED) << "empty key_image set, img=" << txin.keyImage << std::endl << "transaction id = " << tx_id; return false; } @@ -474,9 +542,9 @@ namespace CryptoNote { //it is now empty hash container for this key_image m_spent_key_images.erase(it); } - } else if (in.type() == typeid(TransactionInputMultisignature)) { + } else if (in.type() == typeid(MultisignatureInput)) { if (!keptByBlock) { - const auto& msig = boost::get(in); + const auto& msig = boost::get(in); auto output = GlobalOutput(msig.amount, msig.outputIndex); assert(m_spentOutputs.count(output)); m_spentOutputs.erase(output); @@ -488,12 +556,12 @@ namespace CryptoNote { } //--------------------------------------------------------------------------------- - bool tx_memory_pool::addTransactionInputs(const crypto::hash& id, const Transaction& tx, bool keptByBlock) { + bool tx_memory_pool::addTransactionInputs(const Crypto::Hash& id, const Transaction& tx, bool keptByBlock) { // should not fail - for (const auto& in : tx.vin) { - if (in.type() == typeid(TransactionInputToKey)) { - const auto& txin = boost::get(in); - std::unordered_set& kei_image_set = m_spent_key_images[txin.keyImage]; + for (const auto& in : tx.inputs) { + if (in.type() == typeid(KeyInput)) { + const auto& txin = boost::get(in); + std::unordered_set& kei_image_set = m_spent_key_images[txin.keyImage]; if (!(keptByBlock || kei_image_set.size() == 0)) { logger(ERROR, BRIGHT_RED) << "internal error: keptByBlock=" << keptByBlock @@ -506,9 +574,9 @@ namespace CryptoNote { logger(ERROR, BRIGHT_RED) << "internal error: try to insert duplicate iterator in key_image set"; return false; } - } else if (in.type() == typeid(TransactionInputMultisignature)) { + } else if (in.type() == typeid(MultisignatureInput)) { if (!keptByBlock) { - const auto& msig = boost::get(in); + const auto& msig = boost::get(in); auto r = m_spentOutputs.insert(GlobalOutput(msig.amount, msig.outputIndex)); (void)r; assert(r.second); @@ -521,14 +589,14 @@ namespace CryptoNote { //--------------------------------------------------------------------------------- bool tx_memory_pool::haveSpentInputs(const Transaction& tx) const { - for (const auto& in : tx.vin) { - if (in.type() == typeid(TransactionInputToKey)) { - const auto& tokey_in = boost::get(in); + for (const auto& in : tx.inputs) { + if (in.type() == typeid(KeyInput)) { + const auto& tokey_in = boost::get(in); if (m_spent_key_images.count(tokey_in.keyImage)) { return true; } - } else if (in.type() == typeid(TransactionInputMultisignature)) { - const auto& msig = boost::get(in); + } else if (in.type() == typeid(MultisignatureInput)) { + const auto& msig = boost::get(in); if (m_spentOutputs.count(GlobalOutput(msig.amount, msig.outputIndex))) { return true; } @@ -544,4 +612,22 @@ namespace CryptoNote { bool tx_memory_pool::removeObserver(ITxPoolObserver* observer) { return m_observerManager.remove(observer); } + + void tx_memory_pool::buildIndices() { + std::lock_guard lock(m_transactions_lock); + for (auto it = m_transactions.begin(); it != m_transactions.end(); it++) { + m_paymentIdIndex.add(it->tx); + m_timestampIndex.add(it->receiveTime, it->id); + } + } + + bool tx_memory_pool::getTransactionIdsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactionIds) { + std::lock_guard lock(m_transactions_lock); + return m_paymentIdIndex.find(paymentId, transactionIds); + } + + bool tx_memory_pool::getTransactionIdsByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& hashes, uint64_t& transactionsNumberWithinTimestamps) { + std::lock_guard lock(m_transactions_lock); + return m_timestampIndex.find(timestampBegin, timestampEnd, transactionsNumberLimit, hashes, transactionsNumberWithinTimestamps); + } } diff --git a/src/cryptonote_core/tx_pool.h b/src/CryptoNoteCore/TransactionPool.h old mode 100644 new mode 100755 similarity index 72% rename from src/cryptonote_core/tx_pool.h rename to src/CryptoNoteCore/TransactionPool.h index d52173a1..c0301da2 --- a/src/cryptonote_core/tx_pool.h +++ b/src/CryptoNoteCore/TransactionPool.h @@ -21,7 +21,6 @@ #include #include -#include #include // multi index @@ -30,21 +29,25 @@ #include #include -#include "Common/util.h" +#include "Common/Util.h" #include "Common/int-util.h" #include "Common/ObserverManager.h" #include "crypto/hash.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/Currency.h" -#include "cryptonote_core/ITimeProvider.h" -#include "cryptonote_core/ITransactionValidator.h" -#include "cryptonote_core/ITxPoolObserver.h" -#include "cryptonote_core/verification_context.h" + +#include "CryptoNoteCore/CryptoNoteBasic.h" +#include "CryptoNoteCore/CryptoNoteBasicImpl.h" +#include "CryptoNoteCore/Currency.h" +#include "CryptoNoteCore/ITimeProvider.h" +#include "CryptoNoteCore/ITransactionValidator.h" +#include "CryptoNoteCore/ITxPoolObserver.h" +#include "CryptoNoteCore/VerificationContext.h" +#include "CryptoNoteCore/BlockchainIndices.h" #include namespace CryptoNote { + class ISerializer; class OnceInTimeInterval { public: @@ -93,14 +96,14 @@ namespace CryptoNote { bool init(const std::string& config_folder); bool deinit(); - bool have_tx(const crypto::hash &id) const; - bool add_tx(const Transaction &tx, const crypto::hash &id, size_t blobSize, tx_verification_context& tvc, bool keeped_by_block); + bool have_tx(const Crypto::Hash &id) const; + bool add_tx(const Transaction &tx, const Crypto::Hash &id, size_t blobSize, tx_verification_context& tvc, bool keeped_by_block); bool add_tx(const Transaction &tx, tx_verification_context& tvc, bool keeped_by_block); //gets tx and remove it from pool - bool take_tx(const crypto::hash &id, Transaction &tx, size_t& blobSize, uint64_t& fee); + bool take_tx(const Crypto::Hash &id, Transaction &tx, size_t& blobSize, uint64_t& fee); - bool on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id); - bool on_blockchain_dec(uint64_t new_block_height, const crypto::hash& top_block_id); + bool on_blockchain_inc(uint64_t new_block_height, const Crypto::Hash& top_block_id); + bool on_blockchain_dec(uint64_t new_block_height, const Crypto::Hash& top_block_id); void lock() const; void unlock() const; @@ -108,11 +111,14 @@ namespace CryptoNote { bool fill_block_template(Block &bl, size_t median_size, size_t maxCumulativeSize, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee); void get_transactions(std::list& txs) const; - void get_difference(const std::vector& known_tx_ids, std::vector& new_tx_ids, std::vector& deleted_tx_ids) const; + void get_difference(const std::vector& known_tx_ids, std::vector& new_tx_ids, std::vector& deleted_tx_ids) const; size_t get_transactions_count() const; std::string print_pool(bool short_format) const; void on_idle(); + bool getTransactionIdsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactionIds); + bool getTransactionIdsByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& hashes, uint64_t& transactionsNumberWithinTimestamps); + template void getTransactions(const t_ids_container& txsIds, t_tx_container& txs, t_missed_container& missedTxs) { std::lock_guard lock(m_transactions_lock); @@ -127,20 +133,7 @@ namespace CryptoNote { } } -#define CURRENT_MEMPOOL_ARCHIVE_VER 11 - - template - void serialize(archive_t & a, const unsigned int version) { - if (version < CURRENT_MEMPOOL_ARCHIVE_VER) { - return; - } - - std::lock_guard lock(m_transactions_lock); - a & m_transactions; - a & m_spent_key_images; - a & m_spentOutputs; - a & m_recentlyDeletedTransactions; - } + void serialize(ISerializer& s); struct TransactionCheckInfo { BlockInfo maxUsedBlock; @@ -148,7 +141,7 @@ namespace CryptoNote { }; struct TransactionDetails : public TransactionCheckInfo { - crypto::hash id; + Crypto::Hash id; Transaction tx; size_t blobSize; uint64_t fee; @@ -179,7 +172,7 @@ namespace CryptoNote { } }; - typedef hashed_unique main_index_t; + typedef hashed_unique main_index_t; typedef ordered_non_unique, TransactionPriorityComparator> fee_index_t; typedef multi_index_container GlobalOutput; typedef std::set GlobalOutputsContainer; - typedef std::unordered_map > key_images_container; + typedef std::unordered_map > key_images_container; // double spending checking - bool addTransactionInputs(const crypto::hash& id, const Transaction& tx, bool keptByBlock); + bool addTransactionInputs(const Crypto::Hash& id, const Transaction& tx, bool keptByBlock); bool haveSpentInputs(const Transaction& tx) const; - bool removeTransactionInputs(const crypto::hash& id, const Transaction& tx, bool keptByBlock); + bool removeTransactionInputs(const Crypto::Hash& id, const Transaction& tx, bool keptByBlock); tx_container_t::iterator removeTransaction(tx_container_t::iterator i); bool removeExpiredTransactions(); bool is_transaction_ready_to_go(const Transaction& tx, TransactionCheckInfo& txd) const; - tools::ObserverManager m_observerManager; + void buildIndices(); + + Tools::ObserverManager m_observerManager; const CryptoNote::Currency& m_currency; OnceInTimeInterval m_txCheckInterval; mutable std::recursive_mutex m_transactions_lock; @@ -213,32 +208,13 @@ namespace CryptoNote { tx_container_t m_transactions; tx_container_t::nth_index<1>::type& m_fee_index; - std::unordered_map m_recentlyDeletedTransactions; + std::unordered_map m_recentlyDeletedTransactions; Logging::LoggerRef logger; -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - friend class blockchain_storage; -#endif + PaymentIdIndex m_paymentIdIndex; + TimestampTransactionsIndex m_timestampIndex; }; } -namespace boost { - namespace serialization { - template - void serialize(archive_t & ar, CryptoNote::tx_memory_pool::TransactionDetails& td, const unsigned int version) { - ar & td.id; - ar & td.blobSize; - ar & td.fee; - ar & td.tx; - ar & td.maxUsedBlock.height; - ar & td.maxUsedBlock.id; - ar & td.lastFailedBlock.height; - ar & td.lastFailedBlock.id; - ar & td.keptByBlock; - ar & td.receiveTime; - } - } -} -BOOST_CLASS_VERSION(CryptoNote::tx_memory_pool, CURRENT_MEMPOOL_ARCHIVE_VER) diff --git a/src/CryptoNoteCore/TransactionPrefixImpl.cpp b/src/CryptoNoteCore/TransactionPrefixImpl.cpp new file mode 100755 index 00000000..532b7684 --- /dev/null +++ b/src/CryptoNoteCore/TransactionPrefixImpl.cpp @@ -0,0 +1,227 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "ITransaction.h" + +#include +#include + +#include "CryptoNoteCore/CryptoNoteBasic.h" +#include "CryptoNoteCore/TransactionApiExtra.h" +#include "TransactionUtils.h" +#include "CryptoNoteCore/CryptoNoteTools.h" + +using namespace Crypto; + +namespace CryptoNote { + +class TransactionPrefixImpl : public ITransactionReader { +public: + TransactionPrefixImpl(); + TransactionPrefixImpl(const TransactionPrefix& prefix, const Hash& transactionHash); + + virtual ~TransactionPrefixImpl() { } + + virtual Hash getTransactionHash() const override; + virtual Hash getTransactionPrefixHash() const override; + virtual PublicKey getTransactionPublicKey() const override; + virtual uint64_t getUnlockTime() const override; + + // extra + virtual bool getPaymentId(Hash& paymentId) const override; + virtual bool getExtraNonce(BinaryArray& nonce) const override; + virtual BinaryArray getExtra() const override; + + // inputs + virtual size_t getInputCount() const override; + virtual uint64_t getInputTotalAmount() const override; + virtual TransactionTypes::InputType getInputType(size_t index) const override; + virtual void getInput(size_t index, KeyInput& input) const override; + virtual void getInput(size_t index, MultisignatureInput& input) const override; + + // outputs + virtual size_t getOutputCount() const override; + virtual uint64_t getOutputTotalAmount() const override; + virtual TransactionTypes::OutputType getOutputType(size_t index) const override; + virtual void getOutput(size_t index, KeyOutput& output, uint64_t& amount) const override; + virtual void getOutput(size_t index, MultisignatureOutput& output, uint64_t& amount) const override; + + // signatures + virtual size_t getRequiredSignaturesCount(size_t inputIndex) const override; + virtual bool findOutputsToAccount(const AccountPublicAddress& addr, const SecretKey& viewSecretKey, std::vector& outs, uint64_t& outputAmount) const override; + + // various checks + virtual bool validateInputs() const override; + virtual bool validateOutputs() const override; + virtual bool validateSignatures() const override; + + // serialized transaction + virtual BinaryArray getTransactionData() const override; + + virtual bool getTransactionSecretKey(SecretKey& key) const override; + +private: + TransactionPrefix m_txPrefix; + TransactionExtra m_extra; + Hash m_txHash; +}; + +TransactionPrefixImpl::TransactionPrefixImpl() { +} + +TransactionPrefixImpl::TransactionPrefixImpl(const TransactionPrefix& prefix, const Hash& transactionHash) { + m_extra.parse(prefix.extra); + + m_txPrefix = prefix; + m_txHash = transactionHash; +} + +Hash TransactionPrefixImpl::getTransactionHash() const { + return m_txHash; +} + +Hash TransactionPrefixImpl::getTransactionPrefixHash() const { + return getObjectHash(m_txPrefix); +} + +PublicKey TransactionPrefixImpl::getTransactionPublicKey() const { + Crypto::PublicKey pk(NULL_PUBLIC_KEY); + m_extra.getPublicKey(pk); + return pk; +} + +uint64_t TransactionPrefixImpl::getUnlockTime() const { + return m_txPrefix.unlockTime; +} + +bool TransactionPrefixImpl::getPaymentId(Hash& hash) const { + BinaryArray nonce; + + if (getExtraNonce(nonce)) { + Crypto::Hash paymentId; + if (getPaymentIdFromTransactionExtraNonce(nonce, paymentId)) { + hash = reinterpret_cast(paymentId); + return true; + } + } + + return false; +} + +bool TransactionPrefixImpl::getExtraNonce(BinaryArray& nonce) const { + TransactionExtraNonce extraNonce; + + if (m_extra.get(extraNonce)) { + nonce = extraNonce.nonce; + return true; + } + + return false; +} + +BinaryArray TransactionPrefixImpl::getExtra() const { + return m_txPrefix.extra; +} + +size_t TransactionPrefixImpl::getInputCount() const { + return m_txPrefix.inputs.size(); +} + +uint64_t TransactionPrefixImpl::getInputTotalAmount() const { + return std::accumulate(m_txPrefix.inputs.begin(), m_txPrefix.inputs.end(), 0ULL, [](uint64_t val, const TransactionInput& in) { + return val + getTransactionInputAmount(in); }); +} + +TransactionTypes::InputType TransactionPrefixImpl::getInputType(size_t index) const { + return getTransactionInputType(getInputChecked(m_txPrefix, index)); +} + +void TransactionPrefixImpl::getInput(size_t index, KeyInput& input) const { + input = boost::get(getInputChecked(m_txPrefix, index, TransactionTypes::InputType::Key)); +} + +void TransactionPrefixImpl::getInput(size_t index, MultisignatureInput& input) const { + input = boost::get(getInputChecked(m_txPrefix, index, TransactionTypes::InputType::Multisignature)); +} + +size_t TransactionPrefixImpl::getOutputCount() const { + return m_txPrefix.outputs.size(); +} + +uint64_t TransactionPrefixImpl::getOutputTotalAmount() const { + return std::accumulate(m_txPrefix.outputs.begin(), m_txPrefix.outputs.end(), 0ULL, [](uint64_t val, const TransactionOutput& out) { + return val + out.amount; }); +} + +TransactionTypes::OutputType TransactionPrefixImpl::getOutputType(size_t index) const { + return getTransactionOutputType(getOutputChecked(m_txPrefix, index).target); +} + +void TransactionPrefixImpl::getOutput(size_t index, KeyOutput& output, uint64_t& amount) const { + const auto& out = getOutputChecked(m_txPrefix, index, TransactionTypes::OutputType::Key); + output = boost::get(out.target); + amount = out.amount; +} + +void TransactionPrefixImpl::getOutput(size_t index, MultisignatureOutput& output, uint64_t& amount) const { + const auto& out = getOutputChecked(m_txPrefix, index, TransactionTypes::OutputType::Multisignature); + output = boost::get(out.target); + amount = out.amount; +} + +size_t TransactionPrefixImpl::getRequiredSignaturesCount(size_t inputIndex) const { + return ::CryptoNote::getRequiredSignaturesCount(getInputChecked(m_txPrefix, inputIndex)); +} + +bool TransactionPrefixImpl::findOutputsToAccount(const AccountPublicAddress& addr, const SecretKey& viewSecretKey, std::vector& outs, uint64_t& outputAmount) const { + return ::CryptoNote::findOutputsToAccount(m_txPrefix, addr, viewSecretKey, outs, outputAmount); +} + +bool TransactionPrefixImpl::validateInputs() const { + return check_inputs_types_supported(m_txPrefix) && + check_inputs_overflow(m_txPrefix) && + checkInputsKeyimagesDiff(m_txPrefix) && + checkMultisignatureInputsDiff(m_txPrefix); +} + +bool TransactionPrefixImpl::validateOutputs() const { + return check_outs_valid(m_txPrefix) && + check_outs_overflow(m_txPrefix); +} + +bool TransactionPrefixImpl::validateSignatures() const { + throw std::system_error(std::make_error_code(std::errc::function_not_supported), "Validating signatures is not supported for transaction prefix"); +} + +BinaryArray TransactionPrefixImpl::getTransactionData() const { + return toBinaryArray(m_txPrefix); +} + +bool TransactionPrefixImpl::getTransactionSecretKey(SecretKey& key) const { + return false; +} + + +std::unique_ptr createTransactionPrefix(const TransactionPrefix& prefix, const Hash& transactionHash) { + return std::unique_ptr (new TransactionPrefixImpl(prefix, transactionHash)); +} + +std::unique_ptr createTransactionPrefix(const Transaction& fullTransaction) { + return std::unique_ptr (new TransactionPrefixImpl(fullTransaction, getObjectHash(fullTransaction))); +} + +} diff --git a/src/CryptoNoteCore/TransactionUtils.cpp b/src/CryptoNoteCore/TransactionUtils.cpp new file mode 100755 index 00000000..31b00619 --- /dev/null +++ b/src/CryptoNoteCore/TransactionUtils.cpp @@ -0,0 +1,164 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TransactionUtils.h" + +#include + +#include "crypto/crypto.h" +#include "CryptoNoteCore/Account.h" +#include "CryptoNoteFormatUtils.h" +#include "TransactionExtra.h" + +using namespace Crypto; + +namespace CryptoNote { + +bool checkInputsKeyimagesDiff(const CryptoNote::TransactionPrefix& tx) { + std::unordered_set ki; + for (const auto& in : tx.inputs) { + if (in.type() == typeid(KeyInput)) { + if (!ki.insert(boost::get(in).keyImage).second) + return false; + } + } + return true; +} + +// TransactionInput helper functions + +size_t getRequiredSignaturesCount(const TransactionInput& in) { + if (in.type() == typeid(KeyInput)) { + return boost::get(in).outputIndexes.size(); + } + if (in.type() == typeid(MultisignatureInput)) { + return boost::get(in).signatureCount; + } + return 0; +} + +uint64_t getTransactionInputAmount(const TransactionInput& in) { + if (in.type() == typeid(KeyInput)) { + return boost::get(in).amount; + } + if (in.type() == typeid(MultisignatureInput)) { + return boost::get(in).amount; + } + return 0; +} + +TransactionTypes::InputType getTransactionInputType(const TransactionInput& in) { + if (in.type() == typeid(KeyInput)) { + return TransactionTypes::InputType::Key; + } + if (in.type() == typeid(MultisignatureInput)) { + return TransactionTypes::InputType::Multisignature; + } + if (in.type() == typeid(BaseInput)) { + return TransactionTypes::InputType::Generating; + } + return TransactionTypes::InputType::Invalid; +} + +const TransactionInput& getInputChecked(const CryptoNote::TransactionPrefix& transaction, size_t index) { + if (transaction.inputs.size() <= index) { + throw std::runtime_error("Transaction input index out of range"); + } + return transaction.inputs[index]; +} + +const TransactionInput& getInputChecked(const CryptoNote::TransactionPrefix& transaction, size_t index, TransactionTypes::InputType type) { + const auto& input = getInputChecked(transaction, index); + if (getTransactionInputType(input) != type) { + throw std::runtime_error("Unexpected transaction input type"); + } + return input; +} + +// TransactionOutput helper functions + +TransactionTypes::OutputType getTransactionOutputType(const TransactionOutputTarget& out) { + if (out.type() == typeid(KeyOutput)) { + return TransactionTypes::OutputType::Key; + } + if (out.type() == typeid(MultisignatureOutput)) { + return TransactionTypes::OutputType::Multisignature; + } + return TransactionTypes::OutputType::Invalid; +} + +const TransactionOutput& getOutputChecked(const CryptoNote::TransactionPrefix& transaction, size_t index) { + if (transaction.outputs.size() <= index) { + throw std::runtime_error("Transaction output index out of range"); + } + return transaction.outputs[index]; +} + +const TransactionOutput& getOutputChecked(const CryptoNote::TransactionPrefix& transaction, size_t index, TransactionTypes::OutputType type) { + const auto& output = getOutputChecked(transaction, index); + if (getTransactionOutputType(output.target) != type) { + throw std::runtime_error("Unexpected transaction output target type"); + } + return output; +} + +bool isOutToKey(const Crypto::PublicKey& spendPublicKey, const Crypto::PublicKey& outKey, const Crypto::KeyDerivation& derivation, size_t keyIndex) { + Crypto::PublicKey pk; + derive_public_key(derivation, keyIndex, spendPublicKey, pk); + return pk == outKey; +} + +bool findOutputsToAccount(const CryptoNote::TransactionPrefix& transaction, const AccountPublicAddress& addr, + const SecretKey& viewSecretKey, std::vector& out, uint64_t& amount) { + AccountKeys keys; + keys.address = addr; + // only view secret key is used, spend key is not needed + keys.viewSecretKey = viewSecretKey; + + Crypto::PublicKey txPubKey = getTransactionPublicKeyFromExtra(transaction.extra); + + amount = 0; + size_t keyIndex = 0; + uint32_t outputIndex = 0; + + Crypto::KeyDerivation derivation; + generate_key_derivation(txPubKey, keys.viewSecretKey, derivation); + + for (const TransactionOutput& o : transaction.outputs) { + assert(o.target.type() == typeid(KeyOutput) || o.target.type() == typeid(MultisignatureOutput)); + if (o.target.type() == typeid(KeyOutput)) { + if (is_out_to_acc(keys, boost::get(o.target), derivation, keyIndex)) { + out.push_back(outputIndex); + amount += o.amount; + } + ++keyIndex; + } else if (o.target.type() == typeid(MultisignatureOutput)) { + const auto& target = boost::get(o.target); + for (const auto& key : target.keys) { + if (isOutToKey(keys.address.spendPublicKey, key, derivation, static_cast(outputIndex))) { + out.push_back(outputIndex); + } + ++keyIndex; + } + } + ++outputIndex; + } + + return true; +} + +} diff --git a/src/CryptoNoteCore/TransactionUtils.h b/src/CryptoNoteCore/TransactionUtils.h new file mode 100755 index 00000000..3555c607 --- /dev/null +++ b/src/CryptoNoteCore/TransactionUtils.h @@ -0,0 +1,42 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "CryptoNoteCore/CryptoNoteBasic.h" +#include "ITransaction.h" + +namespace CryptoNote { + +bool checkInputsKeyimagesDiff(const CryptoNote::TransactionPrefix& tx); + +// TransactionInput helper functions +size_t getRequiredSignaturesCount(const TransactionInput& in); +uint64_t getTransactionInputAmount(const TransactionInput& in); +TransactionTypes::InputType getTransactionInputType(const TransactionInput& in); +const TransactionInput& getInputChecked(const CryptoNote::TransactionPrefix& transaction, size_t index); +const TransactionInput& getInputChecked(const CryptoNote::TransactionPrefix& transaction, size_t index, TransactionTypes::InputType type); + +bool isOutToKey(const Crypto::PublicKey& spendPublicKey, const Crypto::PublicKey& outKey, const Crypto::KeyDerivation& derivation, size_t keyIndex); + +// TransactionOutput helper functions +TransactionTypes::OutputType getTransactionOutputType(const TransactionOutputTarget& out); +const TransactionOutput& getOutputChecked(const CryptoNote::TransactionPrefix& transaction, size_t index); +const TransactionOutput& getOutputChecked(const CryptoNote::TransactionPrefix& transaction, size_t index, TransactionTypes::OutputType type); + +bool findOutputsToAccount(const CryptoNote::TransactionPrefix& transaction, const AccountPublicAddress& addr, + const Crypto::SecretKey& viewSecretKey, std::vector& out, uint64_t& amount); + +} //namespace CryptoNote diff --git a/src/cryptonote_core/UpgradeDetector.cpp b/src/CryptoNoteCore/UpgradeDetector.cpp similarity index 100% rename from src/cryptonote_core/UpgradeDetector.cpp rename to src/CryptoNoteCore/UpgradeDetector.cpp diff --git a/src/cryptonote_core/UpgradeDetector.h b/src/CryptoNoteCore/UpgradeDetector.h old mode 100644 new mode 100755 similarity index 99% rename from src/cryptonote_core/UpgradeDetector.h rename to src/CryptoNoteCore/UpgradeDetector.h index 6661783f..e0d2c417 --- a/src/cryptonote_core/UpgradeDetector.h +++ b/src/CryptoNoteCore/UpgradeDetector.h @@ -21,8 +21,8 @@ #include #include -#include "cryptonote_core/Currency.h" -#include "cryptonote_config.h" +#include "CryptoNoteCore/Currency.h" +#include "CryptoNoteConfig.h" #include namespace CryptoNote { diff --git a/src/cryptonote_core/verification_context.h b/src/CryptoNoteCore/VerificationContext.h old mode 100644 new mode 100755 similarity index 100% rename from src/cryptonote_core/verification_context.h rename to src/CryptoNoteCore/VerificationContext.h diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h old mode 100644 new mode 100755 similarity index 77% rename from src/cryptonote_protocol/cryptonote_protocol_defs.h rename to src/CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h index 6a1904d9..e5b070d6 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h @@ -18,13 +18,12 @@ #pragma once #include -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_protocol/blobdatatype.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" // ISerializer-based serialization -#include "serialization/ISerializer.h" -#include "serialization/SerializationOverloads.h" -#include "cryptonote_core/cryptonote_serialization.h" +#include "Serialization/ISerializer.h" +#include "Serialization/SerializationOverloads.h" +#include "CryptoNoteCore/CryptoNoteSerialization.h" namespace CryptoNote { @@ -36,8 +35,8 @@ namespace CryptoNote /************************************************************************/ struct block_complete_entry { - blobdata block; - std::list txs; + std::string block; + std::vector txs; void serialize(ISerializer& s) { KV_MEMBER(block); @@ -48,14 +47,35 @@ namespace CryptoNote struct BlockFullInfo : public block_complete_entry { - crypto::hash block_id; + Crypto::Hash block_id; void serialize(ISerializer& s) { KV_MEMBER(block_id); KV_MEMBER(block); KV_MEMBER(txs); } + }; + struct TransactionPrefixInfo { + Crypto::Hash txHash; + TransactionPrefix txPrefix; + + void serialize(ISerializer& s) { + KV_MEMBER(txHash); + KV_MEMBER(txPrefix); + } + }; + + struct BlockShortInfo { + Crypto::Hash blockId; + std::string block; + std::vector txPrefixes; + + void serialize(ISerializer& s) { + KV_MEMBER(blockId); + KV_MEMBER(block); + KV_MEMBER(txPrefixes); + } }; /************************************************************************/ @@ -64,7 +84,7 @@ namespace CryptoNote struct NOTIFY_NEW_BLOCK_request { block_complete_entry b; - uint64_t current_blockchain_height; + uint32_t current_blockchain_height; uint32_t hop; void serialize(ISerializer& s) { @@ -85,7 +105,7 @@ namespace CryptoNote /************************************************************************/ struct NOTIFY_NEW_TRANSACTIONS_request { - std::list txs; + std::vector txs; void serialize(ISerializer& s) { KV_MEMBER(txs); @@ -104,8 +124,8 @@ namespace CryptoNote /************************************************************************/ struct NOTIFY_REQUEST_GET_OBJECTS_request { - std::list txs; - std::list blocks; + std::vector txs; + std::vector blocks; void serialize(ISerializer& s) { serializeAsBinary(txs, "txs", s); @@ -121,10 +141,10 @@ namespace CryptoNote struct NOTIFY_RESPONSE_GET_OBJECTS_request { - std::list txs; - std::list blocks; - std::list missed_ids; - uint64_t current_blockchain_height; + std::vector txs; + std::vector blocks; + std::vector missed_ids; + uint32_t current_blockchain_height; void serialize(ISerializer& s) { KV_MEMBER(txs) @@ -147,7 +167,7 @@ namespace CryptoNote struct request { - std::list block_ids; /*IDs of the first 10 blocks are sequential, next goes with pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ + std::vector block_ids; /*IDs of the first 10 blocks are sequential, next goes with pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ void serialize(ISerializer& s) { serializeAsBinary(block_ids, "block_ids", s); @@ -157,9 +177,9 @@ namespace CryptoNote struct NOTIFY_RESPONSE_CHAIN_ENTRY_request { - uint64_t start_height; - uint64_t total_height; - std::list m_block_ids; + uint32_t start_height; + uint32_t total_height; + std::vector m_block_ids; void serialize(ISerializer& s) { KV_MEMBER(start_height) @@ -178,7 +198,7 @@ namespace CryptoNote /* */ /************************************************************************/ struct NOTIFY_REQUEST_TX_POOL_request { - std::vector txs; + std::vector txs; void serialize(ISerializer& s) { serializeAsBinary(txs, "txs", s); diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.cpp b/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.cpp similarity index 64% rename from src/cryptonote_protocol/cryptonote_protocol_handler.cpp rename to src/CryptoNoteProtocol/CryptoNoteProtocolHandler.cpp index 0edd5703..e1d65be3 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.cpp +++ b/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.cpp @@ -15,38 +15,40 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "cryptonote_protocol_handler.h" +#include "CryptoNoteProtocolHandler.h" #include #include #include #include -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/Currency.h" -#include "cryptonote_core/verification_context.h" -#include "p2p/LevinProtocol.h" +#include "CryptoNoteCore/CryptoNoteBasicImpl.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteCore/CryptoNoteTools.h" +#include "CryptoNoteCore/Currency.h" +#include "CryptoNoteCore/VerificationContext.h" +#include "P2p/LevinProtocol.h" using namespace Logging; +using namespace Common; namespace CryptoNote { namespace { template -bool post_notify(i_p2p_endpoint& p2p, typename t_parametr::request& arg, const cryptonote_connection_context& context) { +bool post_notify(IP2pEndpoint& p2p, typename t_parametr::request& arg, const CryptoNoteConnectionContext& context) { return p2p.invoke_notify_to_peer(t_parametr::ID, LevinProtocol::encode(arg), context); } template -void relay_post_notify(i_p2p_endpoint& p2p, typename t_parametr::request& arg, const net_connection_id* excludeConnection = nullptr) { +void relay_post_notify(IP2pEndpoint& p2p, typename t_parametr::request& arg, const net_connection_id* excludeConnection = nullptr) { p2p.relay_notify_to_all(t_parametr::ID, LevinProtocol::encode(arg), excludeConnection); } } -cryptonote_protocol_handler::cryptonote_protocol_handler(const Currency& currency, System::Dispatcher& dispatcher, ICore& rcore, i_p2p_endpoint* p_net_layout, Logging::ILogger& log) : +CryptoNoteProtocolHandler::CryptoNoteProtocolHandler(const Currency& currency, System::Dispatcher& dispatcher, ICore& rcore, IP2pEndpoint* p_net_layout, Logging::ILogger& log) : m_dispatcher(dispatcher), m_currency(currency), m_core(rcore), @@ -62,21 +64,21 @@ cryptonote_protocol_handler::cryptonote_protocol_handler(const Currency& currenc } } -size_t cryptonote_protocol_handler::getPeerCount() const { +size_t CryptoNoteProtocolHandler::getPeerCount() const { return m_peersCount; } -void cryptonote_protocol_handler::set_p2p_endpoint(i_p2p_endpoint* p2p) { +void CryptoNoteProtocolHandler::set_p2p_endpoint(IP2pEndpoint* p2p) { if (p2p) m_p2p = p2p; else m_p2p = &m_p2p_stub; } -void cryptonote_protocol_handler::onConnectionOpened(cryptonote_connection_context& context) { +void CryptoNoteProtocolHandler::onConnectionOpened(CryptoNoteConnectionContext& context) { } -void cryptonote_protocol_handler::onConnectionClosed(cryptonote_connection_context& context) { +void CryptoNoteProtocolHandler::onConnectionClosed(CryptoNoteConnectionContext& context) { bool updated = false; { std::lock_guard lock(m_observedHeightMutex); @@ -89,25 +91,25 @@ void cryptonote_protocol_handler::onConnectionClosed(cryptonote_connection_conte if (updated) { logger(TRACE) << "Observed height updated: " << m_observedHeight; - m_observerManager.notify(&ICryptonoteProtocolObserver::lastKnownBlockHeightUpdated, m_observedHeight); + m_observerManager.notify(&ICryptoNoteProtocolObserver::lastKnownBlockHeightUpdated, m_observedHeight); } - if (context.m_state != cryptonote_connection_context::state_befor_handshake) { + if (context.m_state != CryptoNoteConnectionContext::state_befor_handshake) { m_peersCount--; - m_observerManager.notify(&ICryptonoteProtocolObserver::peerCountUpdated, m_peersCount.load()); + m_observerManager.notify(&ICryptoNoteProtocolObserver::peerCountUpdated, m_peersCount.load()); } } -void cryptonote_protocol_handler::stop() { +void CryptoNoteProtocolHandler::stop() { m_stop = true; } -bool cryptonote_protocol_handler::start_sync(cryptonote_connection_context& context) { +bool CryptoNoteProtocolHandler::start_sync(CryptoNoteConnectionContext& context) { logger(Logging::TRACE) << context << "Starting synchronization"; - if (context.m_state == cryptonote_connection_context::state_synchronizing) { + if (context.m_state == CryptoNoteConnectionContext::state_synchronizing) { NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized(); - m_core.get_short_chain_history(r.block_ids); + r.block_ids = m_core.buildSparseChain(); logger(Logging::TRACE) << context << "-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size(); post_notify(*m_p2p, r, context); } @@ -115,20 +117,20 @@ bool cryptonote_protocol_handler::start_sync(cryptonote_connection_context& cont return true; } -bool cryptonote_protocol_handler::get_stat_info(core_stat_info& stat_inf) { +bool CryptoNoteProtocolHandler::get_stat_info(core_stat_info& stat_inf) { return m_core.get_stat_info(stat_inf); } -void cryptonote_protocol_handler::log_connections() { +void CryptoNoteProtocolHandler::log_connections() { std::stringstream ss; ss << std::setw(25) << std::left << "Remote Host" << std::setw(20) << "Peer id" << std::setw(25) << "Recv/Sent (inactive,sec)" << std::setw(25) << "State" - << std::setw(20) << "Livetime(seconds)" << ENDL; + << std::setw(20) << "Lifetime(seconds)" << ENDL; - m_p2p->for_each_connection([&](const cryptonote_connection_context& cntxt, peerid_type peer_id) { + m_p2p->for_each_connection([&](const CryptoNoteConnectionContext& cntxt, PeerIdType peer_id) { ss << std::setw(25) << std::left << std::string(cntxt.m_is_income ? "[INC]" : "[OUT]") + Common::ipAddressToString(cntxt.m_remote_ip) + ":" + std::to_string(cntxt.m_remote_port) << std::setw(20) << std::hex << peer_id @@ -139,24 +141,24 @@ void cryptonote_protocol_handler::log_connections() { logger(INFO) << "Connections: " << ENDL << ss.str(); } -uint64_t cryptonote_protocol_handler::get_current_blockchain_height() { - uint64_t height; - crypto::hash blockId; +uint32_t CryptoNoteProtocolHandler::get_current_blockchain_height() { + uint32_t height; + Crypto::Hash blockId; m_core.get_blockchain_top(height, blockId); return height; } -bool cryptonote_protocol_handler::process_payload_sync_data(const CORE_SYNC_DATA& hshd, cryptonote_connection_context& context, bool is_inital) { - if (context.m_state == cryptonote_connection_context::state_befor_handshake && !is_inital) +bool CryptoNoteProtocolHandler::process_payload_sync_data(const CORE_SYNC_DATA& hshd, CryptoNoteConnectionContext& context, bool is_inital) { + if (context.m_state == CryptoNoteConnectionContext::state_befor_handshake && !is_inital) return true; - if (context.m_state == cryptonote_connection_context::state_synchronizing) { + if (context.m_state == CryptoNoteConnectionContext::state_synchronizing) { } else if (m_core.have_block(hshd.top_id)) { if (is_inital) { on_connection_synchronized(); - context.m_state = cryptonote_connection_context::state_pool_sync_required; + context.m_state = CryptoNoteConnectionContext::state_pool_sync_required; } else { - context.m_state = cryptonote_connection_context::state_normal; + context.m_state = CryptoNoteConnectionContext::state_normal; } } else { int64_t diff = static_cast(hshd.current_height) - static_cast(get_current_blockchain_height()); @@ -169,7 +171,7 @@ bool cryptonote_protocol_handler::process_payload_sync_data(const CORE_SYNC_DATA logger(Logging::DEBUGGING) << "Remote top block height: " << hshd.current_height << ", id: " << hshd.top_id; //let the socket to send response to handshake, but request callback, to let send request data after response logger(Logging::TRACE) << context << "requesting synchronization"; - context.m_state = cryptonote_connection_context::state_sync_required; + context.m_state = CryptoNoteConnectionContext::state_sync_required; } updateObservedHeight(hshd.current_height, context); @@ -177,21 +179,23 @@ bool cryptonote_protocol_handler::process_payload_sync_data(const CORE_SYNC_DATA if (is_inital) { m_peersCount++; - m_observerManager.notify(&ICryptonoteProtocolObserver::peerCountUpdated, m_peersCount.load()); + m_observerManager.notify(&ICryptoNoteProtocolObserver::peerCountUpdated, m_peersCount.load()); } return true; } -bool cryptonote_protocol_handler::get_payload_sync_data(CORE_SYNC_DATA& hshd) { - m_core.get_blockchain_top(hshd.current_height, hshd.top_id); +bool CryptoNoteProtocolHandler::get_payload_sync_data(CORE_SYNC_DATA& hshd) { + uint32_t current_height; + m_core.get_blockchain_top(current_height, hshd.top_id); + hshd.current_height = current_height; hshd.current_height += 1; return true; } template -int notifyAdaptor(const std::string& reqBuf, cryptonote_connection_context& ctx, Handler handler) { +int notifyAdaptor(const BinaryArray& reqBuf, CryptoNoteConnectionContext& ctx, Handler handler) { typedef typename Command::request Request; int command = Command::ID; @@ -206,18 +210,18 @@ int notifyAdaptor(const std::string& reqBuf, cryptonote_connection_context& ctx, #define HANDLE_NOTIFY(CMD, Handler) case CMD::ID: { ret = notifyAdaptor(in, ctx, std::bind(Handler, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); break; } -int cryptonote_protocol_handler::handleCommand(bool is_notify, int command, const std::string& in, std::string& out, cryptonote_connection_context& ctx, bool& handled) { +int CryptoNoteProtocolHandler::handleCommand(bool is_notify, int command, const BinaryArray& in, BinaryArray& out, CryptoNoteConnectionContext& ctx, bool& handled) { int ret = 0; handled = true; switch (command) { - HANDLE_NOTIFY(NOTIFY_NEW_BLOCK, &cryptonote_protocol_handler::handle_notify_new_block) - HANDLE_NOTIFY(NOTIFY_NEW_TRANSACTIONS, &cryptonote_protocol_handler::handle_notify_new_transactions) - HANDLE_NOTIFY(NOTIFY_REQUEST_GET_OBJECTS, &cryptonote_protocol_handler::handle_request_get_objects) - HANDLE_NOTIFY(NOTIFY_RESPONSE_GET_OBJECTS, &cryptonote_protocol_handler::handle_response_get_objects) - HANDLE_NOTIFY(NOTIFY_REQUEST_CHAIN, &cryptonote_protocol_handler::handle_request_chain) - HANDLE_NOTIFY(NOTIFY_RESPONSE_CHAIN_ENTRY, &cryptonote_protocol_handler::handle_response_chain_entry) - HANDLE_NOTIFY(NOTIFY_REQUEST_TX_POOL, &cryptonote_protocol_handler::handleRequestTxPool) + HANDLE_NOTIFY(NOTIFY_NEW_BLOCK, &CryptoNoteProtocolHandler::handle_notify_new_block) + HANDLE_NOTIFY(NOTIFY_NEW_TRANSACTIONS, &CryptoNoteProtocolHandler::handle_notify_new_transactions) + HANDLE_NOTIFY(NOTIFY_REQUEST_GET_OBJECTS, &CryptoNoteProtocolHandler::handle_request_get_objects) + HANDLE_NOTIFY(NOTIFY_RESPONSE_GET_OBJECTS, &CryptoNoteProtocolHandler::handle_response_get_objects) + HANDLE_NOTIFY(NOTIFY_REQUEST_CHAIN, &CryptoNoteProtocolHandler::handle_request_chain) + HANDLE_NOTIFY(NOTIFY_RESPONSE_CHAIN_ENTRY, &CryptoNoteProtocolHandler::handle_response_chain_entry) + HANDLE_NOTIFY(NOTIFY_REQUEST_TX_POOL, &CryptoNoteProtocolHandler::handleRequestTxPool) default: handled = false; @@ -228,32 +232,32 @@ int cryptonote_protocol_handler::handleCommand(bool is_notify, int command, cons #undef HANDLE_NOTIFY -int cryptonote_protocol_handler::handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& context) { +int CryptoNoteProtocolHandler::handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, CryptoNoteConnectionContext& context) { logger(Logging::TRACE) << context << "NOTIFY_NEW_BLOCK (hop " << arg.hop << ")"; updateObservedHeight(arg.current_blockchain_height, context); context.m_remote_blockchain_height = arg.current_blockchain_height; - if (context.m_state != cryptonote_connection_context::state_normal) { + if (context.m_state != CryptoNoteConnectionContext::state_normal) { return 1; } for (auto tx_blob_it = arg.b.txs.begin(); tx_blob_it != arg.b.txs.end(); tx_blob_it++) { CryptoNote::tx_verification_context tvc = boost::value_initialized(); - m_core.handle_incoming_tx(*tx_blob_it, tvc, true); + m_core.handle_incoming_tx(asBinaryArray(*tx_blob_it), tvc, true); if (tvc.m_verifivation_failed) { logger(Logging::INFO) << context << "Block verification failed: transaction verification failed, dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } } block_verification_context bvc = boost::value_initialized(); - m_core.handle_incoming_block_blob(arg.b.block, bvc, true, false); + m_core.handle_incoming_block_blob(asBinaryArray(arg.b.block), bvc, true, false); if (bvc.m_verifivation_failed) { logger(Logging::DEBUGGING) << context << "Block verification failed, dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } if (bvc.m_added_to_main_chain) { @@ -261,27 +265,29 @@ int cryptonote_protocol_handler::handle_notify_new_block(int command, NOTIFY_NEW //TODO: Add here announce protocol usage relay_post_notify(*m_p2p, arg, &context.m_connection_id); // relay_block(arg, context); + + if (bvc.m_switched_to_alt_chain) { + requestMissingPoolTransactions(context); + } } else if (bvc.m_marked_as_orphaned) { - context.m_state = cryptonote_connection_context::state_synchronizing; + context.m_state = CryptoNoteConnectionContext::state_synchronizing; NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized(); - m_core.get_short_chain_history(r.block_ids); + r.block_ids = m_core.buildSparseChain(); logger(Logging::TRACE) << context << "-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size(); post_notify(*m_p2p, r, context); - } else if (bvc.m_switched_to_alt_chain) { - requestMissingPoolTransactions(context); } return 1; } -int cryptonote_protocol_handler::handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& context) { +int CryptoNoteProtocolHandler::handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, CryptoNoteConnectionContext& context) { logger(Logging::TRACE) << context << "NOTIFY_NEW_TRANSACTIONS"; - if (context.m_state != cryptonote_connection_context::state_normal) + if (context.m_state != CryptoNoteConnectionContext::state_normal) return 1; for (auto tx_blob_it = arg.txs.begin(); tx_blob_it != arg.txs.end();) { CryptoNote::tx_verification_context tvc = boost::value_initialized(); - m_core.handle_incoming_tx(*tx_blob_it, tvc, false); + m_core.handle_incoming_tx(asBinaryArray(*tx_blob_it), tvc, false); if (tvc.m_verifivation_failed) { logger(Logging::INFO) << context << "Tx verification failed"; } @@ -300,12 +306,12 @@ int cryptonote_protocol_handler::handle_notify_new_transactions(int command, NOT return true; } -int cryptonote_protocol_handler::handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context) { +int CryptoNoteProtocolHandler::handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, CryptoNoteConnectionContext& context) { logger(Logging::TRACE) << context << "NOTIFY_REQUEST_GET_OBJECTS"; NOTIFY_RESPONSE_GET_OBJECTS::request rsp; if (!m_core.handle_get_objects(arg, rsp)) { logger(Logging::ERROR) << context << "failed to handle request NOTIFY_REQUEST_GET_OBJECTS, dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; } logger(Logging::TRACE) << context << "-->>NOTIFY_RESPONSE_GET_OBJECTS: blocks.size()=" << rsp.blocks.size() << ", txs.size()=" << rsp.txs.size() << ", rsp.m_current_blockchain_height=" << rsp.current_blockchain_height << ", missed_ids.size()=" << rsp.missed_ids.size(); @@ -313,13 +319,13 @@ int cryptonote_protocol_handler::handle_request_get_objects(int command, NOTIFY_ return 1; } -int cryptonote_protocol_handler::handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, cryptonote_connection_context& context) { +int CryptoNoteProtocolHandler::handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, CryptoNoteConnectionContext& context) { logger(Logging::TRACE) << context << "NOTIFY_RESPONSE_GET_OBJECTS"; if (context.m_last_response_height > arg.current_blockchain_height) { logger(Logging::ERROR) << context << "sent wrong NOTIFY_HAVE_OBJECTS: arg.m_current_blockchain_height=" << arg.current_blockchain_height << " < m_last_response_height=" << context.m_last_response_height << ", dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } @@ -331,17 +337,17 @@ int cryptonote_protocol_handler::handle_response_get_objects(int command, NOTIFY for (const block_complete_entry& block_entry : arg.blocks) { ++count; Block b; - if (!parse_and_validate_block_from_blob(block_entry.block, b)) { + if (!fromBinaryArray(b, asBinaryArray(block_entry.block))) { logger(Logging::ERROR) << context << "sent wrong block: failed to parse and validate block: \r\n" - << blobToHex(block_entry.block) << "\r\n dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + << toHex(asBinaryArray(block_entry.block)) << "\r\n dropping connection"; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } //to avoid concurrency in core between connections, suspend connections which delivered block later then first one if (count == 2) { if (m_core.have_block(get_block_hash(b))) { - context.m_state = cryptonote_connection_context::state_idle; + context.m_state = CryptoNoteConnectionContext::state_idle; context.m_needed_objects.clear(); context.m_requested_objects.clear(); logger(Logging::DEBUGGING) << context << "Connection set to idle state."; @@ -351,15 +357,15 @@ int cryptonote_protocol_handler::handle_response_get_objects(int command, NOTIFY auto req_it = context.m_requested_objects.find(get_block_hash(b)); if (req_it == context.m_requested_objects.end()) { - logger(Logging::ERROR) << context << "sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << Common::podToHex(get_blob_hash(block_entry.block)) + logger(Logging::ERROR) << context << "sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << Common::podToHex(getBinaryArrayHash(asBinaryArray(block_entry.block))) << " wasn't requested, dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } - if (b.txHashes.size() != block_entry.txs.size()) { - logger(Logging::ERROR) << context << "sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << Common::podToHex(get_blob_hash(block_entry.block)) - << ", txHashes.size()=" << b.txHashes.size() << " mismatch with block_complete_entry.m_txs.size()=" << block_entry.txs.size() << ", dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + if (b.transactionHashes.size() != block_entry.txs.size()) { + logger(Logging::ERROR) << context << "sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << Common::podToHex(getBinaryArrayHash(asBinaryArray(block_entry.block))) + << ", transactionHashes.size()=" << b.transactionHashes.size() << " mismatch with block_complete_entry.m_txs.size()=" << block_entry.txs.size() << ", dropping connection"; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } @@ -370,7 +376,7 @@ int cryptonote_protocol_handler::handle_response_get_objects(int command, NOTIFY logger(Logging::ERROR, Logging::BRIGHT_RED) << context << "returned not all requested objects (context.m_requested_objects.size()=" << context.m_requested_objects.size() << "), dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } @@ -379,37 +385,25 @@ int cryptonote_protocol_handler::handle_response_get_objects(int command, NOTIFY BOOST_SCOPE_EXIT_ALL(this) { m_core.update_block_template_and_resume_mining(); }; - auto currentContext = m_dispatcher.getCurrentContext(); - - auto resultFuture = std::async(std::launch::async, [&]{ - int result = processObjects(context, arg.blocks); - m_dispatcher.remoteSpawn([&] { - m_dispatcher.pushContext(currentContext); - }); - - return result; - }); - - m_dispatcher.dispatch(); - int result = resultFuture.get(); + int result = processObjects(context, arg.blocks); if (result != 0) { return result; } } - uint64_t height; - crypto::hash top; + uint32_t height; + Crypto::Hash top; m_core.get_blockchain_top(height, top); logger(DEBUGGING, BRIGHT_GREEN) << "Local blockchain updated, new height = " << height; - if (!m_stop && context.m_state == cryptonote_connection_context::state_synchronizing) { + if (!m_stop && context.m_state == CryptoNoteConnectionContext::state_synchronizing) { request_missing_objects(context, true); } return 1; } -int cryptonote_protocol_handler::processObjects(cryptonote_connection_context& context, const std::list& blocks) { +int CryptoNoteProtocolHandler::processObjects(CryptoNoteConnectionContext& context, const std::vector& blocks) { for (const block_complete_entry& block_entry : blocks) { if (m_stop) { @@ -419,32 +413,34 @@ int cryptonote_protocol_handler::processObjects(cryptonote_connection_context& c //process transactions for (auto& tx_blob : block_entry.txs) { tx_verification_context tvc = boost::value_initialized(); - m_core.handle_incoming_tx(tx_blob, tvc, true); + m_core.handle_incoming_tx(asBinaryArray(tx_blob), tvc, true); if (tvc.m_verifivation_failed) { logger(Logging::ERROR) << context << "transaction verification failed on NOTIFY_RESPONSE_GET_OBJECTS, \r\ntx_id = " - << Common::podToHex(get_blob_hash(tx_blob)) << ", dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + << Common::podToHex(getBinaryArrayHash(asBinaryArray(tx_blob))) << ", dropping connection"; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } } // process block block_verification_context bvc = boost::value_initialized(); - m_core.handle_incoming_block_blob(block_entry.block, bvc, false, false); + m_core.handle_incoming_block_blob(asBinaryArray(block_entry.block), bvc, false, false); if (bvc.m_verifivation_failed) { logger(Logging::DEBUGGING) << context << "Block verification failed, dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } else if (bvc.m_marked_as_orphaned) { logger(Logging::INFO) << context << "Block received at sync phase was marked as orphaned, dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } else if (bvc.m_already_exists) { logger(Logging::DEBUGGING) << context << "Block already exists, switching to idle state"; - context.m_state = cryptonote_connection_context::state_idle; + context.m_state = CryptoNoteConnectionContext::state_idle; return 1; } + + m_dispatcher.yield(); } return 0; @@ -452,23 +448,34 @@ int cryptonote_protocol_handler::processObjects(cryptonote_connection_context& c } -bool cryptonote_protocol_handler::on_idle() { +bool CryptoNoteProtocolHandler::on_idle() { return m_core.on_idle(); } -int cryptonote_protocol_handler::handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, cryptonote_connection_context& context) { +int CryptoNoteProtocolHandler::handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, CryptoNoteConnectionContext& context) { logger(Logging::TRACE) << context << "NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << arg.block_ids.size(); - NOTIFY_RESPONSE_CHAIN_ENTRY::request r; - if (!m_core.find_blockchain_supplement(arg.block_ids, r)) { - logger(Logging::ERROR) << context << "Failed to handle NOTIFY_REQUEST_CHAIN."; + + if (arg.block_ids.empty()) { + logger(Logging::ERROR, Logging::BRIGHT_RED) << context << "Failed to handle NOTIFY_REQUEST_CHAIN. block_ids is empty"; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } + + if (arg.block_ids.back() != m_core.getBlockIdByHeight(0)) { + logger(Logging::ERROR) << context << "Failed to handle NOTIFY_REQUEST_CHAIN. block_ids doesn't end with genesis block ID"; + context.m_state = CryptoNoteConnectionContext::state_shutdown; + return 1; + } + + NOTIFY_RESPONSE_CHAIN_ENTRY::request r; + r.m_block_ids = m_core.findBlockchainSupplement(arg.block_ids, BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT, r.total_height, r.start_height); + logger(Logging::TRACE) << context << "-->>NOTIFY_RESPONSE_CHAIN_ENTRY: m_start_height=" << r.start_height << ", m_total_height=" << r.total_height << ", m_block_ids.size()=" << r.m_block_ids.size(); post_notify(*m_p2p, r, context); return 1; } -bool cryptonote_protocol_handler::request_missing_objects(cryptonote_connection_context& context, bool check_having_blocks) { +bool CryptoNoteProtocolHandler::request_missing_objects(CryptoNoteConnectionContext& context, bool check_having_blocks) { if (context.m_needed_objects.size()) { //we know objects that we need, request this objects NOTIFY_REQUEST_GET_OBJECTS::request req; @@ -488,7 +495,7 @@ bool cryptonote_protocol_handler::request_missing_objects(cryptonote_connection_ } else if (context.m_last_response_height < context.m_remote_blockchain_height - 1) {//we have to fetch more objects ids, request blockchain entry NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized(); - m_core.get_short_chain_history(r.block_ids); + r.block_ids = m_core.buildSparseChain(); logger(Logging::TRACE) << context << "-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size(); post_notify(*m_p2p, r, context); } else { @@ -508,14 +515,14 @@ bool cryptonote_protocol_handler::request_missing_objects(cryptonote_connection_ requestMissingPoolTransactions(context); - context.m_state = cryptonote_connection_context::state_normal; + context.m_state = CryptoNoteConnectionContext::state_normal; logger(Logging::INFO, Logging::BRIGHT_GREEN) << context << "SYNCHRONIZED OK"; on_connection_synchronized(); } return true; } -bool cryptonote_protocol_handler::on_connection_synchronized() { +bool CryptoNoteProtocolHandler::on_connection_synchronized() { bool val_expected = false; if (m_synchronized.compare_exchange_strong(val_expected, true)) { logger(Logging::INFO) << ENDL << "**********************************************************************" << ENDL @@ -528,22 +535,21 @@ bool cryptonote_protocol_handler::on_connection_synchronized() { << "**********************************************************************"; m_core.on_synchronized(); - uint64_t height; - crypto::hash hash; - if (m_core.get_blockchain_top(height, hash)) { - m_observerManager.notify(&ICryptonoteProtocolObserver::blockchainSynchronized, height); - } + uint32_t height; + Crypto::Hash hash; + m_core.get_blockchain_top(height, hash); + m_observerManager.notify(&ICryptoNoteProtocolObserver::blockchainSynchronized, height); } return true; } -int cryptonote_protocol_handler::handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context) { +int CryptoNoteProtocolHandler::handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, CryptoNoteConnectionContext& context) { logger(Logging::TRACE) << context << "NOTIFY_RESPONSE_CHAIN_ENTRY: m_block_ids.size()=" << arg.m_block_ids.size() << ", m_start_height=" << arg.start_height << ", m_total_height=" << arg.total_height; if (!arg.m_block_ids.size()) { logger(Logging::ERROR) << context << "sent empty m_block_ids, dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } @@ -552,12 +558,12 @@ int cryptonote_protocol_handler::handle_response_chain_entry(int command, NOTIFY << context << "sent m_block_ids starting from unknown id: " << Common::podToHex(arg.m_block_ids.front()) << " , dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } context.m_remote_blockchain_height = arg.total_height; - context.m_last_response_height = arg.start_height + arg.m_block_ids.size() - 1; + context.m_last_response_height = arg.start_height + static_cast(arg.m_block_ids.size()) - 1; if (context.m_last_response_height > context.m_remote_blockchain_height) { logger(Logging::ERROR) @@ -565,7 +571,7 @@ int cryptonote_protocol_handler::handle_response_chain_entry(int command, NOTIFY << "sent wrong NOTIFY_RESPONSE_CHAIN_ENTRY, with \r\nm_total_height=" << arg.total_height << "\r\nm_start_height=" << arg.start_height << "\r\nm_block_ids.size()=" << arg.m_block_ids.size(); - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; } for (auto& bl_id : arg.m_block_ids) { @@ -577,18 +583,18 @@ int cryptonote_protocol_handler::handle_response_chain_entry(int command, NOTIFY return 1; } -int cryptonote_protocol_handler::handleRequestTxPool(int command, NOTIFY_REQUEST_TX_POOL::request& arg, - cryptonote_connection_context& context) { +int CryptoNoteProtocolHandler::handleRequestTxPool(int command, NOTIFY_REQUEST_TX_POOL::request& arg, + CryptoNoteConnectionContext& context) { logger(Logging::TRACE) << context << "NOTIFY_REQUEST_TX_POOL: txs.size() = " << arg.txs.size(); std::vector addedTransactions; - std::vector deletedTransactions; + std::vector deletedTransactions; m_core.getPoolChanges(arg.txs, addedTransactions, deletedTransactions); if (!addedTransactions.empty()) { NOTIFY_NEW_TRANSACTIONS::request notification; for (auto& tx : addedTransactions) { - notification.txs.push_back(tx_to_blob(tx)); + notification.txs.push_back(asString(toBinaryArray(tx))); } bool ok = post_notify(*m_p2p, notification, context); @@ -601,17 +607,17 @@ int cryptonote_protocol_handler::handleRequestTxPool(int command, NOTIFY_REQUEST } -void cryptonote_protocol_handler::relay_block(NOTIFY_NEW_BLOCK::request& arg) { +void CryptoNoteProtocolHandler::relay_block(NOTIFY_NEW_BLOCK::request& arg) { auto buf = LevinProtocol::encode(arg); m_p2p->externalRelayNotifyToAll(NOTIFY_NEW_BLOCK::ID, buf); } -void cryptonote_protocol_handler::relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg) { +void CryptoNoteProtocolHandler::relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg) { auto buf = LevinProtocol::encode(arg); m_p2p->externalRelayNotifyToAll(NOTIFY_NEW_TRANSACTIONS::ID, buf); } -void cryptonote_protocol_handler::requestMissingPoolTransactions(const cryptonote_connection_context& context) { +void CryptoNoteProtocolHandler::requestMissingPoolTransactions(const CryptoNoteConnectionContext& context) { if (context.version < P2PProtocolVersion::V1) { return; } @@ -620,7 +626,7 @@ void cryptonote_protocol_handler::requestMissingPoolTransactions(const cryptonot NOTIFY_REQUEST_TX_POOL::request notification; for (auto& tx : poolTxs) { - notification.txs.emplace_back(get_transaction_hash(tx)); + notification.txs.emplace_back(getObjectHash(tx)); } bool ok = post_notify(*m_p2p, notification, context); @@ -629,12 +635,12 @@ void cryptonote_protocol_handler::requestMissingPoolTransactions(const cryptonot } } -void cryptonote_protocol_handler::updateObservedHeight(uint64_t peerHeight, const cryptonote_connection_context& context) { +void CryptoNoteProtocolHandler::updateObservedHeight(uint32_t peerHeight, const CryptoNoteConnectionContext& context) { bool updated = false; { std::lock_guard lock(m_observedHeightMutex); - uint64_t height = m_observedHeight; + uint32_t height = m_observedHeight; if (peerHeight > context.m_remote_blockchain_height) { m_observedHeight = std::max(m_observedHeight, peerHeight); if (m_observedHeight != height) { @@ -651,35 +657,35 @@ void cryptonote_protocol_handler::updateObservedHeight(uint64_t peerHeight, cons if (updated) { logger(TRACE) << "Observed height updated: " << m_observedHeight; - m_observerManager.notify(&ICryptonoteProtocolObserver::lastKnownBlockHeightUpdated, m_observedHeight); + m_observerManager.notify(&ICryptoNoteProtocolObserver::lastKnownBlockHeightUpdated, m_observedHeight); } } -void cryptonote_protocol_handler::recalculateMaxObservedHeight(const cryptonote_connection_context& context) { +void CryptoNoteProtocolHandler::recalculateMaxObservedHeight(const CryptoNoteConnectionContext& context) { //should be locked outside - uint64_t peerHeight = 0; - m_p2p->for_each_connection([&peerHeight, &context](const cryptonote_connection_context& ctx, peerid_type peerId) { + uint32_t peerHeight = 0; + m_p2p->for_each_connection([&peerHeight, &context](const CryptoNoteConnectionContext& ctx, PeerIdType peerId) { if (ctx.m_connection_id != context.m_connection_id) { peerHeight = std::max(peerHeight, ctx.m_remote_blockchain_height); } }); - uint64_t localHeight = 0; - crypto::hash ignore; + uint32_t localHeight = 0; + Crypto::Hash ignore; m_core.get_blockchain_top(localHeight, ignore); - m_observedHeight = std::max(peerHeight, localHeight); + m_observedHeight = std::max(peerHeight, localHeight + 1); } -uint64_t cryptonote_protocol_handler::getObservedHeight() const { +uint32_t CryptoNoteProtocolHandler::getObservedHeight() const { std::lock_guard lock(m_observedHeightMutex); return m_observedHeight; }; -bool cryptonote_protocol_handler::addObserver(ICryptonoteProtocolObserver* observer) { +bool CryptoNoteProtocolHandler::addObserver(ICryptoNoteProtocolObserver* observer) { return m_observerManager.add(observer); } -bool cryptonote_protocol_handler::removeObserver(ICryptonoteProtocolObserver* observer) { +bool CryptoNoteProtocolHandler::removeObserver(ICryptoNoteProtocolObserver* observer) { return m_observerManager.remove(observer); } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.h old mode 100644 new mode 100755 similarity index 51% rename from src/cryptonote_protocol/cryptonote_protocol_handler.h rename to src/CryptoNoteProtocol/CryptoNoteProtocolHandler.h index bdd8261d..0c606f24 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.h @@ -19,19 +19,18 @@ #include -#include #include -#include "cryptonote_core/ICore.h" +#include "CryptoNoteCore/ICore.h" -#include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "cryptonote_protocol/cryptonote_protocol_handler_common.h" -#include "cryptonote_protocol/ICryptonoteProtocolObserver.h" -#include "cryptonote_protocol/ICryptonoteProtocolQuery.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolHandlerCommon.h" +#include "CryptoNoteProtocol/ICryptoNoteProtocolObserver.h" +#include "CryptoNoteProtocol/ICryptoNoteProtocolQuery.h" -#include "p2p/p2p_protocol_defs.h" -#include "p2p/net_node_common.h" -#include "p2p/connection_context.h" +#include "P2p/P2pProtocolDefinitions.h" +#include "P2p/NetNodeCommon.h" +#include "P2p/ConnectionContext.h" #include @@ -43,57 +42,57 @@ namespace CryptoNote { class Currency; - class cryptonote_protocol_handler : + class CryptoNoteProtocolHandler : public i_cryptonote_protocol, - public ICryptonoteProtocolQuery + public ICryptoNoteProtocolQuery { public: - cryptonote_protocol_handler(const Currency& currency, System::Dispatcher& dispatcher, ICore& rcore, i_p2p_endpoint* p_net_layout, Logging::ILogger& log); + CryptoNoteProtocolHandler(const Currency& currency, System::Dispatcher& dispatcher, ICore& rcore, IP2pEndpoint* p_net_layout, Logging::ILogger& log); - virtual bool addObserver(ICryptonoteProtocolObserver* observer) override; - virtual bool removeObserver(ICryptonoteProtocolObserver* observer) override; + virtual bool addObserver(ICryptoNoteProtocolObserver* observer); + virtual bool removeObserver(ICryptoNoteProtocolObserver* observer); - void set_p2p_endpoint(i_p2p_endpoint* p2p); + void set_p2p_endpoint(IP2pEndpoint* p2p); // ICore& get_core() { return m_core; } virtual bool isSynchronized() const override { return m_synchronized; } void log_connections(); // Interface t_payload_net_handler, where t_payload_net_handler is template argument of nodetool::node_server void stop(); - bool start_sync(cryptonote_connection_context& context); + bool start_sync(CryptoNoteConnectionContext& context); bool on_idle(); - void onConnectionOpened(cryptonote_connection_context& context); - void onConnectionClosed(cryptonote_connection_context& context); + void onConnectionOpened(CryptoNoteConnectionContext& context); + void onConnectionClosed(CryptoNoteConnectionContext& context); bool get_stat_info(core_stat_info& stat_inf); bool get_payload_sync_data(CORE_SYNC_DATA& hshd); - bool process_payload_sync_data(const CORE_SYNC_DATA& hshd, cryptonote_connection_context& context, bool is_inital); - int handleCommand(bool is_notify, int command, const std::string& in_buff, std::string& buff_out, cryptonote_connection_context& context, bool& handled); - virtual size_t getPeerCount() const override; - virtual uint64_t getObservedHeight() const override; - void requestMissingPoolTransactions(const cryptonote_connection_context& context); + bool process_payload_sync_data(const CORE_SYNC_DATA& hshd, CryptoNoteConnectionContext& context, bool is_inital); + int handleCommand(bool is_notify, int command, const BinaryArray& in_buff, BinaryArray& buff_out, CryptoNoteConnectionContext& context, bool& handled); + virtual size_t getPeerCount() const; + virtual uint32_t getObservedHeight() const; + void requestMissingPoolTransactions(const CryptoNoteConnectionContext& context); private: //----------------- commands handlers ---------------------------------------------- - int handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& context); - int handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& context); - int handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context); - int handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, cryptonote_connection_context& context); - int handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, cryptonote_connection_context& context); - int handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context); - int handleRequestTxPool(int command, NOTIFY_REQUEST_TX_POOL::request& arg, cryptonote_connection_context& context); + int handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, CryptoNoteConnectionContext& context); + int handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, CryptoNoteConnectionContext& context); + int handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, CryptoNoteConnectionContext& context); + int handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, CryptoNoteConnectionContext& context); + int handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, CryptoNoteConnectionContext& context); + int handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, CryptoNoteConnectionContext& context); + int handleRequestTxPool(int command, NOTIFY_REQUEST_TX_POOL::request& arg, CryptoNoteConnectionContext& context); //----------------- i_cryptonote_protocol ---------------------------------- virtual void relay_block(NOTIFY_NEW_BLOCK::request& arg) override; virtual void relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg) override; //---------------------------------------------------------------------------------- - uint64_t get_current_blockchain_height(); - bool request_missing_objects(cryptonote_connection_context& context, bool check_having_blocks); + uint32_t get_current_blockchain_height(); + bool request_missing_objects(CryptoNoteConnectionContext& context, bool check_having_blocks); bool on_connection_synchronized(); - void updateObservedHeight(uint64_t peerHeight, const cryptonote_connection_context& context); - void recalculateMaxObservedHeight(const cryptonote_connection_context& context); - int processObjects(cryptonote_connection_context& context, const std::list& blocks); + void updateObservedHeight(uint32_t peerHeight, const CryptoNoteConnectionContext& context); + void recalculateMaxObservedHeight(const CryptoNoteConnectionContext& context); + int processObjects(CryptoNoteConnectionContext& context, const std::vector& blocks); Logging::LoggerRef logger; private: @@ -103,14 +102,14 @@ namespace CryptoNote const Currency& m_currency; p2p_endpoint_stub m_p2p_stub; - i_p2p_endpoint* m_p2p; + IP2pEndpoint* m_p2p; std::atomic m_synchronized; std::atomic m_stop; mutable std::mutex m_observedHeightMutex; - uint64_t m_observedHeight; + uint32_t m_observedHeight; std::atomic m_peersCount; - tools::ObserverManager m_observerManager; + Tools::ObserverManager m_observerManager; }; } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h b/src/CryptoNoteProtocol/CryptoNoteProtocolHandlerCommon.h old mode 100644 new mode 100755 similarity index 100% rename from src/cryptonote_protocol/cryptonote_protocol_handler_common.h rename to src/CryptoNoteProtocol/CryptoNoteProtocolHandlerCommon.h diff --git a/src/cryptonote_protocol/ICryptonoteProtocolObserver.h b/src/CryptoNoteProtocol/ICryptoNoteProtocolObserver.h old mode 100644 new mode 100755 similarity index 85% rename from src/cryptonote_protocol/ICryptonoteProtocolObserver.h rename to src/CryptoNoteProtocol/ICryptoNoteProtocolObserver.h index 34c11402..0d22c380 --- a/src/cryptonote_protocol/ICryptonoteProtocolObserver.h +++ b/src/CryptoNoteProtocol/ICryptoNoteProtocolObserver.h @@ -23,11 +23,11 @@ namespace CryptoNote { -class ICryptonoteProtocolObserver { +class ICryptoNoteProtocolObserver { public: virtual void peerCountUpdated(size_t count) {} - virtual void lastKnownBlockHeightUpdated(uint64_t height) {} - virtual void blockchainSynchronized(uint64_t topHeight) {} + virtual void lastKnownBlockHeightUpdated(uint32_t height) {} + virtual void blockchainSynchronized(uint32_t topHeight) {} }; } //namespace CryptoNote diff --git a/src/cryptonote_protocol/ICryptonoteProtocolQuery.h b/src/CryptoNoteProtocol/ICryptoNoteProtocolQuery.h old mode 100644 new mode 100755 similarity index 80% rename from src/cryptonote_protocol/ICryptonoteProtocolQuery.h rename to src/CryptoNoteProtocol/ICryptoNoteProtocolQuery.h index b9d42279..eb353fb2 --- a/src/cryptonote_protocol/ICryptonoteProtocolQuery.h +++ b/src/CryptoNoteProtocol/ICryptoNoteProtocolQuery.h @@ -21,14 +21,14 @@ #include namespace CryptoNote { -class ICryptonoteProtocolObserver; +class ICryptoNoteProtocolObserver; -class ICryptonoteProtocolQuery { +class ICryptoNoteProtocolQuery { public: - virtual bool addObserver(ICryptonoteProtocolObserver* observer) = 0; - virtual bool removeObserver(ICryptonoteProtocolObserver* observer) = 0; + virtual bool addObserver(ICryptoNoteProtocolObserver* observer) = 0; + virtual bool removeObserver(ICryptoNoteProtocolObserver* observer) = 0; - virtual uint64_t getObservedHeight() const = 0; + virtual uint32_t getObservedHeight() const = 0; virtual size_t getPeerCount() const = 0; virtual bool isSynchronized() const = 0; }; diff --git a/src/daemon/daemon.cpp b/src/Daemon/Daemon.cpp old mode 100644 new mode 100755 similarity index 92% rename from src/daemon/daemon.cpp rename to src/Daemon/Daemon.cpp index 3c5e6957..c96783ca --- a/src/daemon/daemon.cpp +++ b/src/Daemon/Daemon.cpp @@ -25,15 +25,15 @@ #include "Common/SignalHandler.h" #include "Common/PathTools.h" #include "crypto/hash.h" -#include "cryptonote_core/cryptonote_core.h" -#include "cryptonote_core/CoreConfig.h" -#include "cryptonote_core/Currency.h" -#include "cryptonote_core/MinerConfig.h" -#include "cryptonote_protocol/cryptonote_protocol_handler.h" -#include "p2p/net_node.h" -#include "p2p/NetNodeConfig.h" -#include "rpc/RpcServer.h" -#include "rpc/RpcServerConfig.h" +#include "CryptoNoteCore/Core.h" +#include "CryptoNoteCore/CoreConfig.h" +#include "CryptoNoteCore/Currency.h" +#include "CryptoNoteCore/MinerConfig.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolHandler.h" +#include "P2p/NetNode.h" +#include "P2p/NetNodeConfig.h" +#include "Rpc/RpcServer.h" +#include "Rpc/RpcServerConfig.h" #include "version.h" #include @@ -100,7 +100,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_cmd_only, command_line::arg_version); command_line::add_arg(desc_cmd_only, arg_os_version); // tools::get_default_data_dir() can't be called during static initialization - command_line::add_arg(desc_cmd_only, command_line::arg_data_dir, tools::get_default_data_dir()); + command_line::add_arg(desc_cmd_only, command_line::arg_data_dir, Tools::getDefaultDataDirectory()); command_line::add_arg(desc_cmd_only, arg_config_file); command_line::add_arg(desc_cmd_sett, arg_log_file); @@ -183,7 +183,7 @@ int main(int argc, char* argv[]) CryptoNote::Currency currency = currencyBuilder.currency(); CryptoNote::core ccore(currency, nullptr, logManager); - CryptoNote::checkpoints checkpoints(logManager); + CryptoNote::Checkpoints checkpoints(logManager); for (const auto& cp : CryptoNote::CHECKPOINTS) { checkpoints.add_checkpoint(cp.height, cp.blockId); } @@ -196,6 +196,7 @@ int main(int argc, char* argv[]) coreConfig.init(vm); NetNodeConfig netNodeConfig; netNodeConfig.init(vm); + netNodeConfig.setTestnet(testnet_mode); MinerConfig minerConfig; minerConfig.init(vm); RpcServerConfig rpcConfig; @@ -203,8 +204,8 @@ int main(int argc, char* argv[]) System::Dispatcher dispatcher; - CryptoNote::cryptonote_protocol_handler cprotocol(currency, dispatcher, ccore, nullptr, logManager); - CryptoNote::node_server p2psrv(dispatcher, cprotocol, logManager); + CryptoNote::CryptoNoteProtocolHandler cprotocol(currency, dispatcher, ccore, nullptr, logManager); + CryptoNote::NodeServer p2psrv(dispatcher, cprotocol, logManager); CryptoNote::RpcServer rpcServer(dispatcher, logManager, ccore, p2psrv); cprotocol.set_p2p_endpoint(&p2psrv); @@ -213,7 +214,7 @@ int main(int argc, char* argv[]) // initialize objects logger(INFO) << "Initializing p2p server..."; - if (!p2psrv.init(netNodeConfig, testnet_mode)) { + if (!p2psrv.init(netNodeConfig)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize p2p server."; return 1; } @@ -243,9 +244,9 @@ int main(int argc, char* argv[]) rpcServer.start(rpcConfig.bindIp, rpcConfig.bindPort); logger(INFO) << "Core rpc server started ok"; - tools::SignalHandler::install([&dch, &p2psrv] { + Tools::SignalHandler::install([&dch, &p2psrv] { dch.stop_handling(); - p2psrv.send_stop_signal(); + p2psrv.sendStopSignal(); }); logger(INFO) << "Starting p2p net loop..."; @@ -284,7 +285,7 @@ bool command_line_preprocessor(const boost::program_options::variables_map &vm, exit = true; } if (command_line::get_arg(vm, arg_os_version)) { - std::cout << "OS: " << tools::get_os_version_string() << ENDL; + std::cout << "OS: " << Tools::get_os_version_string() << ENDL; exit = true; } diff --git a/src/daemon/DeamonCommandsHandler.cpp b/src/Daemon/DaemonCommandsHandler.cpp old mode 100644 new mode 100755 similarity index 90% rename from src/daemon/DeamonCommandsHandler.cpp rename to src/Daemon/DaemonCommandsHandler.cpp index 241424ca..c0d3573b --- a/src/daemon/DeamonCommandsHandler.cpp +++ b/src/Daemon/DaemonCommandsHandler.cpp @@ -17,23 +17,23 @@ #include "DaemonCommandsHandler.h" -#include "p2p/net_node.h" -#include "cryptonote_core/miner.h" -#include "cryptonote_core/cryptonote_core.h" -#include "cryptonote_protocol/cryptonote_protocol_handler.h" - +#include "P2p/NetNode.h" +#include "CryptoNoteCore/Miner.h" +#include "CryptoNoteCore/Core.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolHandler.h" +#include "Serialization/SerializationTools.h" #include "version.h" namespace { template static bool print_as_json(const T& obj) { - std::cout << CryptoNote::obj_to_json_str(obj) << ENDL; + std::cout << CryptoNote::storeToJson(obj) << ENDL; return true; } } -DaemonCommandsHandler::DaemonCommandsHandler(CryptoNote::core& core, CryptoNote::node_server& srv, Logging::LoggerManager& log) : +DaemonCommandsHandler::DaemonCommandsHandler(CryptoNote::core& core, CryptoNote::NodeServer& srv, Logging::LoggerManager& log) : m_core(core), m_srv(srv), logger(log, "daemon"), m_logManager(log) { m_consoleHandler.setHandler("exit", boost::bind(&DaemonCommandsHandler::exit, this, _1), "Shutdown the daemon"); m_consoleHandler.setHandler("help", boost::bind(&DaemonCommandsHandler::help, this, _1), "Show this help"); @@ -50,7 +50,7 @@ DaemonCommandsHandler::DaemonCommandsHandler(CryptoNote::core& core, CryptoNote: m_consoleHandler.setHandler("print_pool_sh", boost::bind(&DaemonCommandsHandler::print_pool_sh, this, _1), "Print transaction pool (short format)"); m_consoleHandler.setHandler("show_hr", boost::bind(&DaemonCommandsHandler::show_hr, this, _1), "Start showing hash rate"); m_consoleHandler.setHandler("hide_hr", boost::bind(&DaemonCommandsHandler::hide_hr, this, _1), "Stop showing hash rate"); - m_consoleHandler.setHandler("set_log", boost::bind(&DaemonCommandsHandler::set_log, this, _1), "set_log - Change current log detalization level, is a number 0-4"); + m_consoleHandler.setHandler("set_log", boost::bind(&DaemonCommandsHandler::set_log, this, _1), "set_log - Change current log level, is a number 0-4"); } //-------------------------------------------------------------------------------- @@ -69,7 +69,7 @@ std::string DaemonCommandsHandler::get_commands_str() //-------------------------------------------------------------------------------- bool DaemonCommandsHandler::exit(const std::vector& args) { m_consoleHandler.requestStop(); - m_srv.send_stop_signal(); + m_srv.sendStopSignal(); return true; } @@ -88,7 +88,7 @@ bool DaemonCommandsHandler::show_hr(const std::vector& args) { if (!m_core.get_miner().is_mining()) { - std::cout << "Mining is not started. You need start mining before you can see hash rate." << ENDL; + std::cout << "Mining is not started. You need to start mining before you can see hash rate." << ENDL; } else { m_core.get_miner().do_print_hashrate(true); @@ -125,9 +125,9 @@ bool DaemonCommandsHandler::print_bc(const std::vector &args) { return false; } - uint64_t start_index = 0; - uint64_t end_index = 0; - uint64_t end_block_parametr = m_core.get_current_blockchain_height(); + uint32_t start_index = 0; + uint32_t end_index = 0; + uint32_t end_block_parametr = m_core.get_current_blockchain_height(); if (!Common::fromString(args[0], start_index)) { std::cout << "wrong starter block index parameter" << ENDL; return false; @@ -187,7 +187,7 @@ bool DaemonCommandsHandler::set_log(const std::vector& args) } //-------------------------------------------------------------------------------- -bool DaemonCommandsHandler::print_block_by_height(uint64_t height) +bool DaemonCommandsHandler::print_block_by_height(uint32_t height) { std::list blocks; m_core.get_blocks(height, 1, blocks); @@ -196,8 +196,8 @@ bool DaemonCommandsHandler::print_block_by_height(uint64_t height) std::cout << "block_id: " << get_block_hash(blocks.front()) << ENDL; print_as_json(blocks.front()); } else { - uint64_t current_height; - crypto::hash top_id; + uint32_t current_height; + Crypto::Hash top_id; m_core.get_blockchain_top(current_height, top_id); std::cout << "block wasn't found. Current block chain height: " << current_height << ", requested: " << height << std::endl; return false; @@ -208,15 +208,15 @@ bool DaemonCommandsHandler::print_block_by_height(uint64_t height) //-------------------------------------------------------------------------------- bool DaemonCommandsHandler::print_block_by_hash(const std::string& arg) { - crypto::hash block_hash; + Crypto::Hash block_hash; if (!parse_hash256(arg, block_hash)) { return false; } - std::list block_ids; + std::list block_ids; block_ids.push_back(block_hash); std::list blocks; - std::list missed_ids; + std::list missed_ids; m_core.get_blocks(block_ids, blocks, missed_ids); if (1 == blocks.size()) @@ -239,7 +239,7 @@ bool DaemonCommandsHandler::print_block(const std::vector &args) { const std::string &arg = args.front(); try { - uint64_t height = boost::lexical_cast(arg); + uint32_t height = boost::lexical_cast(arg); print_block_by_height(height); } catch (boost::bad_lexical_cast &) { print_block_by_hash(arg); @@ -256,16 +256,16 @@ bool DaemonCommandsHandler::print_tx(const std::vector& args) } const std::string &str_hash = args.front(); - crypto::hash tx_hash; + Crypto::Hash tx_hash; if (!parse_hash256(str_hash, tx_hash)) { return true; } - std::vector tx_ids; + std::vector tx_ids; tx_ids.push_back(tx_hash); std::list txs; - std::list missed_ids; - m_core.getTransactions(tx_ids, txs, missed_ids); + std::list missed_ids; + m_core.getTransactions(tx_ids, txs, missed_ids, true); if (1 == txs.size()) { print_as_json(txs.front()); @@ -306,10 +306,7 @@ bool DaemonCommandsHandler::start_mining(const std::vector &args) { threads_count = (ok && 0 < threads_count) ? threads_count : 1; } - boost::thread::attributes attrs; - attrs.set_stack_size(CryptoNote::THREAD_STACK_SIZE); - - m_core.get_miner().start(adr, threads_count, attrs); + m_core.get_miner().start(adr, threads_count); return true; } diff --git a/src/daemon/DaemonCommandsHandler.h b/src/Daemon/DaemonCommandsHandler.h old mode 100644 new mode 100755 similarity index 91% rename from src/daemon/DaemonCommandsHandler.h rename to src/Daemon/DaemonCommandsHandler.h index bd483cb0..b65d505c --- a/src/daemon/DaemonCommandsHandler.h +++ b/src/Daemon/DaemonCommandsHandler.h @@ -24,13 +24,13 @@ namespace CryptoNote { class core; -class node_server; +class NodeServer; } class DaemonCommandsHandler { public: - DaemonCommandsHandler(CryptoNote::core& core, CryptoNote::node_server& srv, Logging::LoggerManager& log); + DaemonCommandsHandler(CryptoNote::core& core, CryptoNote::NodeServer& srv, Logging::LoggerManager& log); bool start_handling() { m_consoleHandler.start(); @@ -45,12 +45,12 @@ private: Common::ConsoleHandler m_consoleHandler; CryptoNote::core& m_core; - CryptoNote::node_server& m_srv; + CryptoNote::NodeServer& m_srv; Logging::LoggerRef logger; Logging::LoggerManager& m_logManager; std::string get_commands_str(); - bool print_block_by_height(uint64_t height); + bool print_block_by_height(uint32_t height); bool print_block_by_hash(const std::string& arg); bool exit(const std::vector& args); diff --git a/src/HTTP/HttpParserErrorCodes.cpp b/src/HTTP/HttpParserErrorCodes.cpp old mode 100644 new mode 100755 index f473b609..f211ac28 --- a/src/HTTP/HttpParserErrorCodes.cpp +++ b/src/HTTP/HttpParserErrorCodes.cpp @@ -23,4 +23,4 @@ namespace error { HttpParserErrorCategory HttpParserErrorCategory::INSTANCE; } //namespace error -} //namespace cryptonote +} //namespace CryptoNote diff --git a/src/HTTP/HttpParserErrorCodes.h b/src/HTTP/HttpParserErrorCodes.h old mode 100644 new mode 100755 index ad8475e2..f4361fa5 --- a/src/HTTP/HttpParserErrorCodes.h +++ b/src/HTTP/HttpParserErrorCodes.h @@ -59,7 +59,7 @@ private: }; } //namespace error -} //namespace cryptonote +} //namespace CryptoNote inline std::error_code make_error_code(CryptoNote::error::HttpParserErrorCodes e) { return std::error_code(static_cast(e), CryptoNote::error::HttpParserErrorCategory::INSTANCE); diff --git a/src/HTTP/HttpResponse.cpp b/src/HTTP/HttpResponse.cpp index a15b9820..df5b665f 100755 --- a/src/HTTP/HttpResponse.cpp +++ b/src/HTTP/HttpResponse.cpp @@ -55,7 +55,7 @@ namespace CryptoNote { HttpResponse::HttpResponse() { status = STATUS_200; - headers["Server"] = "Cryptonote-based HTTP server"; + headers["Server"] = "CryptoNote-based HTTP server"; } void HttpResponse::setStatus(HTTP_STATUS s) { diff --git a/src/InProcessNode/InProcessNode.cpp b/src/InProcessNode/InProcessNode.cpp index 0602febc..d1c20ac2 100644 --- a/src/InProcessNode/InProcessNode.cpp +++ b/src/InProcessNode/InProcessNode.cpp @@ -19,16 +19,23 @@ #include #include +#include +#include "CryptoNoteConfig.h" #include "Common/StringTools.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/verification_context.h" -#include "cryptonote_protocol/cryptonote_protocol_handler_common.h" +#include "CryptoNoteCore/CryptoNoteTools.h" +#include "CryptoNoteCore/IBlock.h" +#include "CryptoNoteCore/VerificationContext.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolHandlerCommon.h" #include "InProcessNodeErrors.h" +#include "Common/StringTools.h" + +using namespace Crypto; +using namespace Common; namespace CryptoNote { -InProcessNode::InProcessNode(CryptoNote::ICore& core, CryptoNote::ICryptonoteProtocolQuery& protocol) : +InProcessNode::InProcessNode(CryptoNote::ICore& core, CryptoNote::ICryptoNoteProtocolQuery& protocol) : state(NOT_INITIALIZED), core(core), protocol(protocol), @@ -97,11 +104,11 @@ bool InProcessNode::doShutdown() { } void InProcessNode::workerFunc() { - ioService.run(); + ioService.run(); } -void InProcessNode::getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, - uint64_t& startHeight, const Callback& callback) +void InProcessNode::getNewBlocks(std::vector&& knownBlockIds, std::vector& newBlocks, + uint32_t& startHeight, const Callback& callback) { std::unique_lock lock(mutex); if (state != INITIALIZED) { @@ -121,15 +128,15 @@ void InProcessNode::getNewBlocks(std::list&& knownBlockIds, std::l ); } -void InProcessNode::getNewBlocksAsync(std::list& knownBlockIds, std::list& newBlocks, - uint64_t& startHeight, const Callback& callback) +void InProcessNode::getNewBlocksAsync(std::vector& knownBlockIds, std::vector& newBlocks, + uint32_t& startHeight, const Callback& callback) { std::error_code ec = doGetNewBlocks(std::move(knownBlockIds), newBlocks, startHeight); callback(ec); } //it's always protected with mutex -std::error_code InProcessNode::doGetNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight) { +std::error_code InProcessNode::doGetNewBlocks(std::vector&& knownBlockIds, std::vector& newBlocks, uint32_t& startHeight) { { std::unique_lock lock(mutex); if (state != INITIALIZED) { @@ -138,18 +145,29 @@ std::error_code InProcessNode::doGetNewBlocks(std::list&& knownBlo } try { - uint64_t totalHeight; - std::list > > bs; - if (!core.find_blockchain_supplement(knownBlockIds, bs, totalHeight, startHeight, 1000)) { + // TODO code duplication see RpcServer::on_get_blocks() + if (knownBlockIds.empty()) { return make_error_code(CryptoNote::error::REQUEST_ERROR); } - for (auto& b : bs) { - CryptoNote::block_complete_entry be; - be.block = CryptoNote::block_to_blob(b.first); + if (knownBlockIds.back() != core.getBlockIdByHeight(0)) { + return make_error_code(CryptoNote::error::REQUEST_ERROR); + } - for (auto& t : b.second) { - be.txs.push_back(CryptoNote::tx_to_blob(t)); + uint32_t totalBlockCount; + std::vector supplement = core.findBlockchainSupplement(knownBlockIds, CryptoNote::COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT, totalBlockCount, startHeight); + + for (const auto& blockId : supplement) { + assert(core.have_block(blockId)); + auto completeBlock = core.getBlock(blockId); + assert(completeBlock != nullptr); + + CryptoNote::block_complete_entry be; + be.block = asString(toBinaryArray(completeBlock->getBlock())); + + be.txs.reserve(completeBlock->getTransactionCount()); + for (size_t i = 0; i < completeBlock->getTransactionCount(); ++i) { + be.txs.push_back(asString(toBinaryArray(completeBlock->getTransaction(i)))); } newBlocks.push_back(std::move(be)); @@ -163,7 +181,7 @@ std::error_code InProcessNode::doGetNewBlocks(std::list&& knownBlo return std::error_code(); } -void InProcessNode::getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, +void InProcessNode::getTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) { std::unique_lock lock(mutex); @@ -183,7 +201,7 @@ void InProcessNode::getTransactionOutsGlobalIndices(const crypto::hash& transact ); } -void InProcessNode::getTransactionOutsGlobalIndicesAsync(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, +void InProcessNode::getTransactionOutsGlobalIndicesAsync(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) { std::error_code ec = doGetTransactionOutsGlobalIndices(transactionHash, outsGlobalIndices); @@ -191,7 +209,7 @@ void InProcessNode::getTransactionOutsGlobalIndicesAsync(const crypto::hash& tra } //it's always protected with mutex -std::error_code InProcessNode::doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices) { +std::error_code InProcessNode::doGetTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices) { { std::unique_lock lock(mutex); if (state != INITIALIZED) { @@ -304,10 +322,10 @@ std::error_code InProcessNode::doRelayTransaction(const CryptoNote::Transaction& } try { - CryptoNote::blobdata txBlob = CryptoNote::tx_to_blob(transaction); + CryptoNote::BinaryArray transactionBinaryArray = toBinaryArray(transaction); CryptoNote::tx_verification_context tvc = boost::value_initialized(); - if(!core.handle_incoming_tx(txBlob, tvc, false)) { + if (!core.handle_incoming_tx(transactionBinaryArray, tvc, false)) { return make_error_code(CryptoNote::error::REQUEST_ERROR); } @@ -320,7 +338,7 @@ std::error_code InProcessNode::doRelayTransaction(const CryptoNote::Transaction& } CryptoNote::NOTIFY_NEW_TRANSACTIONS::request r; - r.txs.push_back(txBlob); + r.txs.push_back(asString(transactionBinaryArray)); core.get_protocol()->relay_transactions(r); } catch (std::system_error& e) { return e.code(); @@ -342,7 +360,7 @@ size_t InProcessNode::getPeerCount() const { return protocol.getPeerCount(); } -uint64_t InProcessNode::getLocalBlockCount() const { +uint32_t InProcessNode::getLocalBlockCount() const { { std::unique_lock lock(mutex); if (state != INITIALIZED) { @@ -350,15 +368,15 @@ uint64_t InProcessNode::getLocalBlockCount() const { } } - uint64_t lastIndex; - crypto::hash ignore; + uint32_t lastIndex; + Crypto::Hash ignore; core.get_blockchain_top(lastIndex, ignore); return lastIndex + 1; } -uint64_t InProcessNode::getKnownBlockCount() const { +uint32_t InProcessNode::getKnownBlockCount() const { { std::unique_lock lock(mutex); if (state != INITIALIZED) { @@ -369,7 +387,7 @@ uint64_t InProcessNode::getKnownBlockCount() const { return protocol.getObservedHeight(); } -uint64_t InProcessNode::getLastLocalBlockHeight() const { +uint32_t InProcessNode::getLastLocalBlockHeight() const { { std::unique_lock lock(mutex); if (state != INITIALIZED) { @@ -377,17 +395,15 @@ uint64_t InProcessNode::getLastLocalBlockHeight() const { } } - uint64_t height; - crypto::hash ignore; + uint32_t height; + Crypto::Hash ignore; - if (!core.get_blockchain_top(height, ignore)) { - throw std::system_error(make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR)); - } + core.get_blockchain_top(height, ignore); return height; } -uint64_t InProcessNode::getLastKnownBlockHeight() const { +uint32_t InProcessNode::getLastKnownBlockHeight() const { { std::unique_lock lock(mutex); if (state != INITIALIZED) { @@ -398,14 +414,6 @@ uint64_t InProcessNode::getLastKnownBlockHeight() const { return protocol.getObservedHeight() - 1; } -void InProcessNode::peerCountUpdated(size_t count) { - observerManager.notify(&INodeObserver::peerCountUpdated, count); -} - -void InProcessNode::lastKnownBlockHeightUpdated(uint64_t height) { - observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, height); -} - uint64_t InProcessNode::getLastLocalBlockTimestamp() const { std::unique_lock lock(mutex); if (state != INITIALIZED) { @@ -413,12 +421,10 @@ uint64_t InProcessNode::getLastLocalBlockTimestamp() const { } lock.unlock(); - uint64_t ignore; - crypto::hash hash; + uint32_t ignore; + Crypto::Hash hash; - if (!core.get_blockchain_top(ignore, hash)) { - throw std::system_error(make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR)); - } + core.get_blockchain_top(ignore, hash); CryptoNote::Block block; if (!core.getBlockByHash(hash, block)) { @@ -428,9 +434,17 @@ uint64_t InProcessNode::getLastLocalBlockTimestamp() const { return block.timestamp; } +void InProcessNode::peerCountUpdated(size_t count) { + observerManager.notify(&INodeObserver::peerCountUpdated, count); +} + +void InProcessNode::lastKnownBlockHeightUpdated(uint32_t height) { + observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, height); +} + void InProcessNode::blockchainUpdated() { - uint64_t height; - crypto::hash ignore; + uint32_t height; + Crypto::Hash ignore; core.get_blockchain_top(height, ignore); observerManager.notify(&INodeObserver::localBlockchainUpdated, height); @@ -440,13 +454,12 @@ void InProcessNode::poolUpdated() { observerManager.notify(&INodeObserver::poolChanged); } -void InProcessNode::blockchainSynchronized(uint64_t topHeight) { +void InProcessNode::blockchainSynchronized(uint32_t topHeight) { observerManager.notify(&INodeObserver::blockchainSynchronized, topHeight); } -void InProcessNode::queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, - std::list& newBlocks, uint64_t& startHeight, const InProcessNode::Callback& callback) -{ +void InProcessNode::queryBlocks(std::vector&& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, + uint32_t& startHeight, const Callback& callback) { std::unique_lock lock(mutex); if (state != INITIALIZED) { lock.unlock(); @@ -455,48 +468,60 @@ void InProcessNode::queryBlocks(std::list&& knownBlockIds, uint64_ } ioService.post( - std::bind(&InProcessNode::queryBlocksAsync, - this, - std::move(knownBlockIds), - timestamp, - std::ref(newBlocks), - std::ref(startHeight), - callback - ) + std::bind(&InProcessNode::queryBlocksLiteAsync, + this, + std::move(knownBlockIds), + timestamp, + std::ref(newBlocks), + std::ref(startHeight), + callback + ) ); } -void InProcessNode::queryBlocksAsync(std::list& knownBlockIds, uint64_t timestamp, - std::list& newBlocks, uint64_t& startHeight, const Callback& callback) -{ - std::error_code ec = doQueryBlocks(std::move(knownBlockIds), timestamp, newBlocks, startHeight); +void InProcessNode::queryBlocksLiteAsync(std::vector& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, uint32_t& startHeight, + const Callback& callback) { + std::error_code ec = doQueryBlocksLite(std::move(knownBlockIds), timestamp, newBlocks, startHeight); callback(ec); } -std::error_code InProcessNode::doQueryBlocks(std::list&& knownBlockIds, uint64_t timestamp, - std::list& newBlocks, uint64_t& startHeight) { - uint64_t currentHeight, fullOffset; - std::list entries; +std::error_code InProcessNode::doQueryBlocksLite(std::vector&& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, uint32_t& startHeight) { + uint32_t currentHeight, fullOffset; + std::vector entries; - if (!core.queryBlocks(knownBlockIds, timestamp, startHeight, currentHeight, fullOffset, entries)) { + if (!core.queryBlocksLite(knownBlockIds, timestamp, startHeight, currentHeight, fullOffset, entries)) { return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); } for (const auto& entry: entries) { - BlockCompleteEntry bce; - bce.blockHash = entry.block_id; - bce.block = entry.block; - std::copy(entry.txs.begin(), entry.txs.end(), std::back_inserter(bce.txs)); + BlockShortEntry bse; + bse.blockHash = entry.blockId; + bse.hasBlock = false; - newBlocks.push_back(std::move(bce)); + if (!entry.block.empty()) { + bse.hasBlock = true; + if (!fromBinaryArray(bse.block, asBinaryArray(entry.block))) { + return std::make_error_code(std::errc::invalid_argument); + } + } + + for (const auto& tsi: entry.txPrefixes) { + TransactionShortInfo tpi; + tpi.txId = tsi.txHash; + tpi.txPrefix = tsi.txPrefix; + + bse.txsShortInfo.push_back(std::move(tpi)); + } + + newBlocks.push_back(std::move(bse)); } return std::error_code(); + } -void InProcessNode::getPoolSymmetricDifference(std::vector&& knownPoolTxIds, crypto::hash knownBlockId, bool& isBcActual, std::vector& newTxs, - std::vector& deletedTxIds, const Callback& callback) { - +void InProcessNode::getPoolSymmetricDifference(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, + std::vector>& newTxs, std::vector& deletedTxIds, const Callback& callback) { std::unique_lock lock(mutex); if (state != INITIALIZED) { lock.unlock(); @@ -504,32 +529,62 @@ void InProcessNode::getPoolSymmetricDifference(std::vector&& known return; } - ioService.post( - std::bind(&InProcessNode::getPoolSymmetricDifferenceAsync, - this, - std::move(knownPoolTxIds), - knownBlockId, - std::ref(isBcActual), - std::ref(newTxs), - std::ref(deletedTxIds), - callback - ) - ); + ioService.post([this, knownPoolTxIds, knownBlockId, &isBcActual, &newTxs, &deletedTxIds, callback] () mutable { + this->getPoolSymmetricDifferenceAsync(std::move(knownPoolTxIds), knownBlockId, isBcActual, newTxs, deletedTxIds, callback); + }); } -void InProcessNode::getPoolSymmetricDifferenceAsync(std::vector& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, - std::vector& deleted_tx_ids, const Callback& callback) { +void InProcessNode::getPoolSymmetricDifferenceAsync(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, + std::vector>& newTxs, std::vector& deletedTxIds, const Callback& callback) { std::error_code ec = std::error_code(); - is_bc_actual = core.getPoolChanges(known_block_id, known_pool_tx_ids, new_txs, deleted_tx_ids); - if (!is_bc_actual) { + std::vector added; + isBcActual = core.getPoolChangesLite(knownBlockId, knownPoolTxIds, added, deletedTxIds); + if (!isBcActual) { ec = make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + callback(ec); + return; + } + + try { + for (const auto& tx: added) { + newTxs.push_back(createTransactionPrefix(tx.txPrefix, reinterpret_cast(tx.txHash))); + } + } catch (std::system_error& ex) { + ec = ex.code(); + } catch (std::exception&) { + ec = make_error_code(std::errc::invalid_argument); } callback(ec); } -void InProcessNode::getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) { +void InProcessNode::getMultisignatureOutputByGlobalIndex(uint64_t amount, uint32_t gindex, MultisignatureOutput& out, const Callback& callback) { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + lock.unlock(); + callback(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + return; + } + + ioService.post([this, amount, gindex, &out, callback]() mutable { + this->getOutByMSigGIndexAsync(amount, gindex, out, callback); + }); +} + +void InProcessNode::getOutByMSigGIndexAsync(uint64_t amount, uint32_t gindex, MultisignatureOutput& out, const Callback& callback) { + std::error_code ec = std::error_code(); + bool result = core.getOutByMSigGIndex(amount, gindex, out); + if (!result) { + ec = make_error_code(std::errc::invalid_argument); + callback(ec); + return; + } + + callback(ec); +} + +void InProcessNode::getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) { std::unique_lock lock(mutex); if (state != INITIALIZED) { lock.unlock(); @@ -541,7 +596,7 @@ void InProcessNode::getBlocks(const std::vector& blockHeights, std::ve std::bind( static_cast< void(InProcessNode::*)( - const std::vector&, + const std::vector&, std::vector>&, const Callback& ) @@ -554,38 +609,66 @@ void InProcessNode::getBlocks(const std::vector& blockHeights, std::ve ); } -void InProcessNode::getBlocksAsync(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) { - std::error_code ec = doGetBlocks(blockHeights, blocks); +void InProcessNode::getBlocksAsync(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) { + std::error_code ec = core.executeLocked( + std::bind( + static_cast< + std::error_code(InProcessNode::*)( + const std::vector&, + std::vector>& + ) + >(&InProcessNode::doGetBlocks), + this, + std::cref(blockHeights), + std::ref(blocks) + ) + ); callback(ec); } -std::error_code InProcessNode::doGetBlocks(const std::vector& blockHeights, std::vector>& blocks) { - uint64_t topHeight = 0; - crypto::hash topHash = boost::value_initialized(); - if (!core.get_blockchain_top(topHeight, topHash)) { +std::error_code InProcessNode::doGetBlocks(const std::vector& blockHeights, std::vector>& blocks) { + try { + uint32_t topHeight = 0; + Crypto::Hash topHash = boost::value_initialized(); + core.get_blockchain_top(topHeight, topHash); + for (const uint32_t& height : blockHeights) { + if (height > topHeight) { + return make_error_code(CryptoNote::error::REQUEST_ERROR); + } + Crypto::Hash hash = core.getBlockIdByHeight(height); + Block block; + if (!core.getBlockByHash(hash, block)) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + BlockDetails blockDetails; + if (!blockchainExplorerDataBuilder.fillBlockDetails(block, blockDetails)) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + std::vector blocksOnSameHeight; + blocksOnSameHeight.push_back(std::move(blockDetails)); + + //Getting orphans + std::vector orphanBlocks; + core.getOrphanBlocksByHeight(height, orphanBlocks); + for (const Block& orphanBlock : orphanBlocks) { + BlockDetails orphanBlockDetails; + if (!blockchainExplorerDataBuilder.fillBlockDetails(orphanBlock, orphanBlockDetails)) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + blocksOnSameHeight.push_back(std::move(orphanBlockDetails)); + } + blocks.push_back(std::move(blocksOnSameHeight)); + } + } catch (std::system_error& e) { + return e.code(); + } catch (std::exception&) { return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); } - for (const uint64_t& height : blockHeights) { - if (height > topHeight) { - return make_error_code(CryptoNote::error::REQUEST_ERROR); - } - crypto::hash hash = core.getBlockIdByHeight(height); - Block block; - if (!core.getBlockByHash(hash, block)) { - return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); - } - BlockDetails blockDetails; - if (!blockchainExplorerDataBuilder.fillBlockDetails(block, blockDetails)) { - return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); - } - std::vector blocksOnSameHeight; - blocksOnSameHeight.push_back(std::move(blockDetails)); - blocks.push_back(std::move(blocksOnSameHeight)); - } + return std::error_code(); } -void InProcessNode::getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) { +void InProcessNode::getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) { std::unique_lock lock(mutex); if (state != INITIALIZED) { lock.unlock(); @@ -597,7 +680,7 @@ void InProcessNode::getBlocks(const std::vector& blockHashes, std: std::bind( static_cast< void(InProcessNode::*)( - const std::vector&, + const std::vector&, std::vector&, const Callback& ) @@ -610,27 +693,45 @@ void InProcessNode::getBlocks(const std::vector& blockHashes, std: ); } -void InProcessNode::getBlocksAsync(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) { - std::error_code ec = doGetBlocks(blockHashes, blocks); +void InProcessNode::getBlocksAsync(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) { + std::error_code ec = core.executeLocked( + std::bind( + static_cast< + std::error_code(InProcessNode::*)( + const std::vector&, + std::vector& + ) + >(&InProcessNode::doGetBlocks), + this, + std::cref(blockHashes), + std::ref(blocks) + ) + ); callback(ec); } -std::error_code InProcessNode::doGetBlocks(const std::vector& blockHashes, std::vector& blocks) { - for (const crypto::hash& hash : blockHashes) { - Block block; - if (!core.getBlockByHash(hash, block)) { - return make_error_code(CryptoNote::error::REQUEST_ERROR); +std::error_code InProcessNode::doGetBlocks(const std::vector& blockHashes, std::vector& blocks) { + try { + for (const Crypto::Hash& hash : blockHashes) { + Block block; + if (!core.getBlockByHash(hash, block)) { + return make_error_code(CryptoNote::error::REQUEST_ERROR); + } + BlockDetails blockDetails; + if (!blockchainExplorerDataBuilder.fillBlockDetails(block, blockDetails)) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + blocks.push_back(std::move(blockDetails)); } - BlockDetails blockDetails; - if (!blockchainExplorerDataBuilder.fillBlockDetails(block, blockDetails)) { - return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); - } - blocks.push_back(std::move(blockDetails)); + } catch (std::system_error& e) { + return e.code(); + } catch (std::exception&) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); } return std::error_code(); } -void InProcessNode::getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) { +void InProcessNode::getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps, const Callback& callback) { std::unique_lock lock(mutex); if (state != INITIALIZED) { lock.unlock(); @@ -640,7 +741,89 @@ void InProcessNode::getTransactions(const std::vector& transaction ioService.post( std::bind( - &InProcessNode::getTransactionsAsync, + static_cast< + void(InProcessNode::*)( + uint64_t, + uint64_t, + uint32_t, + std::vector&, + uint32_t&, + const Callback& + ) + >(&InProcessNode::getBlocksAsync), + this, + timestampBegin, + timestampEnd, + blocksNumberLimit, + std::ref(blocks), + std::ref(blocksNumberWithinTimestamps), + callback + ) + ); +} + +void InProcessNode::getBlocksAsync(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps, const Callback& callback) { + std::error_code ec = core.executeLocked( + std::bind( + static_cast< + std::error_code(InProcessNode::*)( + uint64_t, + uint64_t, + uint32_t, + std::vector&, + uint32_t& + ) + >(&InProcessNode::doGetBlocks), + this, + timestampBegin, + timestampEnd, + blocksNumberLimit, + std::ref(blocks), + std::ref(blocksNumberWithinTimestamps) + ) + ); + + callback(ec); +} + +std::error_code InProcessNode::doGetBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps) { + try { + std::vector rawBlocks; + if (!core.getBlocksByTimestamp(timestampBegin, timestampEnd, blocksNumberLimit, rawBlocks, blocksNumberWithinTimestamps)) { + return make_error_code(CryptoNote::error::REQUEST_ERROR); + } + for (const Block& rawBlock : rawBlocks) { + BlockDetails block; + if (!blockchainExplorerDataBuilder.fillBlockDetails(rawBlock, block)) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + blocks.push_back(std::move(block)); + } + } catch (std::system_error& e) { + return e.code(); + } catch (std::exception&) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + return std::error_code(); +} + +void InProcessNode::getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + lock.unlock(); + callback(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + return; + } + + ioService.post( + std::bind( + static_cast< + void(InProcessNode::*)( + const std::vector&, + std::vector&, + const Callback& + ) + >(&InProcessNode::getTransactionsAsync), this, std::cref(transactionHashes), std::ref(transactions), @@ -649,24 +832,154 @@ void InProcessNode::getTransactions(const std::vector& transaction ); } -void InProcessNode::getTransactionsAsync(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) { - std::error_code ec= doGetTransactions(transactionHashes, transactions); +void InProcessNode::getTransactionsAsync(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) { + std::error_code ec = core.executeLocked( + std::bind( + static_cast< + std::error_code(InProcessNode::*)( + const std::vector&, + std::vector& + ) + >(&InProcessNode::doGetTransactions), + this, + std::cref(transactionHashes), + std::ref(transactions) + ) + ); callback(ec); } -std::error_code InProcessNode::doGetTransactions(const std::vector& transactionHashes, std::vector& transactions) { - std::list txs; - std::list missed_txs; - core.getTransactions(transactionHashes, txs, missed_txs, true); - if (missed_txs.size() > 0) { - return make_error_code(CryptoNote::error::REQUEST_ERROR); - } - for (const Transaction& tx : txs) { - TransactionDetails transactionDetails; - if (!blockchainExplorerDataBuilder.fillTransactionDetails(tx, transactionDetails)) { - return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); +std::error_code InProcessNode::doGetTransactions(const std::vector& transactionHashes, std::vector& transactions) { + try { + std::list txs; + std::list missed_txs; + core.getTransactions(transactionHashes, txs, missed_txs, true); + if (missed_txs.size() > 0) { + return make_error_code(CryptoNote::error::REQUEST_ERROR); } - transactions.push_back(std::move(transactionDetails)); + for (const Transaction& tx : txs) { + TransactionDetails transactionDetails; + if (!blockchainExplorerDataBuilder.fillTransactionDetails(tx, transactionDetails)) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + transactions.push_back(std::move(transactionDetails)); + } + } catch (std::system_error& e) { + return e.code(); + } catch (std::exception&) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + return std::error_code(); +} + +void InProcessNode::getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps, const Callback& callback) { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + lock.unlock(); + callback(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + return; + } + + ioService.post( + std::bind( + &InProcessNode::getPoolTransactionsAsync, + this, + timestampBegin, + timestampEnd, + transactionsNumberLimit, + std::ref(transactions), + std::ref(transactionsNumberWithinTimestamps), + callback + ) + ); +} + +void InProcessNode::getPoolTransactionsAsync(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps, const Callback& callback) { + std::error_code ec = core.executeLocked( + std::bind( + &InProcessNode::doGetPoolTransactions, + this, + timestampBegin, + timestampEnd, + transactionsNumberLimit, + std::ref(transactions), + std::ref(transactionsNumberWithinTimestamps) + ) + ); + + callback(ec); +} + +std::error_code InProcessNode::doGetPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps) { + try { + std::vector rawTransactions; + if (!core.getPoolTransactionsByTimestamp(timestampBegin, timestampEnd, transactionsNumberLimit, rawTransactions, transactionsNumberWithinTimestamps)) { + return make_error_code(CryptoNote::error::REQUEST_ERROR); + } + for (const Transaction& rawTransaction : rawTransactions) { + TransactionDetails transactionDetails; + if (!blockchainExplorerDataBuilder.fillTransactionDetails(rawTransaction, transactionDetails)) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + transactions.push_back(std::move(transactionDetails)); + } + } catch (std::system_error& e) { + return e.code(); + } catch (std::exception&) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + return std::error_code(); +} + +void InProcessNode::getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions, const Callback& callback) { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + lock.unlock(); + callback(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + return; + } + + ioService.post( + std::bind( + &InProcessNode::getTransactionsByPaymentIdAsync, + this, + std::cref(paymentId), + std::ref(transactions), + callback + ) + ); +} + +void InProcessNode::getTransactionsByPaymentIdAsync(const Crypto::Hash& paymentId, std::vector& transactions, const Callback& callback) { + std::error_code ec = core.executeLocked( + std::bind( + &InProcessNode::doGetTransactionsByPaymentId, + this, + paymentId, + std::ref(transactions) + ) + ); + + callback(ec); +} + +std::error_code InProcessNode::doGetTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions) { + try { + std::vector rawTransactions; + if (!core.getTransactionsByPaymentId(paymentId, rawTransactions)) { + return make_error_code(CryptoNote::error::REQUEST_ERROR); + } + for (const Transaction& rawTransaction : rawTransactions) { + TransactionDetails transactionDetails; + if (!blockchainExplorerDataBuilder.fillTransactionDetails(rawTransaction, transactionDetails)) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + transactions.push_back(std::move(transactionDetails)); + } + } catch (std::system_error& e) { + return e.code(); + } catch (std::exception&) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); } return std::error_code(); } diff --git a/src/InProcessNode/InProcessNode.h b/src/InProcessNode/InProcessNode.h index 9baed79d..6f805d60 100644 --- a/src/InProcessNode/InProcessNode.h +++ b/src/InProcessNode/InProcessNode.h @@ -18,10 +18,11 @@ #pragma once #include "INode.h" -#include "cryptonote_protocol/ICryptonoteProtocolQuery.h" -#include "cryptonote_protocol/ICryptonoteProtocolObserver.h" -#include "cryptonote_core/ICore.h" -#include "cryptonote_core/ICoreObserver.h" +#include "ITransaction.h" +#include "CryptoNoteProtocol/ICryptoNoteProtocolQuery.h" +#include "CryptoNoteProtocol/ICryptoNoteProtocolObserver.h" +#include "CryptoNoteCore/ICore.h" +#include "CryptoNoteCore/ICoreObserver.h" #include "Common/ObserverManager.h" #include "BlockchainExplorer/BlockchainExplorerDataBuilder.h" @@ -32,9 +33,9 @@ namespace CryptoNote { class core; -class InProcessNode : public INode, public CryptoNote::ICryptonoteProtocolObserver, public CryptoNote::ICoreObserver { +class InProcessNode : public INode, public CryptoNote::ICryptoNoteProtocolObserver, public CryptoNote::ICoreObserver { public: - InProcessNode(CryptoNote::ICore& core, CryptoNote::ICryptonoteProtocolQuery& protocol); + InProcessNode(CryptoNote::ICore& core, CryptoNote::ICryptoNoteProtocolQuery& protocol); InProcessNode(const InProcessNode&) = delete; InProcessNode(InProcessNode&&) = delete; @@ -51,39 +52,44 @@ public: virtual bool removeObserver(INodeObserver* observer) override; virtual size_t getPeerCount() const; - virtual uint64_t getLastLocalBlockHeight() const; - virtual uint64_t getLastKnownBlockHeight() const; - virtual uint64_t getLocalBlockCount() const override; - virtual uint64_t getKnownBlockCount() const override; + virtual uint32_t getLastLocalBlockHeight() const; + virtual uint32_t getLastKnownBlockHeight() const; + virtual uint32_t getLocalBlockCount() const override; + virtual uint32_t getKnownBlockCount() const override; virtual uint64_t getLastLocalBlockTimestamp() const override; - virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) override; - virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) override; + virtual void getNewBlocks(std::vector&& knownBlockIds, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback) override; + virtual void getTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) override; virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) override; virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) override; - virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, - const Callback& callback) override; - virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, - std::vector& deleted_tx_ids, const Callback& callback) override; + virtual void queryBlocks(std::vector&& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, + uint32_t& startHeight, const Callback& callback) override; + virtual void getPoolSymmetricDifference(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, + std::vector>& newTxs, std::vector& deletedTxIds, const Callback& callback) override; + virtual void getMultisignatureOutputByGlobalIndex(uint64_t amount, uint32_t gindex, MultisignatureOutput& out, const Callback& callback) override; - virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) override; - virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) override; - virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) override; + + virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) override; + virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) override; + virtual void getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps, const Callback& callback) override; + virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) override; + virtual void getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions, const Callback& callback) override; + virtual void getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps, const Callback& callback) override; virtual void isSynchronized(bool& syncStatus, const Callback& callback) override; private: virtual void peerCountUpdated(size_t count) override; - virtual void lastKnownBlockHeightUpdated(uint64_t height) override; - virtual void blockchainSynchronized(uint64_t topHeight) override; + virtual void lastKnownBlockHeightUpdated(uint32_t height) override; + virtual void blockchainSynchronized(uint32_t topHeight) override; virtual void blockchainUpdated() override; virtual void poolUpdated() override; - void getNewBlocksAsync(std::list& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); - std::error_code doGetNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight); + void getNewBlocksAsync(std::vector& knownBlockIds, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback); + std::error_code doGetNewBlocks(std::vector&& knownBlockIds, std::vector& newBlocks, uint32_t& startHeight); - void getTransactionOutsGlobalIndicesAsync(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); - std::error_code doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices); + void getTransactionOutsGlobalIndicesAsync(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); + std::error_code doGetTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices); void getRandomOutsByAmountsAsync(std::vector& amounts, uint64_t outsCount, std::vector& result, const Callback& callback); @@ -93,21 +99,32 @@ private: void relayTransactionAsync(const CryptoNote::Transaction& transaction, const Callback& callback); std::error_code doRelayTransaction(const CryptoNote::Transaction& transaction); - void queryBlocksAsync(std::list& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, - const Callback& callback); - std::error_code doQueryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight); + void queryBlocksLiteAsync(std::vector& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, uint32_t& startHeight, + const Callback& callback); + std::error_code doQueryBlocksLite(std::vector&& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, uint32_t& startHeight); - void getPoolSymmetricDifferenceAsync(std::vector& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, - std::vector& deleted_tx_ids, const Callback& callback); + void getPoolSymmetricDifferenceAsync(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, + std::vector>& newTxs, std::vector& deletedTxIds, const Callback& callback); - void getBlocksAsync(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback); - std::error_code doGetBlocks(const std::vector& blockHeights, std::vector>& blocks); + void getOutByMSigGIndexAsync(uint64_t amount, uint32_t gindex, MultisignatureOutput& out, const Callback& callback); - void getBlocksAsync(const std::vector& blockHashes, std::vector& blocks, const Callback& callback); - std::error_code doGetBlocks(const std::vector& blockHashes, std::vector& blocks); + void getBlocksAsync(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback); + std::error_code doGetBlocks(const std::vector& blockHeights, std::vector>& blocks); - void getTransactionsAsync(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback); - std::error_code doGetTransactions(const std::vector& transactionHashes, std::vector& transactions); + void getBlocksAsync(const std::vector& blockHashes, std::vector& blocks, const Callback& callback); + std::error_code doGetBlocks(const std::vector& blockHashes, std::vector& blocks); + + void getBlocksAsync(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps, const Callback& callback); + std::error_code doGetBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps); + + void getTransactionsAsync(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback); + std::error_code doGetTransactions(const std::vector& transactionHashes, std::vector& transactions); + + void getPoolTransactionsAsync(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps, const Callback& callback); + std::error_code doGetPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps); + + void getTransactionsByPaymentIdAsync(const Crypto::Hash& paymentId, std::vector& transactions, const Callback& callback); + std::error_code doGetTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions); void isSynchronizedAsync(bool& syncStatus, const Callback& callback); std::error_code doIsSynchronized(bool& syncStatus); @@ -122,8 +139,8 @@ private: State state; CryptoNote::ICore& core; - CryptoNote::ICryptonoteProtocolQuery& protocol; - tools::ObserverManager observerManager; + CryptoNote::ICryptoNoteProtocolQuery& protocol; + Tools::ObserverManager observerManager; boost::asio::io_service ioService; std::unique_ptr workerThread; diff --git a/src/JsonRpcServer/JsonRpcServer.cpp b/src/JsonRpcServer/JsonRpcServer.cpp new file mode 100755 index 00000000..6939c80b --- /dev/null +++ b/src/JsonRpcServer/JsonRpcServer.cpp @@ -0,0 +1,190 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "JsonRpcServer.h" + +#include +#include +#include +#include +#include +#include "HTTP/HttpParserErrorCodes.h" + +#include +#include +#include +#include +#include "HTTP/HttpParser.h" +#include "HTTP/HttpResponse.h" + +#include "Common/JsonValue.h" +#include "Serialization/JsonInputValueSerializer.h" +#include "Serialization/JsonOutputStreamSerializer.h" + +namespace CryptoNote { + +JsonRpcServer::JsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, Logging::ILogger& loggerGroup) : + HttpServer(sys, loggerGroup), + system(sys), + stopEvent(stopEvent), + logger(loggerGroup, "JsonRpcServer") +{ +} + +void JsonRpcServer::start(const std::string& bindAddress, uint16_t bindPort) { + HttpServer::start(bindAddress, bindPort); + stopEvent.wait(); + HttpServer::stop(); +} + +void JsonRpcServer::processRequest(const CryptoNote::HttpRequest& req, CryptoNote::HttpResponse& resp) { + try { + logger(Logging::TRACE) << "HTTP request came: \n" << req; + + if (req.getUrl() == "/json_rpc") { + std::istringstream jsonInputStream(req.getBody()); + Common::JsonValue jsonRpcRequest; + Common::JsonValue jsonRpcResponse(Common::JsonValue::OBJECT); + + try { + jsonInputStream >> jsonRpcRequest; + } catch (std::runtime_error&) { + logger(Logging::DEBUGGING) << "Couldn't parse request: \"" << req.getBody() << "\""; + makeJsonParsingErrorResponse(jsonRpcResponse); + resp.setStatus(CryptoNote::HttpResponse::STATUS_200); + resp.setBody(jsonRpcResponse.toString()); + return; + } + + processJsonRpcRequest(jsonRpcRequest, jsonRpcResponse); + + std::ostringstream jsonOutputStream; + jsonOutputStream << jsonRpcResponse; + + resp.setStatus(CryptoNote::HttpResponse::STATUS_200); + resp.setBody(jsonOutputStream.str()); + + } else { + logger(Logging::WARNING) << "Requested url \"" << req.getUrl() << "\" is not found"; + resp.setStatus(CryptoNote::HttpResponse::STATUS_404); + return; + } + } catch (std::exception& e) { + logger(Logging::WARNING) << "Error while processing http request: " << e.what(); + resp.setStatus(CryptoNote::HttpResponse::STATUS_500); + } +} + +void JsonRpcServer::prepareJsonResponse(const Common::JsonValue& req, Common::JsonValue& resp) { + using Common::JsonValue; + + if (req.contains("id")) { + resp.insert("id", req("id")); + } + + resp.insert("jsonrpc", "2.0"); +} + +void JsonRpcServer::makeErrorResponse(const std::error_code& ec, Common::JsonValue& resp) { + using Common::JsonValue; + + JsonValue error(JsonValue::OBJECT); + + JsonValue code; + code = static_cast(-32000); //Application specific error code + + JsonValue message; + message = ec.message(); + + JsonValue data(JsonValue::OBJECT); + JsonValue appCode; + appCode = static_cast(ec.value()); + data.insert("application_code", appCode); + + error.insert("code", code); + error.insert("message", message); + error.insert("data", data); + + resp.insert("error", error); +} + +void JsonRpcServer::makeGenericErrorReponse(Common::JsonValue& resp, const char* what, int errorCode) { + using Common::JsonValue; + + JsonValue error(JsonValue::OBJECT); + + JsonValue code; + code = static_cast(errorCode); + + std::string msg; + if (what) { + msg = what; + } else { + msg = "Unknown application error"; + } + + JsonValue message; + message = msg; + + error.insert("code", code); + error.insert("message", message); + + resp.insert("error", error); + +} + +void JsonRpcServer::makeMethodNotFoundResponse(Common::JsonValue& resp) { + using Common::JsonValue; + + JsonValue error(JsonValue::OBJECT); + + JsonValue code; + code = static_cast(-32601); //ambigous declaration of JsonValue::operator= (between int and JsonValue) + + JsonValue message; + message = "Method not found"; + + error.insert("code", code); + error.insert("message", message); + + resp.insert("error", error); +} + +void JsonRpcServer::fillJsonResponse(const Common::JsonValue& v, Common::JsonValue& resp) { + resp.insert("result", v); +} + +void JsonRpcServer::makeJsonParsingErrorResponse(Common::JsonValue& resp) { + using Common::JsonValue; + + resp = JsonValue(JsonValue::OBJECT); + resp.insert("jsonrpc", "2.0"); + resp.insert("id", nullptr); + + JsonValue error(JsonValue::OBJECT); + JsonValue code; + code = static_cast(-32700); //ambigous declaration of JsonValue::operator= (between int and JsonValue) + + JsonValue message = "Parse error"; + + error.insert("code", code); + error.insert("message", message); + + resp.insert("error", error); +} + +} diff --git a/src/payment_service/JsonRpcServer.h b/src/JsonRpcServer/JsonRpcServer.h old mode 100644 new mode 100755 similarity index 61% rename from src/payment_service/JsonRpcServer.h rename to src/JsonRpcServer/JsonRpcServer.h index feb133e9..8dacc6df --- a/src/payment_service/JsonRpcServer.h +++ b/src/JsonRpcServer/JsonRpcServer.h @@ -23,9 +23,7 @@ #include #include "Logging/ILogger.h" #include "Logging/LoggerRef.h" -#include "rpc/HttpServer.h" - -#include "PaymentServiceConfiguration.h" +#include "Rpc/HttpServer.h" namespace CryptoNote { @@ -41,37 +39,32 @@ namespace System { class TcpConnection; } -namespace PaymentService { +namespace CryptoNote { -class WalletService; - -class JsonRpcServer : CryptoNote::HttpServer { +class JsonRpcServer : HttpServer { public: - JsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, WalletService& service, Logging::ILogger& loggerGroup); + JsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, Logging::ILogger& loggerGroup); JsonRpcServer(const JsonRpcServer&) = delete; - void start(const Configuration& config); + void start(const std::string& bindAddress, uint16_t bindPort); + +protected: + static void makeErrorResponse(const std::error_code& ec, Common::JsonValue& resp); + static void makeMethodNotFoundResponse(Common::JsonValue& resp); + static void makeGenericErrorReponse(Common::JsonValue& resp, const char* what, int errorCode = -32001); + static void fillJsonResponse(const Common::JsonValue& v, Common::JsonValue& resp); + static void prepareJsonResponse(const Common::JsonValue& req, Common::JsonValue& resp); + static void makeJsonParsingErrorResponse(Common::JsonValue& resp); + + virtual void processJsonRpcRequest(const Common::JsonValue& req, Common::JsonValue& resp) = 0; private: - void sessionProcedure(System::TcpConnection* tcpConnection); - // HttpServer virtual void processRequest(const CryptoNote::HttpRequest& request, CryptoNote::HttpResponse& response) override; - void processJsonRpcRequest(const Common::JsonValue& req, Common::JsonValue& resp); - void prepareJsonResponse(const Common::JsonValue& req, Common::JsonValue& resp); - - void makeErrorResponse(const std::error_code& ec, Common::JsonValue& resp); - void makeMethodNotFoundResponse(Common::JsonValue& resp); - void makeGenericErrorReponse(Common::JsonValue& resp, const char* what, int errorCode = -32001); - void makeJsonParsingErrorResponse(Common::JsonValue& resp); - - void fillJsonResponse(const Common::JsonValue& v, Common::JsonValue& resp); - System::Dispatcher& system; System::Event& stopEvent; - WalletService& service; Logging::LoggerRef logger; }; -} //namespace PaymentService +} //namespace CryptoNote diff --git a/src/node_rpc_proxy/NodeErrors.cpp b/src/NodeRpcProxy/NodeErrors.cpp similarity index 100% rename from src/node_rpc_proxy/NodeErrors.cpp rename to src/NodeRpcProxy/NodeErrors.cpp diff --git a/src/node_rpc_proxy/NodeErrors.h b/src/NodeRpcProxy/NodeErrors.h similarity index 100% rename from src/node_rpc_proxy/NodeErrors.h rename to src/NodeRpcProxy/NodeErrors.h diff --git a/src/NodeRpcProxy/NodeRpcProxy.cpp b/src/NodeRpcProxy/NodeRpcProxy.cpp new file mode 100644 index 00000000..bc94b35b --- /dev/null +++ b/src/NodeRpcProxy/NodeRpcProxy.cpp @@ -0,0 +1,637 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "NodeRpcProxy.h" +#include "NodeErrors.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Common/StringTools.h" +#include "CryptoNoteCore/CryptoNoteBasicImpl.h" +#include "CryptoNoteCore/CryptoNoteTools.h" +#include "Rpc/CoreRpcServerCommandsDefinitions.h" +#include "Rpc/HttpClient.h" +#include "Rpc/JsonRpc.h" + +#ifndef AUTO_VAL_INIT +#define AUTO_VAL_INIT(n) boost::value_initialized() +#endif + +using namespace Crypto; +using namespace Common; +using namespace System; + +namespace CryptoNote { + +namespace { + +std::error_code interpretResponseStatus(const std::string& status) { + if (CORE_RPC_STATUS_BUSY == status) { + return make_error_code(error::NODE_BUSY); + } else if (CORE_RPC_STATUS_OK != status) { + return make_error_code(error::INTERNAL_NODE_ERROR); + } + return std::error_code(); +} + +} + +NodeRpcProxy::NodeRpcProxy(const std::string& nodeHost, unsigned short nodePort) : + m_rpcTimeout(10000), + m_pullInterval(5000), + m_nodeHost(nodeHost), + m_nodePort(nodePort), + m_lastLocalBlockTimestamp(0) { + resetInternalState(); +} + +NodeRpcProxy::~NodeRpcProxy() { + try { + shutdown(); + } catch (std::exception&) { + } +} + +void NodeRpcProxy::resetInternalState() { + m_stop = false; + m_peerCount.store(0, std::memory_order_relaxed); + m_nodeHeight.store(0, std::memory_order_relaxed); + m_networkHeight.store(0, std::memory_order_relaxed); + m_lastKnowHash = CryptoNote::NULL_HASH; +} + +void NodeRpcProxy::init(const INode::Callback& callback) { + std::lock_guard lock(m_mutex); + + if (m_state != STATE_NOT_INITIALIZED) { + callback(make_error_code(error::ALREADY_INITIALIZED)); + return; + } + + m_state = STATE_INITIALIZING; + resetInternalState(); + m_workerThread = std::thread([this, callback] { + workerThread(callback); + }); +} + +bool NodeRpcProxy::shutdown() { + std::unique_lock lock(m_mutex); + + if (m_state == STATE_NOT_INITIALIZED) { + return true; + } else if (m_state == STATE_INITIALIZING) { + m_cv_initialized.wait(lock, [this] { return m_state != STATE_INITIALIZING; }); + if (m_state == STATE_NOT_INITIALIZED) { + return true; + } + } + + assert(m_state == STATE_INITIALIZED); + assert(m_dispatcher != nullptr); + + m_dispatcher->remoteSpawn([this]() { + m_stop = true; + // Run all spawned contexts + m_dispatcher->yield(); + }); + + if (m_workerThread.joinable()) { + m_workerThread.join(); + } + m_state = STATE_NOT_INITIALIZED; + + return true; +} + +void NodeRpcProxy::workerThread(const INode::Callback& initialized_callback) { + try { + Dispatcher dispatcher; + m_dispatcher = &dispatcher; + ContextGroup contextGroup(dispatcher); + m_context_group = &contextGroup; + HttpClient httpClient(dispatcher, m_nodeHost, m_nodePort); + m_httpClient = &httpClient; + Event httpEvent(dispatcher); + m_httpEvent = &httpEvent; + m_httpEvent->set(); + + { + std::lock_guard lock(m_mutex); + assert(m_state == STATE_INITIALIZING); + m_state = STATE_INITIALIZED; + m_cv_initialized.notify_all(); + } + + initialized_callback(std::error_code()); + + contextGroup.spawn([this]() { + Timer pullTimer(*m_dispatcher); + while (!m_stop) { + updateNodeStatus(); + if (!m_stop) { + pullTimer.sleep(std::chrono::milliseconds(m_pullInterval)); + } + } + }); + + contextGroup.wait(); + // Make sure all remote spawns are executed + m_dispatcher->yield(); + } catch (std::exception&) { + } + + m_dispatcher = nullptr; + m_context_group = nullptr; + m_httpClient = nullptr; + m_httpEvent = nullptr; +} + +void NodeRpcProxy::updateNodeStatus() { + CryptoNote::COMMAND_RPC_GET_LAST_BLOCK_HEADER::request req = AUTO_VAL_INIT(req); + CryptoNote::COMMAND_RPC_GET_LAST_BLOCK_HEADER::response rsp = AUTO_VAL_INIT(rsp); + + std::error_code ec = jsonRpcCommand("getlastblockheader", req, rsp); + + if (!ec) { + Crypto::Hash blockHash; + if (!parse_hash256(rsp.block_header.hash, blockHash)) { + return; + } + + if (blockHash != m_lastKnowHash) { + m_lastKnowHash = blockHash; + m_nodeHeight.store(static_cast(rsp.block_header.height), std::memory_order_relaxed); + m_lastLocalBlockTimestamp.store(rsp.block_header.timestamp, std::memory_order_relaxed); + // TODO request and update network height + m_networkHeight.store(static_cast(rsp.block_header.height), std::memory_order_relaxed); + m_observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, m_networkHeight.load(std::memory_order_relaxed)); + //if (m_networkHeight.load(std::memory_order_relaxed) != rsp.block_header.network_height) { + // m_networkHeight.store(rsp.block_header.height, std::memory_order_relaxed); + // m_observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, m_networkHeight); + //} + m_observerManager.notify(&INodeObserver::localBlockchainUpdated, m_nodeHeight.load(std::memory_order_relaxed)); + } + } + + updatePeerCount(); +} + +void NodeRpcProxy::updatePeerCount() { + CryptoNote::COMMAND_RPC_GET_INFO::request req = AUTO_VAL_INIT(req); + CryptoNote::COMMAND_RPC_GET_INFO::response rsp = AUTO_VAL_INIT(rsp); + + std::error_code ec = jsonCommand("/getinfo", req, rsp); + + if (!ec) { + size_t peerCount = rsp.incoming_connections_count + rsp.outgoing_connections_count; + if (peerCount != m_peerCount) { + m_peerCount = peerCount; + m_observerManager.notify(&INodeObserver::peerCountUpdated, m_peerCount.load(std::memory_order_relaxed)); + } + } +} + +bool NodeRpcProxy::addObserver(INodeObserver* observer) { + return m_observerManager.add(observer); +} + +bool NodeRpcProxy::removeObserver(INodeObserver* observer) { + return m_observerManager.remove(observer); +} + +size_t NodeRpcProxy::getPeerCount() const { + return m_peerCount.load(std::memory_order_relaxed); +} + +uint32_t NodeRpcProxy::getLastLocalBlockHeight() const { + return m_nodeHeight.load(std::memory_order_relaxed); +} + +uint32_t NodeRpcProxy::getLastKnownBlockHeight() const { + return m_networkHeight.load(std::memory_order_relaxed); +} + +uint32_t NodeRpcProxy::getLocalBlockCount() const { + return m_nodeHeight.load(std::memory_order_relaxed); +} + +uint32_t NodeRpcProxy::getKnownBlockCount() const { + return m_networkHeight.load(std::memory_order_relaxed); +} + +uint64_t NodeRpcProxy::getLastLocalBlockTimestamp() const { + return m_lastLocalBlockTimestamp; +} + +void NodeRpcProxy::relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + scheduleRequest(std::bind(&NodeRpcProxy::doRelayTransaction, this, transaction), callback); +} + +void NodeRpcProxy::getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, + std::vector& outs, + const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + scheduleRequest(std::bind(&NodeRpcProxy::doGetRandomOutsByAmounts, this, std::move(amounts), outsCount, std::ref(outs)), + callback); +} + +void NodeRpcProxy::getNewBlocks(std::vector&& knownBlockIds, + std::vector& newBlocks, + uint32_t& startHeight, + const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + scheduleRequest(std::bind(&NodeRpcProxy::doGetNewBlocks, this, std::move(knownBlockIds), std::ref(newBlocks), + std::ref(startHeight)), callback); +} + +void NodeRpcProxy::getTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, + std::vector& outsGlobalIndices, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + scheduleRequest(std::bind(&NodeRpcProxy::doGetTransactionOutsGlobalIndices, this, transactionHash, + std::ref(outsGlobalIndices)), callback); +} + +void NodeRpcProxy::queryBlocks(std::vector&& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, + uint32_t& startHeight, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + scheduleRequest(std::bind(&NodeRpcProxy::doQueryBlocksLite, this, std::move(knownBlockIds), timestamp, + std::ref(newBlocks), std::ref(startHeight)), callback); +} + +void NodeRpcProxy::getPoolSymmetricDifference(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, + std::vector>& newTxs, std::vector& deletedTxIds, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + scheduleRequest([this, knownPoolTxIds, knownBlockId, &isBcActual, &newTxs, &deletedTxIds] () mutable -> std::error_code { + return this->doGetPoolSymmetricDifference(std::move(knownPoolTxIds), knownBlockId, isBcActual, newTxs, deletedTxIds); } , callback); +} + +void NodeRpcProxy::getMultisignatureOutputByGlobalIndex(uint64_t amount, uint32_t gindex, MultisignatureOutput& out, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + // TODO NOT IMPLEMENTED + callback(std::error_code()); +} + +void NodeRpcProxy::getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + // TODO NOT IMPLEMENTED + callback(std::error_code()); +} + +void NodeRpcProxy::getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + // TODO NOT IMPLEMENTED + callback(std::error_code()); +} + +void NodeRpcProxy::getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + // TODO NOT IMPLEMENTED + callback(std::error_code()); +} + +void NodeRpcProxy::getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + // TODO NOT IMPLEMENTED + callback(std::error_code()); +} + +void NodeRpcProxy::getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + // TODO NOT IMPLEMENTED + callback(std::error_code()); +} + +void NodeRpcProxy::getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + // TODO NOT IMPLEMENTED + callback(std::error_code()); +} + +void NodeRpcProxy::isSynchronized(bool& syncStatus, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + // TODO NOT IMPLEMENTED + callback(std::error_code()); +} + +std::error_code NodeRpcProxy::doRelayTransaction(const CryptoNote::Transaction& transaction) { + COMMAND_RPC_SEND_RAW_TX::request req; + COMMAND_RPC_SEND_RAW_TX::response rsp; + req.tx_as_hex = toHex(toBinaryArray(transaction)); + return jsonCommand("/sendrawtransaction", req, rsp); +} + +std::error_code NodeRpcProxy::doGetRandomOutsByAmounts(std::vector& amounts, uint64_t outsCount, + std::vector& outs) { + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req = AUTO_VAL_INIT(req); + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response rsp = AUTO_VAL_INIT(rsp); + req.amounts = std::move(amounts); + req.outs_count = outsCount; + + std::error_code ec = binaryCommand("/getrandom_outs.bin", req, rsp); + if (!ec) { + outs = std::move(rsp.outs); + } + + return ec; +} + +std::error_code NodeRpcProxy::doGetNewBlocks(std::vector& knownBlockIds, + std::vector& newBlocks, + uint32_t& startHeight) { + CryptoNote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req); + CryptoNote::COMMAND_RPC_GET_BLOCKS_FAST::response rsp = AUTO_VAL_INIT(rsp); + req.block_ids = std::move(knownBlockIds); + + std::error_code ec = binaryCommand("/getblocks.bin", req, rsp); + if (!ec) { + newBlocks = std::move(rsp.blocks); + startHeight = static_cast(rsp.start_height); + } + + return ec; +} + +std::error_code NodeRpcProxy::doGetTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, + std::vector& outsGlobalIndices) { + CryptoNote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request req = AUTO_VAL_INIT(req); + CryptoNote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response rsp = AUTO_VAL_INIT(rsp); + req.txid = transactionHash; + + std::error_code ec = binaryCommand("/get_o_indexes.bin", req, rsp); + if (!ec) { + outsGlobalIndices.clear(); + for (auto idx : rsp.o_indexes) { + outsGlobalIndices.push_back(static_cast(idx)); + } + } + + return ec; +} + +std::error_code NodeRpcProxy::doQueryBlocksLite(const std::vector& knownBlockIds, uint64_t timestamp, + std::vector& newBlocks, uint32_t& startHeight) { + CryptoNote::COMMAND_RPC_QUERY_BLOCKS_LITE::request req = AUTO_VAL_INIT(req); + CryptoNote::COMMAND_RPC_QUERY_BLOCKS_LITE::response rsp = AUTO_VAL_INIT(rsp); + + req.blockIds = knownBlockIds; + req.timestamp = timestamp; + + std::error_code ec = binaryCommand("/queryblockslite.bin", req, rsp); + if (ec) { + return ec; + } + + startHeight = static_cast(rsp.startHeight); + + for (auto& item: rsp.items) { + BlockShortEntry bse; + bse.hasBlock = false; + + bse.blockHash = std::move(item.blockId); + if (!item.block.empty()) { + if (!fromBinaryArray(bse.block, asBinaryArray(item.block))) { + return std::make_error_code(std::errc::invalid_argument); + } + + bse.hasBlock = true; + } + + for (const auto& txp: item.txPrefixes) { + TransactionShortInfo tsi; + tsi.txId = txp.txHash; + tsi.txPrefix = txp.txPrefix; + bse.txsShortInfo.push_back(std::move(tsi)); + } + + newBlocks.push_back(std::move(bse)); + } + + return std::error_code(); +} + +std::error_code NodeRpcProxy::doGetPoolSymmetricDifference(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, + std::vector>& newTxs, std::vector& deletedTxIds) { + CryptoNote::COMMAND_RPC_GET_POOL_CHANGES_LITE::request req = AUTO_VAL_INIT(req); + CryptoNote::COMMAND_RPC_GET_POOL_CHANGES_LITE::response rsp = AUTO_VAL_INIT(rsp); + + req.tailBlockId = knownBlockId; + req.knownTxsIds = knownPoolTxIds; + + std::error_code ec = binaryCommand("/get_pool_changes_lite.bin", req, rsp); + + if (ec) { + return ec; + } + + isBcActual = rsp.isTailBlockActual; + if (!isBcActual) { + return ec; + } + + deletedTxIds = std::move(rsp.deletedTxsIds); + + for (const auto& tpi : rsp.addedTxs) { + newTxs.push_back(createTransactionPrefix(tpi.txPrefix, tpi.txHash)); + } + + return ec; +} + +void NodeRpcProxy::scheduleRequest(std::function&& procedure, const Callback& callback) { + // callback is located on stack, so copy it inside binder + class Wrapper { + public: + Wrapper(std::function&, Callback&)>&& _func, + std::function&& _procedure, const Callback& _callback) + : func(std::move(_func)), procedure(std::move(_procedure)), callback(std::move(_callback)) { + } + Wrapper(const Wrapper& other) + : func(other.func), procedure(other.procedure), callback(other.callback) { + } + Wrapper(Wrapper&& other) // must be noexcept + : func(std::move(other.func)), procedure(std::move(other.procedure)), callback(std::move(other.callback)) { + } + void operator()() { + func(procedure, callback); + } + private: + std::function&, Callback&)> func; + std::function procedure; + Callback callback; + }; + assert(m_dispatcher != nullptr && m_context_group != nullptr); + m_dispatcher->remoteSpawn(Wrapper([this](std::function& procedure, Callback& callback) { + m_context_group->spawn(Wrapper([this](std::function& procedure, const Callback& callback) { + if (m_stop) { + callback(std::make_error_code(std::errc::operation_canceled)); + } else { + std::error_code ec = procedure(); + callback(m_stop ? std::make_error_code(std::errc::operation_canceled) : ec); + } + }, std::move(procedure), std::move(callback))); + }, std::move(procedure), callback)); +} + +template +std::error_code NodeRpcProxy::binaryCommand(const std::string& url, const Request& req, Response& res) { + std::error_code ec; + + try { + EventLock eventLock(*m_httpEvent); + invokeBinaryCommand(*m_httpClient, url, req, res); + ec = interpretResponseStatus(res.status); + } catch (const std::exception&) { + ec = make_error_code(error::NETWORK_ERROR); + } + + return ec; +} + +template +std::error_code NodeRpcProxy::jsonCommand(const std::string& url, const Request& req, Response& res) { + std::error_code ec; + + try { + EventLock eventLock(*m_httpEvent); + invokeJsonCommand(*m_httpClient, url, req, res); + ec = interpretResponseStatus(res.status); + } catch (const std::exception&) { + ec = make_error_code(error::NETWORK_ERROR); + } + + return ec; +} + +template +std::error_code NodeRpcProxy::jsonRpcCommand(const std::string& method, const Request& req, Response& res) { + std::error_code ec = make_error_code(error::INTERNAL_NODE_ERROR); + + try { + EventLock eventLock(*m_httpEvent); + + JsonRpc::JsonRpcRequest jsReq; + + jsReq.setMethod(method); + jsReq.setParams(req); + + HttpRequest httpReq; + HttpResponse httpRes; + + httpReq.setUrl("/json_rpc"); + httpReq.setBody(jsReq.getBody()); + + m_httpClient->request(httpReq, httpRes); + + JsonRpc::JsonRpcResponse jsRes; + + if (httpRes.getStatus() == HttpResponse::STATUS_200) { + jsRes.parse(httpRes.getBody()); + if (jsRes.getResult(res)) { + ec = interpretResponseStatus(res.status); + } + } + } catch (const std::exception&) { + ec = make_error_code(error::NETWORK_ERROR); + } + + return ec; +} + +} diff --git a/src/NodeRpcProxy/NodeRpcProxy.h b/src/NodeRpcProxy/NodeRpcProxy.h new file mode 100644 index 00000000..8b5674c0 --- /dev/null +++ b/src/NodeRpcProxy/NodeRpcProxy.h @@ -0,0 +1,139 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include +#include +#include + +#include "Common/ObserverManager.h" +#include "INode.h" + +namespace System { + class ContextGroup; + class Dispatcher; + class Event; +} + +namespace CryptoNote { + +class HttpClient; + +class NodeRpcProxy : public CryptoNote::INode { +public: + NodeRpcProxy(const std::string& nodeHost, unsigned short nodePort); + virtual ~NodeRpcProxy(); + + virtual bool addObserver(CryptoNote::INodeObserver* observer); + virtual bool removeObserver(CryptoNote::INodeObserver* observer); + + virtual void init(const Callback& callback); + virtual bool shutdown(); + + virtual size_t getPeerCount() const; + virtual uint32_t getLastLocalBlockHeight() const; + virtual uint32_t getLastKnownBlockHeight() const; + virtual uint32_t getLocalBlockCount() const override; + virtual uint32_t getKnownBlockCount() const override; + virtual uint64_t getLastLocalBlockTimestamp() const override; + + virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback); + virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback); + virtual void getNewBlocks(std::vector&& knownBlockIds, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback); + virtual void getTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) override; + virtual void queryBlocks(std::vector&& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback) override; + // TODO INodeObserver::poolChanged() notification NOT implemented!!! + virtual void getPoolSymmetricDifference(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, + std::vector>& newTxs, std::vector& deletedTxIds, const Callback& callback) override; + virtual void getMultisignatureOutputByGlobalIndex(uint64_t amount, uint32_t gindex, MultisignatureOutput& out, const Callback& callback) override; + virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) override; + virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) override; + virtual void getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps, const Callback& callback) override; + virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) override; + virtual void getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions, const Callback& callback) override; + virtual void getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps, const Callback& callback) override; + virtual void isSynchronized(bool& syncStatus, const Callback& callback) override; + + unsigned int rpcTimeout() const { return m_rpcTimeout; } + void rpcTimeout(unsigned int val) { m_rpcTimeout = val; } + +private: + void resetInternalState(); + void workerThread(const Callback& initialized_callback); + + void pullNodeStatusAndScheduleTheNext(); + void updateNodeStatus(); + void updatePeerCount(); + + std::error_code doRelayTransaction(const CryptoNote::Transaction& transaction); + std::error_code doGetRandomOutsByAmounts(std::vector& amounts, uint64_t outsCount, + std::vector& result); + std::error_code doGetNewBlocks(std::vector& knownBlockIds, + std::vector& newBlocks, uint32_t& startHeight); + std::error_code doGetTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, + std::vector& outsGlobalIndices); + std::error_code doQueryBlocksLite(const std::vector& knownBlockIds, uint64_t timestamp, + std::vector& newBlocks, uint32_t& startHeight); + std::error_code doGetPoolSymmetricDifference(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, + std::vector>& newTxs, std::vector& deletedTxIds); + + void scheduleRequest(std::function&& procedure, const Callback& callback); + template + std::error_code binaryCommand(const std::string& url, const Request& req, Response& res); + template + std::error_code jsonCommand(const std::string& url, const Request& req, Response& res); + template + std::error_code jsonRpcCommand(const std::string& method, const Request& req, Response& res); + + enum State { + STATE_NOT_INITIALIZED, + STATE_INITIALIZING, + STATE_INITIALIZED + }; + +private: + State m_state = STATE_NOT_INITIALIZED; + std::mutex m_mutex; + std::condition_variable m_cv_initialized; + std::thread m_workerThread; + System::Dispatcher* m_dispatcher = nullptr; + System::ContextGroup* m_context_group = nullptr; + Tools::ObserverManager m_observerManager; + + const std::string m_nodeHost; + const unsigned short m_nodePort; + unsigned int m_rpcTimeout; + HttpClient* m_httpClient = nullptr; + System::Event* m_httpEvent = nullptr; + + uint64_t m_pullInterval; + + // Internal state + bool m_stop = false; + std::atomic m_peerCount; + std::atomic m_nodeHeight; + std::atomic m_networkHeight; + + //protect it with mutex if decided to add worker threads + Crypto::Hash m_lastKnowHash; + std::atomic m_lastLocalBlockTimestamp; +}; + +} diff --git a/src/p2p/connection_context.h b/src/P2p/ConnectionContext.h old mode 100644 new mode 100755 similarity index 72% rename from src/p2p/connection_context.h rename to src/P2p/ConnectionContext.h index 97cf4021..1550e969 --- a/src/p2p/connection_context.h +++ b/src/P2p/ConnectionContext.h @@ -27,7 +27,7 @@ namespace CryptoNote { -struct cryptonote_connection_context { +struct CryptoNoteConnectionContext { uint8_t version; boost::uuids::uuid m_connection_id; uint32_t m_remote_ip = 0; @@ -46,25 +46,25 @@ struct cryptonote_connection_context { }; state m_state = state_befor_handshake; - std::list m_needed_objects; - std::unordered_set m_requested_objects; - uint64_t m_remote_blockchain_height = 0; - uint64_t m_last_response_height = 0; + std::list m_needed_objects; + std::unordered_set m_requested_objects; + uint32_t m_remote_blockchain_height = 0; + uint32_t m_last_response_height = 0; }; -inline std::string get_protocol_state_string(cryptonote_connection_context::state s) { +inline std::string get_protocol_state_string(CryptoNoteConnectionContext::state s) { switch (s) { - case cryptonote_connection_context::state_befor_handshake: + case CryptoNoteConnectionContext::state_befor_handshake: return "state_befor_handshake"; - case cryptonote_connection_context::state_synchronizing: + case CryptoNoteConnectionContext::state_synchronizing: return "state_synchronizing"; - case cryptonote_connection_context::state_idle: + case CryptoNoteConnectionContext::state_idle: return "state_idle"; - case cryptonote_connection_context::state_normal: + case CryptoNoteConnectionContext::state_normal: return "state_normal"; - case cryptonote_connection_context::state_sync_required: + case CryptoNoteConnectionContext::state_sync_required: return "state_sync_required"; - case cryptonote_connection_context::state_shutdown: + case CryptoNoteConnectionContext::state_shutdown: return "state_shutdown"; default: return "unknown"; @@ -74,7 +74,7 @@ inline std::string get_protocol_state_string(cryptonote_connection_context::stat } namespace std { -inline std::ostream& operator << (std::ostream& s, const CryptoNote::cryptonote_connection_context& context) { +inline std::ostream& operator << (std::ostream& s, const CryptoNote::CryptoNoteConnectionContext& context) { return s << "[" << Common::ipAddressToString(context.m_remote_ip) << ":" << context.m_remote_port << (context.m_is_income ? " INC" : " OUT") << "] "; } diff --git a/src/P2p/IP2pNodeInternal.cpp b/src/P2p/IP2pNodeInternal.cpp new file mode 100644 index 00000000..3a46427a --- /dev/null +++ b/src/P2p/IP2pNodeInternal.cpp @@ -0,0 +1,18 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "IP2pNodeInternal.h" diff --git a/src/CryptoNote/UnsignedKeyInput.h b/src/P2p/IP2pNodeInternal.h old mode 100755 new mode 100644 similarity index 60% rename from src/CryptoNote/UnsignedKeyInput.h rename to src/P2p/IP2pNodeInternal.h index 223c7c93..b822a8d1 --- a/src/CryptoNote/UnsignedKeyInput.h +++ b/src/P2p/IP2pNodeInternal.h @@ -17,24 +17,23 @@ #pragma once -#include "../crypto/crypto.h" +#include +#include "P2pProtocolDefinitions.h" namespace CryptoNote { -class UnsignedKeyInput { -public: - UnsignedKeyInput(uint64_t amount, std::vector&& outputs, const crypto::key_image& keyImage); - UnsignedKeyInput(const UnsignedKeyInput& other) = delete; - UnsignedKeyInput& operator=(const UnsignedKeyInput& other) = delete; - uint64_t getAmount() const; - uint32_t getOutputCount() const; - uint32_t getOutputIndex(uint32_t index) const; - const crypto::key_image& getKeyImage() const; +class P2pContext; -private: - uint64_t amount; - std::vector outputs; - crypto::key_image keyImage; +class IP2pNodeInternal { +public: + virtual const CORE_SYNC_DATA& getGenesisPayload() const = 0; + virtual std::list getLocalPeerList() const = 0; + virtual basic_node_data getNodeData() const = 0; + virtual PeerIdType getPeerId() const = 0; + + virtual void handleNodeData(const basic_node_data& node, P2pContext& ctx) = 0; + virtual bool handleRemotePeerList(const std::list& peerlist, time_t local_time) = 0; + virtual void tryPing(P2pContext& ctx) = 0; }; } diff --git a/src/p2p/LevinProtocol.cpp b/src/P2p/LevinProtocol.cpp similarity index 73% rename from src/p2p/LevinProtocol.cpp rename to src/P2p/LevinProtocol.cpp index 29baaaa1..ccecdd5f 100644 --- a/src/p2p/LevinProtocol.cpp +++ b/src/P2p/LevinProtocol.cpp @@ -44,10 +44,14 @@ struct bucket_head2 } +bool LevinProtocol::Command::needReply() const { + return !(isNotify || isResponse); +} + LevinProtocol::LevinProtocol(System::TcpConnection& connection) : m_conn(connection) {} -std::string LevinProtocol::sendBuf(uint32_t command, const std::string& out, bool needResponse, bool readResponse) { +void LevinProtocol::sendMessage(uint32_t command, const BinaryArray& out, bool needResponse) { bucket_head2 head = { 0 }; head.m_signature = LEVIN_SIGNATURE; head.m_cb = out.size(); @@ -57,37 +61,14 @@ std::string LevinProtocol::sendBuf(uint32_t command, const std::string& out, boo head.m_flags = LEVIN_PACKET_REQUEST; // write header and body in one operation - std::string writeBuffer; + BinaryArray writeBuffer; writeBuffer.reserve(sizeof(head) + out.size()); - writeBuffer.append(reinterpret_cast(&head), sizeof(head)); - writeBuffer.append(out); - m_conn.write(reinterpret_cast(writeBuffer.data()), writeBuffer.size()); - std::string response; + Common::VectorOutputStream stream(writeBuffer); + stream.writeSome(&head, sizeof(head)); + stream.writeSome(out.data(), out.size()); - if (readResponse) { - if (!readStrict(reinterpret_cast(&head), sizeof(head))) { - throw std::runtime_error("Levin::sendBuf, failed to read header, peer closed connection"); - } - - if (head.m_signature != LEVIN_SIGNATURE) { - throw std::runtime_error("Levin signature mismatch"); - } - - if (head.m_cb > LEVIN_DEFAULT_MAX_PACKET_SIZE) { - throw std::runtime_error("Levin packet size is too big"); - } - - response.resize(head.m_cb); - - if (response.size()) { - if (!readStrict(&response[0], head.m_cb)) { - throw std::runtime_error("Levin::sendBuf, failed to read body, peer closed connection"); - } - } - } - - return response; + m_conn.write(writeBuffer.data(), writeBuffer.size()); } bool LevinProtocol::readCommand(Command& cmd) { @@ -105,10 +86,10 @@ bool LevinProtocol::readCommand(Command& cmd) { throw std::runtime_error("Levin packet size is too big"); } - std::string buf; - buf.resize(head.m_cb); + BinaryArray buf; - if (!buf.empty()) { + if (head.m_cb != 0) { + buf.resize(head.m_cb); if (!readStrict(&buf[0], head.m_cb)) { return false; } @@ -122,7 +103,7 @@ bool LevinProtocol::readCommand(Command& cmd) { return true; } -void LevinProtocol::sendReply(uint32_t command, const std::string& out, int32_t returnCode) { +void LevinProtocol::sendReply(uint32_t command, const BinaryArray& out, int32_t returnCode) { bucket_head2 head = { 0 }; head.m_signature = LEVIN_SIGNATURE; head.m_cb = out.size(); @@ -147,6 +128,7 @@ bool LevinProtocol::readStrict(void* ptr, size_t size) { if (read == 0) { return false; } + offset += read; } diff --git a/src/p2p/LevinProtocol.h b/src/P2p/LevinProtocol.h old mode 100644 new mode 100755 similarity index 60% rename from src/p2p/LevinProtocol.h rename to src/P2p/LevinProtocol.h index 5d335c9e..37f394da --- a/src/p2p/LevinProtocol.h +++ b/src/P2p/LevinProtocol.h @@ -17,8 +17,11 @@ #pragma once -#include "serialization/KVBinaryInputStreamSerializer.h" -#include "serialization/KVBinaryOutputStreamSerializer.h" +#include "CryptoNote.h" +#include +#include +#include "Serialization/KVBinaryInputStreamSerializer.h" +#include "Serialization/KVBinaryOutputStreamSerializer.h" namespace System { class TcpConnection; @@ -37,41 +40,50 @@ enum class LevinError: int32_t { ERROR_FORMAT = -7, }; +const int32_t LEVIN_PROTOCOL_RETCODE_SUCCESS = 1; + class LevinProtocol { public: LevinProtocol(System::TcpConnection& connection); - template - void invoke(uint32_t command, const Req& req, Resp& resp, bool readResponse = true) { - decode(sendBuf(command, encode(req), true, readResponse), resp); + template + bool invoke(uint32_t command, const Request& request, Response& response) { + sendMessage(command, encode(request), true); + + Command cmd; + readCommand(cmd); + + if (!cmd.isResponse) { + return false; + } + + return decode(cmd.buf, response); } - template - void notify(uint32_t command, const Req& req, int) { - sendBuf(command, encode(req), false, false); + template + void notify(uint32_t command, const Request& request, int) { + sendMessage(command, encode(request), false); } struct Command { uint32_t command; bool isNotify; bool isResponse; - std::string buf; + BinaryArray buf; - bool needReply() const { - return !(isNotify || isResponse); - } + bool needReply() const; }; bool readCommand(Command& cmd); - std::string sendBuf(uint32_t command, const std::string& out, bool needResponse, bool readResponse = false); - void sendReply(uint32_t command, const std::string& out, int32_t returnCode); + void sendMessage(uint32_t command, const BinaryArray& out, bool needResponse); + void sendReply(uint32_t command, const BinaryArray& out, int32_t returnCode); template - static bool decode(const std::string& buf, T& value) { + static bool decode(const BinaryArray& buf, T& value) { try { - std::stringstream stream(buf); + Common::MemoryInputStream stream(buf.data(), buf.size()); KVBinaryInputStreamSerializer serializer(stream); serialize(value, serializer); } catch (std::exception&) { @@ -82,12 +94,13 @@ public: } template - static std::string encode(const T& value) { + static BinaryArray encode(const T& value) { + BinaryArray result; KVBinaryOutputStreamSerializer serializer; serialize(const_cast(value), serializer); - std::stringstream stream; - serializer.write(stream); - return stream.str(); + Common::VectorOutputStream stream(result); + serializer.dump(stream); + return result; } private: diff --git a/src/p2p/net_node.cpp b/src/P2p/NetNode.cpp similarity index 64% rename from src/p2p/net_node.cpp rename to src/P2p/NetNode.cpp index c77ad260..106f0b41 100644 --- a/src/p2p/net_node.cpp +++ b/src/P2p/NetNode.cpp @@ -15,38 +15,43 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "net_node.h" +#include "NetNode.h" #include -#include #include -#include -#include -#include +#include #include #include +#include #include #include +#include +#include #include #include #include #include -#include #include #include #include "version.h" -#include "Common/util.h" +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "Common/Util.h" #include "crypto/crypto.h" -#include "p2p_protocol_defs.h" -#include "net_peerlist_boost_serialization.h" -#include "connection_context.h" +#include "ConnectionContext.h" #include "LevinProtocol.h" +#include "P2pProtocolDefinitions.h" +#include "Serialization/BinaryInputStreamSerializer.h" +#include "Serialization/BinaryOutputStreamSerializer.h" +#include "Serialization/SerializationOverloads.h" + +using namespace Common; using namespace Logging; using namespace CryptoNote; @@ -56,7 +61,7 @@ size_t get_random_index_with_fixed_probability(size_t max_index) { //divide by zero workaround if (!max_index) return 0; - size_t x = crypto::rand() % (max_index + 1); + size_t x = Crypto::rand() % (max_index + 1); return (x*x*x) / (max_index*max_index); //parabola \/ } @@ -95,7 +100,7 @@ void addPortMapping(Logging::LoggerRef& logger, uint32_t port) { } } -bool parse_peer_from_string(net_address& pe, const std::string& node_addr) { +bool parse_peer_from_string(NetworkAddress& pe, const std::string& node_addr) { return Common::parseIpAddressAndPort(pe.ip, pe.port, node_addr); } @@ -107,7 +112,7 @@ namespace CryptoNote namespace { const command_line::arg_descriptor arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol", "0.0.0.0"}; - const command_line::arg_descriptor arg_p2p_bind_port = {"p2p-bind-port", "Port for p2p network protocol", boost::to_string(CryptoNote::P2P_DEFAULT_PORT)}; + const command_line::arg_descriptor arg_p2p_bind_port = {"p2p-bind-port", "Port for p2p network protocol", std::to_string(CryptoNote::P2P_DEFAULT_PORT)}; const command_line::arg_descriptor arg_p2p_external_port = {"p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0}; const command_line::arg_descriptor arg_p2p_allow_local_ip = {"allow-local-ip", "Allow local ip add to peer list, mostly in debug purposes"}; const command_line::arg_descriptor > arg_p2p_add_peer = {"add-peer", "Manually add peer to local peerlist"}; @@ -116,22 +121,68 @@ namespace CryptoNote " If this option is given the options add-priority-node and seed-node are ignored"}; const command_line::arg_descriptor > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect"}; const command_line::arg_descriptor arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; + + std::string print_peerlist_to_string(const std::list& pl) { + time_t now_time = 0; + time(&now_time); + std::stringstream ss; + ss << std::setfill('0') << std::setw(8) << std::hex << std::noshowbase; + for (const auto& pe : pl) { + ss << pe.id << "\t" << pe.adr << " \tlast_seen: " << Common::timeIntervalToString(now_time - pe.last_seen) << std::endl; + } + return ss.str(); + } } - std::string print_peerlist_to_string(const std::list& pl) { - time_t now_time = 0; - time(&now_time); - std::stringstream ss; - ss << std::setfill('0') << std::setw(8) << std::hex << std::noshowbase; - for (const auto& pe : pl) { - ss << pe.id << "\t" << pe.adr << " \tlast_seen: " << Common::timeIntervalToString(now_time - pe.last_seen) << std::endl; + + //----------------------------------------------------------------------------------- + // P2pConnectionContext implementation + //----------------------------------------------------------------------------------- + + bool P2pConnectionContext::pushMessage(P2pMessage&& msg) { + writeQueueSize += msg.size(); + + if (writeQueueSize > P2P_CONNECTION_MAX_WRITE_BUFFER_SIZE) { + logger(DEBUGGING) << *this << "Write queue overflows. Interrupt connection"; + interrupt(); + return false; } - return ss.str(); + + writeQueue.push_back(std::move(msg)); + queueEvent.set(); + return true; + } + + std::vector P2pConnectionContext::popBuffer() { + writeOperationStartTime = TimePoint(); + + while (writeQueue.empty() && !stopped) { + queueEvent.wait(); + } + + std::vector msgs(std::move(writeQueue)); + writeQueue.clear(); + writeQueueSize = 0; + writeOperationStartTime = Clock::now(); + queueEvent.clear(); + return msgs; + } + + uint64_t P2pConnectionContext::writeDuration(TimePoint now) const { // in milliseconds + return writeOperationStartTime == TimePoint() ? 0 : std::chrono::duration_cast(now - writeOperationStartTime).count(); + } + + void P2pConnectionContext::interrupt() { + logger(DEBUGGING) << *this << "Interrupt connection"; + assert(context != nullptr); + stopped = true; + queueEvent.set(); + context->interrupt(); } template - int invokeAdaptor(const std::string& reqBuf, std::string& resBuf, p2p_connection_context& ctx, Handler handler) { + int invokeAdaptor(const BinaryArray& reqBuf, BinaryArray& resBuf, P2pConnectionContext& ctx, Handler handler) { typedef typename Command::request Request; typedef typename Command::response Response; int command = Command::ID; @@ -148,18 +199,18 @@ namespace CryptoNote return ret; } - node_server::node_server(System::Dispatcher& dispatcher, CryptoNote::cryptonote_protocol_handler& payload_handler, Logging::ILogger& log) : + NodeServer::NodeServer(System::Dispatcher& dispatcher, CryptoNote::CryptoNoteProtocolHandler& payload_handler, Logging::ILogger& log) : m_dispatcher(dispatcher), + m_workingContextGroup(dispatcher), m_payload_handler(payload_handler), m_allow_local_ip(false), m_hide_my_port(false), m_network_id(BYTECOIN_NETWORK), logger(log, "node_server"), m_stopEvent(m_dispatcher), - m_shutdownCompleteEvent(m_dispatcher), m_idleTimer(m_dispatcher), m_timedSyncTimer(m_dispatcher), - m_spawnCount(0), + m_timeoutTimer(m_dispatcher), m_stop(false), // intervals // m_peer_handshake_idle_maker_interval(CryptoNote::P2P_DEFAULT_HANDSHAKE_INTERVAL), @@ -167,29 +218,40 @@ namespace CryptoNote m_peerlist_store_interval(60*30, false) { } + void NodeServer::serialize(ISerializer& s) { + uint8_t version = 1; + s(version, "version"); + + if (version != 1) { + return; + } + + s(m_peerlist, "peerlist"); + s(m_config.m_peer_id, "peer_id"); + } #define INVOKE_HANDLER(CMD, Handler) case CMD::ID: { ret = invokeAdaptor(cmd.buf, out, ctx, boost::bind(Handler, this, _1, _2, _3, _4)); break; } - int node_server::handleCommand(const LevinProtocol::Command& cmd, std::string& out, p2p_connection_context& ctx, bool& handled) { + int NodeServer::handleCommand(const LevinProtocol::Command& cmd, BinaryArray& out, P2pConnectionContext& ctx, bool& handled) { int ret = 0; handled = true; if (cmd.isResponse && cmd.command == COMMAND_TIMED_SYNC::ID) { if (!handleTimedSyncResponse(cmd.buf, ctx)) { // invalid response, close connection - ctx.m_state = cryptonote_connection_context::state_shutdown; + ctx.m_state = CryptoNoteConnectionContext::state_shutdown; } return 0; } switch (cmd.command) { - INVOKE_HANDLER(COMMAND_HANDSHAKE, &node_server::handle_handshake) - INVOKE_HANDLER(COMMAND_TIMED_SYNC, &node_server::handle_timed_sync) - INVOKE_HANDLER(COMMAND_PING, &node_server::handle_ping) + INVOKE_HANDLER(COMMAND_HANDSHAKE, &NodeServer::handle_handshake) + INVOKE_HANDLER(COMMAND_TIMED_SYNC, &NodeServer::handle_timed_sync) + INVOKE_HANDLER(COMMAND_PING, &NodeServer::handle_ping) #ifdef ALLOW_DEBUG_COMMANDS - INVOKE_HANDLER(COMMAND_REQUEST_STAT_INFO, &node_server::handle_get_stat_info) - INVOKE_HANDLER(COMMAND_REQUEST_NETWORK_STATE, &node_server::handle_get_network_state) - INVOKE_HANDLER(COMMAND_REQUEST_PEER_ID, &node_server::handle_get_peer_id) + INVOKE_HANDLER(COMMAND_REQUEST_STAT_INFO, &NodeServer::handle_get_stat_info) + INVOKE_HANDLER(COMMAND_REQUEST_NETWORK_STATE, &NodeServer::handle_get_network_state) + INVOKE_HANDLER(COMMAND_REQUEST_PEER_ID, &NodeServer::handle_get_peer_id) #endif default: { handled = false; @@ -204,7 +266,7 @@ namespace CryptoNote //----------------------------------------------------------------------------------- - void node_server::init_options(boost::program_options::options_description& desc) + void NodeServer::init_options(boost::program_options::options_description& desc) { command_line::add_arg(desc, arg_p2p_bind_ip); command_line::add_arg(desc, arg_p2p_bind_port); @@ -218,16 +280,25 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::init_config() { + bool NodeServer::init_config() { try { - std::string state_file_path = m_config_folder + "/" + CryptoNote::parameters::P2P_NET_DATA_FILENAME; - std::ifstream p2p_data; - p2p_data.open(state_file_path, std::ios_base::binary | std::ios_base::in); - - if (!p2p_data.fail()) { - boost::archive::binary_iarchive a(p2p_data); - a >> *this; - } else { + std::string state_file_path = m_config_folder + "/" + m_p2p_state_filename; + bool loaded = false; + + try { + std::ifstream p2p_data; + p2p_data.open(state_file_path, std::ios_base::binary | std::ios_base::in); + + if (!p2p_data.fail()) { + StdInputStream inputStream(p2p_data); + BinaryInputStreamSerializer a(inputStream); + CryptoNote::serialize(*this, a); + loaded = true; + } + } catch (std::exception&) { + } + + if (!loaded) { make_default_config(); } @@ -249,30 +320,30 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - void node_server::for_each_connection(std::function f) + void NodeServer::for_each_connection(std::function f) { for (auto& ctx : m_connections) { - f(ctx.second, ctx.second.peer_id); + f(ctx.second, ctx.second.peerId); } } //----------------------------------------------------------------------------------- - void node_server::externalRelayNotifyToAll(int command, const std::string& data_buff) { + void NodeServer::externalRelayNotifyToAll(int command, const BinaryArray& data_buff) { m_dispatcher.remoteSpawn([this, command, data_buff] { relay_notify_to_all(command, data_buff, nullptr); }); } //----------------------------------------------------------------------------------- - bool node_server::make_default_config() + bool NodeServer::make_default_config() { - m_config.m_peer_id = crypto::rand(); + m_config.m_peer_id = Crypto::rand(); return true; } //----------------------------------------------------------------------------------- - bool node_server::handle_command_line(const boost::program_options::variables_map& vm) + bool NodeServer::handle_command_line(const boost::program_options::variables_map& vm) { m_bind_ip = command_line::get_arg(vm, arg_p2p_bind_ip); m_port = command_line::get_arg(vm, arg_p2p_bind_port); @@ -284,8 +355,8 @@ namespace CryptoNote std::vector perrs = command_line::get_arg(vm, arg_p2p_add_peer); for(const std::string& pr_str: perrs) { - peerlist_entry pe = boost::value_initialized(); - pe.id = crypto::rand(); + PeerlistEntry pe = boost::value_initialized(); + pe.id = Crypto::rand(); bool r = parse_peer_from_string(pe.adr, pr_str); if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to parse address from string: " << pr_str; return false; } m_command_line_peers.push_back(pe); @@ -312,22 +383,29 @@ namespace CryptoNote return true; } - bool node_server::handleConfig(const NetNodeConfig& config) { - m_bind_ip = config.bindIp; - m_port = config.bindPort; - m_external_port = config.externalPort; - m_allow_local_ip = config.allowLocalIp; + bool NodeServer::handleConfig(const NetNodeConfig& config) { + m_bind_ip = config.getBindIp(); + m_port = std::to_string(config.getBindPort()); + m_external_port = config.getExternalPort(); + m_allow_local_ip = config.getAllowLocalIp(); - std::copy(config.peers.begin(), config.peers.end(), std::back_inserter(m_command_line_peers)); - std::copy(config.exclusiveNodes.begin(), config.exclusiveNodes.end(), std::back_inserter(m_exclusive_peers)); - std::copy(config.priorityNodes.begin(), config.priorityNodes.end(), std::back_inserter(m_priority_peers)); - std::copy(config.seedNodes.begin(), config.seedNodes.end(), std::back_inserter(m_seed_nodes)); + auto peers = config.getPeers(); + std::copy(peers.begin(), peers.end(), std::back_inserter(m_command_line_peers)); - m_hide_my_port = config.hideMyPort; + auto exclusiveNodes = config.getExclusiveNodes(); + std::copy(exclusiveNodes.begin(), exclusiveNodes.end(), std::back_inserter(m_exclusive_peers)); + + auto priorityNodes = config.getPriorityNodes(); + std::copy(priorityNodes.begin(), priorityNodes.end(), std::back_inserter(m_priority_peers)); + + auto seedNodes = config.getSeedNodes(); + std::copy(seedNodes.begin(), seedNodes.end(), std::back_inserter(m_seed_nodes)); + + m_hide_my_port = config.getHideMyPort(); return true; } - bool node_server::append_net_address(std::vector& nodes, const std::string& addr) { + bool NodeServer::append_net_address(std::vector& nodes, const std::string& addr) { size_t pos = addr.find_last_of(':'); if (!(std::string::npos != pos && addr.length() - 1 != pos && 0 != pos)) { logger(ERROR, BRIGHT_RED) << "Failed to parse seed address from string: '" << addr << '\''; @@ -341,7 +419,7 @@ namespace CryptoNote System::Ipv4Resolver resolver(m_dispatcher); auto addr = resolver.resolve(host); - nodes.push_back(net_address{hostToNetwork(addr.getValue()), port}); + nodes.push_back(NetworkAddress{hostToNetwork(addr.getValue()), port}); logger(TRACE) << "Added seed node: " << nodes.back() << " (" << host << ")"; @@ -356,8 +434,8 @@ namespace CryptoNote //----------------------------------------------------------------------------------- - bool node_server::init(const NetNodeConfig& config, bool testnet) { - if (!testnet) { + bool NodeServer::init(const NetNodeConfig& config) { + if (!config.getTestnet()) { for (auto seed : CryptoNote::SEED_NODES) { append_net_address(m_seed_nodes, seed); } @@ -369,9 +447,10 @@ namespace CryptoNote logger(ERROR, BRIGHT_RED) << "Failed to handle command line"; return false; } - m_config_folder = config.configFolder; + m_config_folder = config.getConfigFolder(); + m_p2p_state_filename = config.getP2pStateFilename(); - if (!init_config()) { + if (!init_config()) { logger(ERROR, BRIGHT_RED) << "Failed to init config."; return false; } @@ -410,66 +489,52 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - CryptoNote::cryptonote_protocol_handler& node_server::get_payload_object() + CryptoNote::CryptoNoteProtocolHandler& NodeServer::get_payload_object() { return m_payload_handler; } //----------------------------------------------------------------------------------- - bool node_server::run() { + bool NodeServer::run() { logger(INFO) << "Starting node_server"; - ++m_spawnCount; - m_dispatcher.spawn(std::bind(&node_server::acceptLoop, this)); - - ++m_spawnCount; - m_dispatcher.spawn(std::bind(&node_server::onIdle, this)); - - ++m_spawnCount; - m_dispatcher.spawn(std::bind(&node_server::timedSyncLoop, this)); + m_workingContextGroup.spawn(std::bind(&NodeServer::acceptLoop, this)); + m_workingContextGroup.spawn(std::bind(&NodeServer::onIdle, this)); + m_workingContextGroup.spawn(std::bind(&NodeServer::timedSyncLoop, this)); + m_workingContextGroup.spawn(std::bind(&NodeServer::timeoutLoop, this)); m_stopEvent.wait(); - logger(INFO) << "Stopping node_server..."; + logger(INFO) << "Stopping NodeServer and it's" << m_connections.size() << " connections..."; + m_workingContextGroup.interrupt(); + m_workingContextGroup.wait(); - m_listener.stop(); - m_idleTimer.stop(); - m_timedSyncTimer.stop(); - - logger(INFO) << "Stopping " << m_connections.size() << " connections"; - - for (auto& conn : m_connections) { - conn.second.connection.stop(); - } - - m_shutdownCompleteEvent.wait(); - - logger(INFO) << "net_service loop stopped"; + logger(INFO) << "NodeServer loop stopped"; return true; } //----------------------------------------------------------------------------------- - uint64_t node_server::get_connections_count() { + uint64_t NodeServer::get_connections_count() { return m_connections.size(); } //----------------------------------------------------------------------------------- - bool node_server::deinit() { + bool NodeServer::deinit() { return store_config(); } //----------------------------------------------------------------------------------- - bool node_server::store_config() + bool NodeServer::store_config() { try { - if (!tools::create_directories_if_necessary(m_config_folder)) { + if (!Tools::create_directories_if_necessary(m_config_folder)) { logger(INFO) << "Failed to create data directory: " << m_config_folder; return false; } - std::string state_file_path = m_config_folder + "/" + CryptoNote::parameters::P2P_NET_DATA_FILENAME; + std::string state_file_path = m_config_folder + "/" + m_p2p_state_filename; std::ofstream p2p_data; p2p_data.open(state_file_path, std::ios_base::binary | std::ios_base::out | std::ios::trunc); if (p2p_data.fail()) { @@ -477,8 +542,9 @@ namespace CryptoNote return false; }; - boost::archive::binary_oarchive a(p2p_data); - a << *this; + StdOutputStream stream(p2p_data); + BinaryOutputStreamSerializer a(stream); + CryptoNote::serialize(*this, a); return true; } catch (const std::exception& e) { logger(WARNING) << "store_config failed: " << e.what(); @@ -488,7 +554,7 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::send_stop_signal() { + bool NodeServer::sendStopSignal() { m_stop = true; m_dispatcher.remoteSpawn([this] { @@ -501,7 +567,7 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::handshake(CryptoNote::LevinProtocol& proto, p2p_connection_context& context, bool just_take_peerlist) { + bool NodeServer::handshake(CryptoNote::LevinProtocol& proto, P2pConnectionContext& context, bool just_take_peerlist) { COMMAND_HANDSHAKE::request arg; COMMAND_HANDSHAKE::response rsp; get_local_node_data(arg.node_data); @@ -530,7 +596,7 @@ namespace CryptoNote return false; } - context.peer_id = rsp.node_data.peer_id; + context.peerId = rsp.node_data.peer_id; m_peerlist.set_peer_just_seen(rsp.node_data.peer_id, context.m_remote_ip, context.m_remote_port); if (rsp.node_data.peer_id == m_config.m_peer_id) { @@ -543,29 +609,23 @@ namespace CryptoNote } - bool node_server::timedSync() { + bool NodeServer::timedSync() { COMMAND_TIMED_SYNC::request arg = boost::value_initialized(); m_payload_handler.get_payload_sync_data(arg.payload_data); auto cmdBuf = LevinProtocol::encode(arg); - forEachConnection([&](p2p_connection_context& conn) { - if (conn.peer_id && - (conn.m_state == cryptonote_connection_context::state_normal || - conn.m_state == cryptonote_connection_context::state_idle)) { - try { - System::LatchGuard latch(conn.writeLatch); - System::EventLock lock(conn.connectionEvent); - LevinProtocol(conn.connection).sendBuf(COMMAND_TIMED_SYNC::ID, cmdBuf, true, false); - } catch (std::exception&) { - logger(DEBUGGING) << conn << "Failed to send COMMAND_TIMED_SYNC"; - } + forEachConnection([&](P2pConnectionContext& conn) { + if (conn.peerId && + (conn.m_state == CryptoNoteConnectionContext::state_normal || + conn.m_state == CryptoNoteConnectionContext::state_idle)) { + conn.pushMessage(P2pMessage(P2pMessage::COMMAND, COMMAND_TIMED_SYNC::ID, cmdBuf)); } }); return true; } - bool node_server::handleTimedSyncResponse(const std::string& in, p2p_connection_context& context) { + bool NodeServer::handleTimedSyncResponse(const BinaryArray& in, P2pConnectionContext& context) { COMMAND_TIMED_SYNC::response rsp; if (!LevinProtocol::decode(in, rsp)) { return false; @@ -577,7 +637,7 @@ namespace CryptoNote } if (!context.m_is_income) { - m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_ip, context.m_remote_port); + m_peerlist.set_peer_just_seen(context.peerId, context.m_remote_ip, context.m_remote_port); } if (!m_payload_handler.process_payload_sync_data(rsp.payload_data, context, false)) { @@ -587,7 +647,7 @@ namespace CryptoNote return true; } - void node_server::forEachConnection(std::function action) { + void NodeServer::forEachConnection(std::function action) { // create copy of connection ids because the list can be changed during action std::vector connectionIds; @@ -605,13 +665,13 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::is_peer_used(const peerlist_entry& peer) { + bool NodeServer::is_peer_used(const PeerlistEntry& peer) { if(m_config.m_peer_id == peer.id) return true; //dont make connections to ourself for (const auto& kv : m_connections) { const auto& cntxt = kv.second; - if(cntxt.peer_id == peer.id || (!cntxt.m_is_income && peer.adr.ip == cntxt.m_remote_ip && peer.adr.port == cntxt.m_remote_port)) { + if(cntxt.peerId == peer.id || (!cntxt.m_is_income && peer.adr.ip == cntxt.m_remote_ip && peer.adr.port == cntxt.m_remote_port)) { return true; } } @@ -619,7 +679,7 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::is_addr_connected(const net_address& peer) { + bool NodeServer::is_addr_connected(const NetworkAddress& peer) { for (const auto& conn : m_connections) { if (!conn.second.m_is_income && peer.ip == conn.second.m_remote_ip && peer.port == conn.second.m_remote_port) { return true; @@ -629,47 +689,33 @@ namespace CryptoNote } - bool node_server::try_to_connect_and_handshake_with_new_peer(const net_address& na, bool just_take_peerlist, uint64_t last_seen_stamp, bool white) { + bool NodeServer::try_to_connect_and_handshake_with_new_peer(const NetworkAddress& na, bool just_take_peerlist, uint64_t last_seen_stamp, bool white) { logger(DEBUGGING) << "Connecting to " << na << " (white=" << white << ", last_seen: " << (last_seen_stamp ? Common::timeIntervalToString(time(NULL) - last_seen_stamp) : "never") << ")..."; try { - System::TcpConnector connector(m_dispatcher); - - System::Event connectorTimeoutEvent(m_dispatcher); - System::Timer connectorTimeoutTimer(m_dispatcher); - - m_dispatcher.spawn([&]() { - try { - connectorTimeoutTimer.sleep(std::chrono::milliseconds(m_config.m_net_config.connection_timeout)); - connector.stop(); - } catch (std::exception&) { - } - - connectorTimeoutEvent.set(); - }); - System::TcpConnection connection; try { - connection = - connector.connect(System::Ipv4Address(Common::ipAddressToString(na.ip)), static_cast(na.port)); + System::Context connectionContext(m_dispatcher, [&] { + System::TcpConnector connector(m_dispatcher); + return connector.connect(System::Ipv4Address(Common::ipAddressToString(na.ip)), static_cast(na.port)); + }); + + System::Context<> timeoutContext(m_dispatcher, [&] { + System::Timer(m_dispatcher).sleep(std::chrono::milliseconds(m_config.m_net_config.connection_timeout)); + connectionContext.interrupt(); + logger(DEBUGGING) << "Connection to " << na <<" timed out, interrupt it"; + }); + + connection = std::move(connectionContext.get()); } catch (System::InterruptedException&) { - connectorTimeoutEvent.wait(); + logger(DEBUGGING) << "Connection timed out"; return false; - } catch (std::exception& e) { - connectorTimeoutTimer.stop(); - connectorTimeoutEvent.wait(); - throw; } - p2p_connection_context ctx(m_dispatcher, std::move(connection)); - - connectorTimeoutTimer.stop(); - connectorTimeoutEvent.wait(); - - // p2p_connection_context ctx(m_dispatcher, std::move(connector.connect())); + P2pConnectionContext ctx(m_dispatcher, logger.getLogger(), std::move(connection)); ctx.m_connection_id = boost::uuids::random_generator()(); ctx.m_remote_ip = na.ip; @@ -677,73 +723,48 @@ namespace CryptoNote ctx.m_is_income = false; ctx.m_started = time(nullptr); - System::Event handshakeTimeoutFinishedEvent(m_dispatcher); - System::Event handshakeFinishedEvent(m_dispatcher); - System::Timer handshakeTimeoutTimer(m_dispatcher); - m_dispatcher.spawn([&] { - try { - handshakeTimeoutTimer.sleep(std::chrono::milliseconds(m_config.m_net_config.connection_timeout)); - ctx.connection.stop(); - } catch (std::exception& e) { + + try { + System::Context handshakeContext(m_dispatcher, [&] { + CryptoNote::LevinProtocol proto(ctx.connection); + return handshake(proto, ctx, just_take_peerlist); + }); + + System::Context<> timeoutContext(m_dispatcher, [&] { + System::Timer(m_dispatcher).sleep(std::chrono::milliseconds(m_config.m_net_config.connection_timeout)); + handshakeContext.interrupt(); + logger(DEBUGGING) << "Handshake with " << na << " timed out, interrupt it"; + }); + + if (!handshakeContext.get()) { + logger(WARNING) << "Failed to HANDSHAKE with peer " << na; + return false; } - - handshakeTimeoutFinishedEvent.set(); - }); - - CryptoNote::LevinProtocol proto(ctx.connection); - bool error = false; - std::exception_ptr exceptionHolder; - m_dispatcher.spawn([&] { - try { - if (!handshake(proto, ctx, just_take_peerlist)) { - logger(WARNING) << "Failed to HANDSHAKE with peer " << na; - error = true; - } - - } catch (System::InterruptedException&) { - error = true; - handshakeFinishedEvent.set(); - return; - } catch (...) { - exceptionHolder = std::current_exception(); - } - - handshakeTimeoutTimer.stop(); - handshakeFinishedEvent.set(); - }); - - handshakeFinishedEvent.wait(); - handshakeTimeoutFinishedEvent.wait(); - if (error) { + } catch (System::InterruptedException&) { + logger(DEBUGGING) << "Handshake timed out"; return false; } - if (exceptionHolder != nullptr) { - std::rethrow_exception(exceptionHolder); - } - if (just_take_peerlist) { logger(Logging::DEBUGGING, Logging::BRIGHT_GREEN) << ctx << "CONNECTION HANDSHAKED OK AND CLOSED."; return true; } - peerlist_entry pe_local = boost::value_initialized(); + PeerlistEntry pe_local = boost::value_initialized(); pe_local.adr = na; - pe_local.id = ctx.peer_id; - time(&pe_local.last_seen); + pe_local.id = ctx.peerId; + pe_local.last_seen = time(nullptr); m_peerlist.append_with_peer_white(pe_local); if (m_stop) { throw System::InterruptedException(); } - auto key = ctx.m_connection_id; - auto iter = m_connections.emplace(key, std::move(ctx)).first; + auto iter = m_connections.emplace(ctx.m_connection_id, std::move(ctx)).first; const boost::uuids::uuid& connectionId = iter->first; - p2p_connection_context& connectionContext = iter->second; + P2pConnectionContext& connectionContext = iter->second; - ++m_spawnCount; - m_dispatcher.spawn(std::bind(&node_server::connectionHandler, this, std::cref(connectionId), std::ref(connectionContext))); + m_workingContextGroup.spawn(std::bind(&NodeServer::connectionHandler, this, std::cref(connectionId), std::ref(connectionContext))); return true; } catch (System::InterruptedException&) { @@ -757,7 +778,7 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::make_new_connection_from_peerlist(bool use_white_list) + bool NodeServer::make_new_connection_from_peerlist(bool use_white_list) { size_t local_peers_count = use_white_list ? m_peerlist.get_white_peers_count():m_peerlist.get_gray_peers_count(); if(!local_peers_count) @@ -778,7 +799,7 @@ namespace CryptoNote continue; tried_peers.insert(random_index); - peerlist_entry pe = boost::value_initialized(); + PeerlistEntry pe = boost::value_initialized(); bool r = use_white_list ? m_peerlist.get_white_peer_by_index(pe, random_index):m_peerlist.get_gray_peer_by_index(pe, random_index); if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to get random peer from peerlist(white:" << use_white_list << ")"; return false; } @@ -799,7 +820,7 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::connections_maker() + bool NodeServer::connections_maker() { if (!connect_to_peerlist(m_exclusive_peers)) { return false; @@ -811,7 +832,7 @@ namespace CryptoNote if(!m_peerlist.get_white_peers_count() && m_seed_nodes.size()) { size_t try_count = 0; - size_t current_index = crypto::rand()%m_seed_nodes.size(); + size_t current_index = Crypto::rand() % m_seed_nodes.size(); while(true) { if(try_to_connect_and_handshake_with_new_peer(m_seed_nodes[current_index], true)) @@ -856,7 +877,7 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::make_expected_connections_count(bool white_list, size_t expected_connections) + bool NodeServer::make_expected_connections_count(bool white_list, size_t expected_connections) { size_t conn_count = get_outgoing_connections_count(); //add new connections from white peers @@ -873,7 +894,7 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - size_t node_server::get_outgoing_connections_count() { + size_t NodeServer::get_outgoing_connections_count() { size_t count = 0; for (const auto& cntxt : m_connections) { if (!cntxt.second.m_is_income) @@ -883,10 +904,10 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::idle_worker() { + bool NodeServer::idle_worker() { try { - m_connections_maker_interval.call(std::bind(&node_server::connections_maker, this)); - m_peerlist_store_interval.call(std::bind(&node_server::store_config, this)); + m_connections_maker_interval.call(std::bind(&NodeServer::connections_maker, this)); + m_peerlist_store_interval.call(std::bind(&NodeServer::store_config, this)); } catch (std::exception& e) { logger(DEBUGGING) << "exception in idle_worker: " << e.what(); } @@ -894,16 +915,16 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::fix_time_delta(std::list& local_peerlist, time_t local_time, int64_t& delta) + bool NodeServer::fix_time_delta(std::list& local_peerlist, time_t local_time, int64_t& delta) { //fix time delta time_t now = 0; time(&now); delta = now - local_time; - BOOST_FOREACH(peerlist_entry& be, local_peerlist) + BOOST_FOREACH(PeerlistEntry& be, local_peerlist) { - if(be.last_seen > local_time) + if(be.last_seen > uint64_t(local_time)) { logger(ERROR) << "FOUND FUTURE peerlist for entry " << be.adr << " last_seen: " << be.last_seen << ", local_time(on remote node):" << local_time; return false; @@ -915,10 +936,10 @@ namespace CryptoNote //----------------------------------------------------------------------------------- - bool node_server::handle_remote_peerlist(const std::list& peerlist, time_t local_time, const cryptonote_connection_context& context) + bool NodeServer::handle_remote_peerlist(const std::list& peerlist, time_t local_time, const CryptoNoteConnectionContext& context) { int64_t delta = 0; - std::list peerlist_ = peerlist; + std::list peerlist_ = peerlist; if(!fix_time_delta(peerlist_, local_time, delta)) return false; logger(Logging::TRACE) << context << "REMOTE PEERLIST: TIME_DELTA: " << delta << ", remote peerlist size=" << peerlist_.size(); @@ -927,7 +948,7 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::get_local_node_data(basic_node_data& node_data) + bool NodeServer::get_local_node_data(basic_node_data& node_data) { node_data.version = P2PProtocolVersion::CURRENT; time_t local_time; @@ -944,7 +965,7 @@ namespace CryptoNote //----------------------------------------------------------------------------------- #ifdef ALLOW_DEBUG_COMMANDS - bool node_server::check_trust(const proof_of_trust &tr) { + bool NodeServer::check_trust(const proof_of_trust &tr) { uint64_t local_time = time(NULL); uint64_t time_delata = local_time > tr.time ? local_time - tr.time : tr.time - local_time; @@ -963,10 +984,10 @@ namespace CryptoNote return false; } - crypto::public_key pk; + Crypto::PublicKey pk; Common::podFromHex(CryptoNote::P2P_STAT_TRUSTED_PUB_KEY, pk); - crypto::hash h = get_proof_of_trust_hash(tr); - if (!crypto::check_signature(h, pk, tr.sign)) { + Crypto::Hash h = get_proof_of_trust_hash(tr); + if (!Crypto::check_signature(h, pk, tr.sign)) { logger(ERROR) << "check_trust failed: sign check failed"; return false; } @@ -977,25 +998,25 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - int node_server::handle_get_stat_info(int command, COMMAND_REQUEST_STAT_INFO::request& arg, COMMAND_REQUEST_STAT_INFO::response& rsp, p2p_connection_context& context) + int NodeServer::handle_get_stat_info(int command, COMMAND_REQUEST_STAT_INFO::request& arg, COMMAND_REQUEST_STAT_INFO::response& rsp, P2pConnectionContext& context) { if(!check_trust(arg.tr)) { - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } rsp.connections_count = get_connections_count(); rsp.incoming_connections_count = rsp.connections_count - get_outgoing_connections_count(); rsp.version = PROJECT_VERSION_LONG; - rsp.os_version = tools::get_os_version_string(); + rsp.os_version = Tools::get_os_version_string(); m_payload_handler.get_stat_info(rsp.payload_info); return 1; } //----------------------------------------------------------------------------------- - int node_server::handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, p2p_connection_context& context) + int NodeServer::handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, P2pConnectionContext& context) { if(!check_trust(arg.tr)) { - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } @@ -1003,7 +1024,7 @@ namespace CryptoNote connection_entry ce; ce.adr.ip = cntxt.second.m_remote_ip; ce.adr.port = cntxt.second.m_remote_port; - ce.id = cntxt.second.peer_id; + ce.id = cntxt.second.peerId; ce.is_income = cntxt.second.m_is_income; rsp.connections_list.push_back(ce); } @@ -1015,7 +1036,7 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - int node_server::handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, p2p_connection_context& context) + int NodeServer::handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, P2pConnectionContext& context) { rsp.my_id = m_config.m_peer_id; return 1; @@ -1024,43 +1045,30 @@ namespace CryptoNote //----------------------------------------------------------------------------------- - void node_server::relay_notify_to_all(int command, const std::string& data_buff, const net_connection_id* excludeConnection) { + void NodeServer::relay_notify_to_all(int command, const BinaryArray& data_buff, const net_connection_id* excludeConnection) { net_connection_id excludeId = excludeConnection ? *excludeConnection : boost::value_initialized(); - forEachConnection([&](p2p_connection_context& conn) { - if (conn.peer_id && conn.m_connection_id != excludeId) { - try { - logger(TRACE) << conn << "Relay command " << command; - System::LatchGuard latch(conn.writeLatch); - System::EventLock lock(conn.connectionEvent); - LevinProtocol(conn.connection).sendBuf(command, data_buff, false); - } catch (const std::exception& e) { - logger(DEBUGGING) << conn << "Failed to relay notification id=" << command << ": " << e.what(); - } + forEachConnection([&](P2pConnectionContext& conn) { + if (conn.peerId && conn.m_connection_id != excludeId) { + conn.pushMessage(P2pMessage(P2pMessage::NOTIFY, command, data_buff)); } }); } //----------------------------------------------------------------------------------- - bool node_server::invoke_notify_to_peer(int command, const std::string& req_buff, const cryptonote_connection_context& context) { + bool NodeServer::invoke_notify_to_peer(int command, const BinaryArray& buffer, const CryptoNoteConnectionContext& context) { auto it = m_connections.find(context.m_connection_id); if (it == m_connections.end()) { return false; } - try { - System::LatchGuard latch(it->second.writeLatch); - System::EventLock lock(it->second.connectionEvent); - LevinProtocol(it->second.connection).sendBuf(command, req_buff, false); - } catch (const std::exception& e) { - logger(DEBUGGING) << it->second << "Failed to invoke notification id=" << command << ": " << e.what(); - } + it->second.pushMessage(P2pMessage(P2pMessage::NOTIFY, command, buffer)); return true; } //----------------------------------------------------------------------------------- - bool node_server::try_ping(basic_node_data& node_data, p2p_connection_context& context) + bool NodeServer::try_ping(basic_node_data& node_data, P2pConnectionContext& context) { if(!node_data.my_port) return false; @@ -1071,7 +1079,7 @@ namespace CryptoNote std::string ip = Common::ipAddressToString(actual_ip); auto port = node_data.my_port; - peerid_type pr = node_data.peer_id; + PeerIdType pr = node_data.peer_id; try { System::TcpConnector connector(m_dispatcher); @@ -1098,11 +1106,11 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - int node_server::handle_timed_sync(int command, COMMAND_TIMED_SYNC::request& arg, COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context) + int NodeServer::handle_timed_sync(int command, COMMAND_TIMED_SYNC::request& arg, COMMAND_TIMED_SYNC::response& rsp, P2pConnectionContext& context) { if(!m_payload_handler.process_payload_sync_data(arg.payload_data, context, false)) { logger(Logging::ERROR) << context << "Failed to process_payload_sync_data(), dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } @@ -1115,46 +1123,46 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - int node_server::handle_handshake(int command, COMMAND_HANDSHAKE::request& arg, COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context) + int NodeServer::handle_handshake(int command, COMMAND_HANDSHAKE::request& arg, COMMAND_HANDSHAKE::response& rsp, P2pConnectionContext& context) { context.version = arg.node_data.version; if (arg.node_data.network_id != m_network_id) { logger(Logging::INFO) << context << "WRONG NETWORK AGENT CONNECTED! id=" << arg.node_data.network_id; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } if(!context.m_is_income) { logger(Logging::ERROR) << context << "COMMAND_HANDSHAKE came not from incoming connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } - if(context.peer_id) { + if(context.peerId) { logger(Logging::ERROR) << context << "COMMAND_HANDSHAKE came, but seems that connection already have associated peer_id (double COMMAND_HANDSHAKE?)"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } if(!m_payload_handler.process_payload_sync_data(arg.payload_data, context, true)) { logger(Logging::ERROR) << context << "COMMAND_HANDSHAKE came, but process_payload_sync_data returned false, dropping connection."; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } //associate peer_id with this connection - context.peer_id = arg.node_data.peer_id; + context.peerId = arg.node_data.peer_id; if(arg.node_data.peer_id != m_config.m_peer_id && arg.node_data.my_port) { - peerid_type peer_id_l = arg.node_data.peer_id; + PeerIdType peer_id_l = arg.node_data.peer_id; uint32_t port_l = arg.node_data.my_port; if (try_ping(arg.node_data, context)) { //called only(!) if success pinged, update local peerlist - peerlist_entry pe; + PeerlistEntry pe; pe.adr.ip = context.m_remote_ip; pe.adr.port = port_l; - time(&pe.last_seen); + pe.last_seen = time(nullptr); pe.id = peer_id_l; m_peerlist.append_with_peer_white(pe); @@ -1172,7 +1180,7 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - int node_server::handle_ping(int command, COMMAND_PING::request& arg, COMMAND_PING::response& rsp, p2p_connection_context& context) + int NodeServer::handle_ping(int command, COMMAND_PING::request& arg, COMMAND_PING::response& rsp, P2pConnectionContext& context) { logger(Logging::TRACE) << context << "COMMAND_PING"; rsp.status = PING_OK_RESPONSE_STATUS_TEXT; @@ -1181,29 +1189,29 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::log_peerlist() + bool NodeServer::log_peerlist() { - std::list pl_wite; - std::list pl_gray; + std::list pl_wite; + std::list pl_gray; m_peerlist.get_peerlist_full(pl_gray, pl_wite); logger(INFO) << ENDL << "Peerlist white:" << ENDL << print_peerlist_to_string(pl_wite) << ENDL << "Peerlist gray:" << ENDL << print_peerlist_to_string(pl_gray) ; return true; } //----------------------------------------------------------------------------------- - bool node_server::log_connections() { + bool NodeServer::log_connections() { logger(INFO) << "Connections: \r\n" << print_connections_container() ; return true; } //----------------------------------------------------------------------------------- - std::string node_server::print_connections_container() { + std::string NodeServer::print_connections_container() { std::stringstream ss; for (const auto& cntxt : m_connections) { ss << Common::ipAddressToString(cntxt.second.m_remote_ip) << ":" << cntxt.second.m_remote_port - << " \t\tpeer_id " << cntxt.second.peer_id + << " \t\tpeer_id " << cntxt.second.peerId << " \t\tconn_id " << cntxt.second.m_connection_id << (cntxt.second.m_is_income ? " INC" : " OUT") << std::endl; } @@ -1212,27 +1220,27 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - void node_server::on_connection_new(p2p_connection_context& context) + void NodeServer::on_connection_new(P2pConnectionContext& context) { logger(TRACE) << context << "NEW CONNECTION"; m_payload_handler.onConnectionOpened(context); } //----------------------------------------------------------------------------------- - void node_server::on_connection_close(p2p_connection_context& context) + void NodeServer::on_connection_close(P2pConnectionContext& context) { logger(TRACE) << context << "CLOSE CONNECTION"; m_payload_handler.onConnectionClosed(context); } - bool node_server::is_priority_node(const net_address& na) + bool NodeServer::is_priority_node(const NetworkAddress& na) { return (std::find(m_priority_peers.begin(), m_priority_peers.end(), na) != m_priority_peers.end()) || (std::find(m_exclusive_peers.begin(), m_exclusive_peers.end(), na) != m_exclusive_peers.end()); } - bool node_server::connect_to_peerlist(const std::vector& peers) + bool NodeServer::connect_to_peerlist(const std::vector& peers) { for(const auto& na: peers) { if (!is_addr_connected(na)) { @@ -1243,13 +1251,13 @@ namespace CryptoNote return true; } - bool node_server::parse_peers_and_add_to_container(const boost::program_options::variables_map& vm, - const command_line::arg_descriptor > & arg, std::vector& container) + bool NodeServer::parse_peers_and_add_to_container(const boost::program_options::variables_map& vm, + const command_line::arg_descriptor > & arg, std::vector& container) { std::vector perrs = command_line::get_arg(vm, arg); for(const std::string& pr_str: perrs) { - net_address na; + NetworkAddress na; if (!parse_peer_from_string(na, pr_str)) { logger(ERROR, BRIGHT_RED) << "Failed to parse address from string: " << pr_str; return false; @@ -1260,10 +1268,10 @@ namespace CryptoNote return true; } - void node_server::acceptLoop() { + void NodeServer::acceptLoop() { for (;;) { try { - p2p_connection_context ctx(m_dispatcher, m_listener.accept()); + P2pConnectionContext ctx(m_dispatcher, logger.getLogger(), m_listener.accept()); ctx.m_connection_id = boost::uuids::random_generator()(); ctx.m_is_income = true; ctx.m_started = time(nullptr); @@ -1274,11 +1282,11 @@ namespace CryptoNote auto iter = m_connections.emplace(ctx.m_connection_id, std::move(ctx)).first; const boost::uuids::uuid& connectionId = iter->first; - p2p_connection_context& connection = iter->second; + P2pConnectionContext& connection = iter->second; - ++m_spawnCount; - m_dispatcher.spawn(std::bind(&node_server::connectionHandler, this, std::cref(connectionId), std::ref(connection))); + m_workingContextGroup.spawn(std::bind(&NodeServer::connectionHandler, this, std::cref(connectionId), std::ref(connection))); } catch (System::InterruptedException&) { + logger(DEBUGGING) << "acceptLoop() is interrupted"; break; } catch (const std::exception& e) { logger(WARNING) << "Exception in acceptLoop: " << e.what(); @@ -1286,13 +1294,9 @@ namespace CryptoNote } logger(DEBUGGING) << "acceptLoop finished"; - - if (--m_spawnCount == 0) { - m_shutdownCompleteEvent.set(); - } } - void node_server::onIdle() { + void NodeServer::onIdle() { logger(DEBUGGING) << "onIdle started"; try { @@ -1302,92 +1306,152 @@ namespace CryptoNote m_idleTimer.sleep(std::chrono::seconds(1)); } } catch (System::InterruptedException&) { + logger(DEBUGGING) << "onIdle() is interrupted"; } catch (std::exception& e) { logger(WARNING) << "Exception in onIdle: " << e.what(); } logger(DEBUGGING) << "onIdle finished"; + } - if (--m_spawnCount == 0) { - m_shutdownCompleteEvent.set(); + void NodeServer::timeoutLoop() { + try { + while (!m_stop) { + m_timeoutTimer.sleep(std::chrono::seconds(10)); + auto now = P2pConnectionContext::Clock::now(); + + for (auto& kv : m_connections) { + auto& ctx = kv.second; + if (ctx.writeDuration(now) > P2P_DEFAULT_INVOKE_TIMEOUT) { + logger(WARNING) << ctx << "write operation timed out, stopping connection"; + ctx.interrupt(); + } + } + } + } catch (System::InterruptedException&) { + logger(DEBUGGING) << "timeoutLoop() is interrupted"; + } catch (std::exception& e) { + logger(WARNING) << "Exception in timeoutLoop: " << e.what(); } } - void node_server::timedSyncLoop() { + void NodeServer::timedSyncLoop() { try { for (;;) { m_timedSyncTimer.sleep(std::chrono::seconds(P2P_DEFAULT_HANDSHAKE_INTERVAL)); timedSync(); } } catch (System::InterruptedException&) { + logger(DEBUGGING) << "timedSyncLoop() is interrupted"; } catch (std::exception& e) { logger(WARNING) << "Exception in timedSyncLoop: " << e.what(); } logger(DEBUGGING) << "timedSyncLoop finished"; - - if (--m_spawnCount == 0) { - m_shutdownCompleteEvent.set(); - } } - void node_server::connectionHandler(const boost::uuids::uuid& connectionId, p2p_connection_context& ctx) { + void NodeServer::connectionHandler(const boost::uuids::uuid& connectionId, P2pConnectionContext& ctx) { + // This inner context is necessary in order to stop connection handler at any moment + System::Context<> context(m_dispatcher, [this, &connectionId, &ctx] { + System::Context<> writeContext(m_dispatcher, std::bind(&NodeServer::writeHandler, this, std::ref(ctx))); - try { - on_connection_new(ctx); + try { + on_connection_new(ctx); - LevinProtocol proto(ctx.connection); - LevinProtocol::Command cmd; + LevinProtocol proto(ctx.connection); + LevinProtocol::Command cmd; - for (;;) { - if (ctx.m_state == cryptonote_connection_context::state_sync_required) { - ctx.m_state = cryptonote_connection_context::state_synchronizing; - m_payload_handler.start_sync(ctx); - } else if (ctx.m_state == cryptonote_connection_context::state_pool_sync_required) { - ctx.m_state = cryptonote_connection_context::state_normal; - m_payload_handler.requestMissingPoolTransactions(ctx); - } + for (;;) { + if (ctx.m_state == CryptoNoteConnectionContext::state_sync_required) { + ctx.m_state = CryptoNoteConnectionContext::state_synchronizing; + m_payload_handler.start_sync(ctx); + } else if (ctx.m_state == CryptoNoteConnectionContext::state_pool_sync_required) { + ctx.m_state = CryptoNoteConnectionContext::state_normal; + m_payload_handler.requestMissingPoolTransactions(ctx); + } - if (!proto.readCommand(cmd)) { - break; - } + if (!proto.readCommand(cmd)) { + break; + } - std::string response; - bool handled = false; - auto retcode = handleCommand(cmd, response, ctx, handled); + BinaryArray response; + bool handled = false; + auto retcode = handleCommand(cmd, response, ctx, handled); - // send response - if (cmd.needReply()) { - System::LatchGuard latch(ctx.writeLatch); - System::EventLock lock(ctx.connectionEvent); - if (handled) { - proto.sendReply(cmd.command, response, retcode); - } else { - proto.sendReply(cmd.command, std::string(), static_cast(LevinError::ERROR_CONNECTION_HANDLER_NOT_DEFINED)); + // send response + if (cmd.needReply()) { + if (!handled) { + retcode = static_cast(LevinError::ERROR_CONNECTION_HANDLER_NOT_DEFINED); + response.clear(); + } + + ctx.pushMessage(P2pMessage(P2pMessage::REPLY, cmd.command, std::move(response), retcode)); + } + + if (ctx.m_state == CryptoNoteConnectionContext::state_shutdown) { + break; } } + } catch (System::InterruptedException&) { + logger(DEBUGGING) << ctx << "connectionHandler() inner context is interrupted"; + } catch (std::exception& e) { + logger(WARNING) << ctx << "Exception in connectionHandler: " << e.what(); + } - if (ctx.m_state == cryptonote_connection_context::state_shutdown) { + ctx.interrupt(); + writeContext.interrupt(); + writeContext.get(); + + on_connection_close(ctx); + m_connections.erase(connectionId); + }); + + ctx.context = &context; + + try { + context.get(); + } catch (System::InterruptedException&) { + logger(DEBUGGING) << "connectionHandler() is interrupted"; + } + } + + void NodeServer::writeHandler(P2pConnectionContext& ctx) { + logger(DEBUGGING) << ctx << "writeHandler started"; + + try { + LevinProtocol proto(ctx.connection); + + for (;;) { + auto msgs = ctx.popBuffer(); + if (msgs.empty()) { break; } + + for (const auto& msg : msgs) { + logger(DEBUGGING) << ctx << "msg " << msg.type << ':' << msg.command; + switch (msg.type) { + case P2pMessage::COMMAND: + proto.sendMessage(msg.command, msg.buffer, true); + break; + case P2pMessage::NOTIFY: + proto.sendMessage(msg.command, msg.buffer, false); + break; + case P2pMessage::REPLY: + proto.sendReply(msg.command, msg.buffer, msg.returnCode); + break; + default: + assert(false); + } + } } - } catch (System::InterruptedException&) { - logger(TRACE) << "Closing connection..."; + // connection stopped + logger(DEBUGGING) << ctx << "writeHandler() is interrupted"; } catch (std::exception& e) { - logger(WARNING) << "Exception in connectionHandler: " << e.what(); - } - - ctx.writeLatch.wait(); - - on_connection_close(ctx); - m_connections.erase(connectionId); - - if (--m_spawnCount == 0) { - m_shutdownCompleteEvent.set(); + logger(WARNING) << ctx << "error during write: " << e.what(); + ctx.interrupt(); // stop connection on write error } + logger(DEBUGGING) << ctx << "writeHandler finished"; } - - } diff --git a/src/P2p/NetNode.h b/src/P2p/NetNode.h new file mode 100644 index 00000000..cadfd240 --- /dev/null +++ b/src/P2p/NetNode.h @@ -0,0 +1,273 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "CryptoNoteCore/OnceInInterval.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolHandler.h" +#include "Common/CommandLine.h" +#include "Logging/LoggerRef.h" + +#include "ConnectionContext.h" +#include "LevinProtocol.h" +#include "NetNodeCommon.h" +#include "NetNodeConfig.h" +#include "P2pProtocolDefinitions.h" +#include "P2pNetworks.h" +#include "PeerListManager.h" + +namespace System { +class TcpConnection; +} + +namespace CryptoNote +{ + class LevinProtocol; + class ISerializer; + + struct P2pMessage { + enum Type { + COMMAND, + REPLY, + NOTIFY + }; + + P2pMessage(Type type, uint32_t command, const BinaryArray& buffer, int32_t returnCode = 0) : + type(type), command(command), buffer(buffer), returnCode(returnCode) { + } + + P2pMessage(P2pMessage&& msg) : + type(msg.type), command(msg.command), buffer(std::move(msg.buffer)), returnCode(msg.returnCode) { + } + + size_t size() { + return buffer.size(); + } + + Type type; + uint32_t command; + const BinaryArray buffer; + int32_t returnCode; + }; + + struct P2pConnectionContext : public CryptoNoteConnectionContext { + public: + using Clock = std::chrono::steady_clock; + using TimePoint = Clock::time_point; + + System::Context* context; + PeerIdType peerId; + System::TcpConnection connection; + + P2pConnectionContext(System::Dispatcher& dispatcher, Logging::ILogger& log, System::TcpConnection&& conn) : + context(nullptr), + peerId(0), + connection(std::move(conn)), + logger(log, "node_server"), + queueEvent(dispatcher), + stopped(false) { + } + + P2pConnectionContext(P2pConnectionContext&& ctx) : + CryptoNoteConnectionContext(std::move(ctx)), + context(ctx.context), + peerId(ctx.peerId), + connection(std::move(ctx.connection)), + logger(ctx.logger.getLogger(), "node_server"), + queueEvent(std::move(ctx.queueEvent)), + stopped(std::move(ctx.stopped)) { + } + + bool pushMessage(P2pMessage&& msg); + std::vector popBuffer(); + void interrupt(); + + uint64_t writeDuration(TimePoint now) const; + + private: + Logging::LoggerRef logger; + TimePoint writeOperationStartTime; + System::Event queueEvent; + std::vector writeQueue; + size_t writeQueueSize = 0; + bool stopped; + }; + + class NodeServer : public IP2pEndpoint + { + public: + + static void init_options(boost::program_options::options_description& desc); + + NodeServer(System::Dispatcher& dispatcher, CryptoNote::CryptoNoteProtocolHandler& payload_handler, Logging::ILogger& log); + + bool run(); + bool init(const NetNodeConfig& config); + bool deinit(); + bool sendStopSignal(); + uint32_t get_this_peer_port(){return m_listeningPort;} + CryptoNote::CryptoNoteProtocolHandler& get_payload_object(); + + void serialize(ISerializer& s); + + // debug functions + bool log_peerlist(); + bool log_connections(); + virtual uint64_t get_connections_count() override; + size_t get_outgoing_connections_count(); + + CryptoNote::PeerlistManager& getPeerlistManager() { return m_peerlist; } + + private: + + int handleCommand(const LevinProtocol::Command& cmd, BinaryArray& buff_out, P2pConnectionContext& context, bool& handled); + + //----------------- commands handlers ---------------------------------------------- + int handle_handshake(int command, COMMAND_HANDSHAKE::request& arg, COMMAND_HANDSHAKE::response& rsp, P2pConnectionContext& context); + int handle_timed_sync(int command, COMMAND_TIMED_SYNC::request& arg, COMMAND_TIMED_SYNC::response& rsp, P2pConnectionContext& context); + int handle_ping(int command, COMMAND_PING::request& arg, COMMAND_PING::response& rsp, P2pConnectionContext& context); +#ifdef ALLOW_DEBUG_COMMANDS + int handle_get_stat_info(int command, COMMAND_REQUEST_STAT_INFO::request& arg, COMMAND_REQUEST_STAT_INFO::response& rsp, P2pConnectionContext& context); + int handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, P2pConnectionContext& context); + int handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, P2pConnectionContext& context); +#endif + + bool init_config(); + bool make_default_config(); + bool store_config(); + bool check_trust(const proof_of_trust& tr); + void initUpnp(); + + bool handshake(CryptoNote::LevinProtocol& proto, P2pConnectionContext& context, bool just_take_peerlist = false); + bool timedSync(); + bool handleTimedSyncResponse(const BinaryArray& in, P2pConnectionContext& context); + void forEachConnection(std::function action); + + void on_connection_new(P2pConnectionContext& context); + void on_connection_close(P2pConnectionContext& context); + + //----------------- i_p2p_endpoint ------------------------------------------------------------- + virtual void relay_notify_to_all(int command, const BinaryArray& data_buff, const net_connection_id* excludeConnection) override; + virtual bool invoke_notify_to_peer(int command, const BinaryArray& req_buff, const CryptoNoteConnectionContext& context) override; + virtual void for_each_connection(std::function f) override; + virtual void externalRelayNotifyToAll(int command, const BinaryArray& data_buff) override; + + //----------------------------------------------------------------------------------------------- + bool handle_command_line(const boost::program_options::variables_map& vm); + bool handleConfig(const NetNodeConfig& config); + bool append_net_address(std::vector& nodes, const std::string& addr); + bool idle_worker(); + bool handle_remote_peerlist(const std::list& peerlist, time_t local_time, const CryptoNoteConnectionContext& context); + bool get_local_node_data(basic_node_data& node_data); + + bool merge_peerlist_with_local(const std::list& bs); + bool fix_time_delta(std::list& local_peerlist, time_t local_time, int64_t& delta); + + bool connections_maker(); + bool make_new_connection_from_peerlist(bool use_white_list); + bool try_to_connect_and_handshake_with_new_peer(const NetworkAddress& na, bool just_take_peerlist = false, uint64_t last_seen_stamp = 0, bool white = true); + bool is_peer_used(const PeerlistEntry& peer); + bool is_addr_connected(const NetworkAddress& peer); + bool try_ping(basic_node_data& node_data, P2pConnectionContext& context); + bool make_expected_connections_count(bool white_list, size_t expected_connections); + bool is_priority_node(const NetworkAddress& na); + + bool connect_to_peerlist(const std::vector& peers); + + bool parse_peers_and_add_to_container(const boost::program_options::variables_map& vm, + const command_line::arg_descriptor > & arg, std::vector& container); + + //debug functions + std::string print_connections_container(); + + typedef std::unordered_map> ConnectionContainer; + typedef ConnectionContainer::iterator ConnectionIterator; + ConnectionContainer m_connections; + + void acceptLoop(); + void connectionHandler(const boost::uuids::uuid& connectionId, P2pConnectionContext& connection); + void writeHandler(P2pConnectionContext& ctx); + void onIdle(); + void timedSyncLoop(); + void timeoutLoop(); + + struct config + { + network_config m_net_config; + uint64_t m_peer_id; + + void serialize(ISerializer& s) { + KV_MEMBER(m_net_config) + KV_MEMBER(m_peer_id) + } + }; + + config m_config; + std::string m_config_folder; + + bool m_have_address; + bool m_first_connection_maker_call; + uint32_t m_listeningPort; + uint32_t m_external_port; + uint32_t m_ip_address; + bool m_allow_local_ip; + bool m_hide_my_port; + std::string m_p2p_state_filename; + + System::Dispatcher& m_dispatcher; + System::ContextGroup m_workingContextGroup; + System::Event m_stopEvent; + System::Timer m_idleTimer; + System::Timer m_timeoutTimer; + System::TcpListener m_listener; + Logging::LoggerRef logger; + std::atomic m_stop; + + CryptoNoteProtocolHandler& m_payload_handler; + PeerlistManager m_peerlist; + + // OnceInInterval m_peer_handshake_idle_maker_interval; + OnceInInterval m_connections_maker_interval; + OnceInInterval m_peerlist_store_interval; + System::Timer m_timedSyncTimer; + + std::string m_bind_ip; + std::string m_port; +#ifdef ALLOW_DEBUG_COMMANDS + uint64_t m_last_stat_request_time; +#endif + std::vector m_priority_peers; + std::vector m_exclusive_peers; + std::vector m_seed_nodes; + std::list m_command_line_peers; + uint64_t m_peer_livetime; + boost::uuids::uuid m_network_id; + }; +} diff --git a/src/p2p/net_node_common.h b/src/P2p/NetNodeCommon.h old mode 100644 new mode 100755 similarity index 52% rename from src/p2p/net_node_common.h rename to src/P2p/NetNodeCommon.h index 054caa03..1b36b198 --- a/src/p2p/net_node_common.h +++ b/src/P2p/NetNodeCommon.h @@ -17,26 +17,27 @@ #pragma once -#include "p2p_protocol_types.h" +#include "CryptoNote.h" +#include "P2pProtocolTypes.h" namespace CryptoNote { - struct cryptonote_connection_context; + struct CryptoNoteConnectionContext; - struct i_p2p_endpoint { - virtual void relay_notify_to_all(int command, const std::string& data_buff, const net_connection_id* excludeConnection) = 0; - virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const CryptoNote::cryptonote_connection_context& context) = 0; + struct IP2pEndpoint { + virtual void relay_notify_to_all(int command, const BinaryArray& data_buff, const net_connection_id* excludeConnection) = 0; + virtual bool invoke_notify_to_peer(int command, const BinaryArray& req_buff, const CryptoNote::CryptoNoteConnectionContext& context) = 0; virtual uint64_t get_connections_count()=0; - virtual void for_each_connection(std::function f) = 0; + virtual void for_each_connection(std::function f) = 0; // can be called from external threads - virtual void externalRelayNotifyToAll(int command, const std::string& data_buff) = 0; + virtual void externalRelayNotifyToAll(int command, const BinaryArray& data_buff) = 0; }; - struct p2p_endpoint_stub: public i_p2p_endpoint { - virtual void relay_notify_to_all(int command, const std::string& data_buff, const net_connection_id* excludeConnection) {} - virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const CryptoNote::cryptonote_connection_context& context) { return true; } - virtual void for_each_connection(std::function f) {} + struct p2p_endpoint_stub: public IP2pEndpoint { + virtual void relay_notify_to_all(int command, const BinaryArray& data_buff, const net_connection_id* excludeConnection) {} + virtual bool invoke_notify_to_peer(int command, const BinaryArray& req_buff, const CryptoNote::CryptoNoteConnectionContext& context) { return true; } + virtual void for_each_connection(std::function f) {} virtual uint64_t get_connections_count() { return 0; } - virtual void externalRelayNotifyToAll(int command, const std::string& data_buff) {} + virtual void externalRelayNotifyToAll(int command, const BinaryArray& data_buff) {} }; } diff --git a/src/p2p/NetNodeConfig.cpp b/src/P2p/NetNodeConfig.cpp old mode 100644 new mode 100755 similarity index 60% rename from src/p2p/NetNodeConfig.cpp rename to src/P2p/NetNodeConfig.cpp index 2ab6d8d3..643b39a9 --- a/src/p2p/NetNodeConfig.cpp +++ b/src/P2p/NetNodeConfig.cpp @@ -19,18 +19,18 @@ #include -#include -#include "Common/command_line.h" +#include +#include "Common/CommandLine.h" #include "Common/StringTools.h" #include "crypto/crypto.h" -#include "cryptonote_config.h" +#include "CryptoNoteConfig.h" namespace CryptoNote { namespace { const command_line::arg_descriptor arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol", "0.0.0.0"}; -const command_line::arg_descriptor arg_p2p_bind_port = {"p2p-bind-port", "Port for p2p network protocol", std::to_string(P2P_DEFAULT_PORT)}; -const command_line::arg_descriptor arg_p2p_external_port = {"p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0}; +const command_line::arg_descriptor arg_p2p_bind_port = {"p2p-bind-port", "Port for p2p network protocol", P2P_DEFAULT_PORT}; +const command_line::arg_descriptor arg_p2p_external_port = { "p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0 }; const command_line::arg_descriptor arg_p2p_allow_local_ip = {"allow-local-ip", "Allow local ip add to peer list, mostly in debug purposes"}; const command_line::arg_descriptor > arg_p2p_add_peer = {"add-peer", "Manually add peer to local peerlist"}; const command_line::arg_descriptor > arg_p2p_add_priority_node = {"add-priority-node", "Specify list of peers to connect to and attempt to keep the connection open"}; @@ -39,17 +39,17 @@ const command_line::arg_descriptor > arg_p2p_add_exclus const command_line::arg_descriptor > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect"}; const command_line::arg_descriptor arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; -bool parsePeerFromString(net_address& pe, const std::string& node_addr) { +bool parsePeerFromString(NetworkAddress& pe, const std::string& node_addr) { return Common::parseIpAddressAndPort(pe.ip, pe.port, node_addr); } bool parsePeersAndAddToContainer(const boost::program_options::variables_map& vm, - const command_line::arg_descriptor>& arg, std::vector& container) + const command_line::arg_descriptor>& arg, std::vector& container) { std::vector peers = command_line::get_arg(vm, arg); for(const std::string& str: peers) { - net_address na = boost::value_initialized(); + NetworkAddress na = boost::value_initialized(); if (!parsePeerFromString(na, str)) { return false; } @@ -75,11 +75,11 @@ void NetNodeConfig::initOptions(boost::program_options::options_description& des NetNodeConfig::NetNodeConfig() { bindIp = "0.0.0.0"; - bindPort = std::to_string(P2P_DEFAULT_PORT); + bindPort = P2P_DEFAULT_PORT; externalPort = 0; allowLocalIp = false; hideMyPort = false; - configFolder = tools::get_default_data_dir(); + configFolder = Tools::getDefaultDataDirectory(); } bool NetNodeConfig::init(const boost::program_options::variables_map& vm) @@ -89,14 +89,15 @@ bool NetNodeConfig::init(const boost::program_options::variables_map& vm) externalPort = command_line::get_arg(vm, arg_p2p_external_port); allowLocalIp = command_line::get_arg(vm, arg_p2p_allow_local_ip); configFolder = command_line::get_arg(vm, command_line::arg_data_dir); + p2pStateFilename = CryptoNote::parameters::P2P_NET_DATA_FILENAME; if (command_line::has_arg(vm, arg_p2p_add_peer)) { std::vector perrs = command_line::get_arg(vm, arg_p2p_add_peer); for(const std::string& pr_str: perrs) { - peerlist_entry pe = boost::value_initialized(); - pe.id = crypto::rand(); + PeerlistEntry pe = boost::value_initialized(); + pe.id = Crypto::rand(); if (!parsePeerFromString(pe.adr, pr_str)) { return false; } @@ -125,4 +126,105 @@ bool NetNodeConfig::init(const boost::program_options::variables_map& vm) return true; } +void NetNodeConfig::setTestnet(bool isTestnet) { + testnet = isTestnet; +} + +std::string NetNodeConfig::getP2pStateFilename() const { + if (testnet) { + return "testnet_" + p2pStateFilename; + } + + return p2pStateFilename; +} + +bool NetNodeConfig::getTestnet() const { + return testnet; +} + +std::string NetNodeConfig::getBindIp() const { + return bindIp; +} + +uint16_t NetNodeConfig::getBindPort() const { + return bindPort; +} + +uint16_t NetNodeConfig::getExternalPort() const { + return externalPort; +} + +bool NetNodeConfig::getAllowLocalIp() const { + return allowLocalIp; +} + +std::vector NetNodeConfig::getPeers() const { + return peers; +} + +std::vector NetNodeConfig::getPriorityNodes() const { + return priorityNodes; +} + +std::vector NetNodeConfig::getExclusiveNodes() const { + return exclusiveNodes; +} + +std::vector NetNodeConfig::getSeedNodes() const { + return seedNodes; +} + +bool NetNodeConfig::getHideMyPort() const { + return hideMyPort; +} + +std::string NetNodeConfig::getConfigFolder() const { + return configFolder; +} + +void NetNodeConfig::setP2pStateFilename(const std::string& filename) { + p2pStateFilename = filename; +} + +void NetNodeConfig::setBindIp(const std::string& ip) { + bindIp = ip; +} + +void NetNodeConfig::setBindPort(uint16_t port) { + bindPort = port; +} + +void NetNodeConfig::setExternalPort(uint16_t port) { + externalPort = port; +} + +void NetNodeConfig::setAllowLocalIp(bool allow) { + allowLocalIp = allow; +} + +void NetNodeConfig::setPeers(const std::vector& peerList) { + peers = peerList; +} + +void NetNodeConfig::setPriorityNodes(const std::vector& addresses) { + priorityNodes = addresses; +} + +void NetNodeConfig::setExclusiveNodes(const std::vector& addresses) { + exclusiveNodes = addresses; +} + +void NetNodeConfig::setSeedNodes(const std::vector& addresses) { + seedNodes = addresses; +} + +void NetNodeConfig::setHideMyPort(bool hide) { + hideMyPort = hide; +} + +void NetNodeConfig::setConfigFolder(const std::string& folder) { + configFolder = folder; +} + + } //namespace nodetool diff --git a/src/P2p/NetNodeConfig.h b/src/P2p/NetNodeConfig.h new file mode 100755 index 00000000..4590ef8f --- /dev/null +++ b/src/P2p/NetNodeConfig.h @@ -0,0 +1,76 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +#include +#include "P2pProtocolTypes.h" + +namespace CryptoNote { + +class NetNodeConfig { +public: + NetNodeConfig(); + static void initOptions(boost::program_options::options_description& desc); + bool init(const boost::program_options::variables_map& vm); + + std::string getP2pStateFilename() const; + bool getTestnet() const; + std::string getBindIp() const; + uint16_t getBindPort() const; + uint16_t getExternalPort() const; + bool getAllowLocalIp() const; + std::vector getPeers() const; + std::vector getPriorityNodes() const; + std::vector getExclusiveNodes() const; + std::vector getSeedNodes() const; + bool getHideMyPort() const; + std::string getConfigFolder() const; + + void setP2pStateFilename(const std::string& filename); + void setTestnet(bool isTestnet); + void setBindIp(const std::string& ip); + void setBindPort(uint16_t port); + void setExternalPort(uint16_t port); + void setAllowLocalIp(bool allow); + void setPeers(const std::vector& peerList); + void setPriorityNodes(const std::vector& addresses); + void setExclusiveNodes(const std::vector& addresses); + void setSeedNodes(const std::vector& addresses); + void setHideMyPort(bool hide); + void setConfigFolder(const std::string& folder); + +private: + std::string bindIp; + uint16_t bindPort; + uint16_t externalPort; + bool allowLocalIp; + std::vector peers; + std::vector priorityNodes; + std::vector exclusiveNodes; + std::vector seedNodes; + bool hideMyPort; + std::string configFolder; + std::string p2pStateFilename; + bool testnet = false; +}; + +} //namespace nodetool diff --git a/src/P2p/P2pConnectionProxy.cpp b/src/P2p/P2pConnectionProxy.cpp new file mode 100644 index 00000000..9484d5a3 --- /dev/null +++ b/src/P2p/P2pConnectionProxy.cpp @@ -0,0 +1,168 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "P2pConnectionProxy.h" + +#include "LevinProtocol.h" +#include "P2pContext.h" +#include "P2pNode.h" + +#include + +using namespace System; + +namespace CryptoNote { + +P2pConnectionProxy::P2pConnectionProxy(P2pContextOwner&& ctx, IP2pNodeInternal& node) + : m_contextOwner(std::move(ctx)), m_context(m_contextOwner.get()), m_node(node) {} + +P2pConnectionProxy::~P2pConnectionProxy() { + m_context.stop(); +} + +bool P2pConnectionProxy::processIncomingHandshake() { + LevinProtocol::Command cmd; + if (!m_context.readCommand(cmd)) { + throw std::runtime_error("Connection unexpectedly closed"); + } + + if (cmd.command == COMMAND_HANDSHAKE::ID) { + handleHandshakeRequest(cmd); + return true; + } + + if (cmd.command == COMMAND_PING::ID) { + COMMAND_PING::response resp{ PING_OK_RESPONSE_STATUS_TEXT, m_node.getPeerId() }; + m_context.writeMessage(makeReply(COMMAND_PING::ID, LevinProtocol::encode(resp), LEVIN_PROTOCOL_RETCODE_SUCCESS)); + return false; + } + + throw std::runtime_error("Unexpected command: " + std::to_string(cmd.command)); +} + +void P2pConnectionProxy::read(P2pMessage& message) { + if (!m_readQueue.empty()) { + message = std::move(m_readQueue.front()); + m_readQueue.pop(); + return; + } + + for (;;) { + LevinProtocol::Command cmd; + if (!m_context.readCommand(cmd)) { + throw InterruptedException(); + } + + message.type = cmd.command; + + if (cmd.command == COMMAND_HANDSHAKE::ID) { + handleHandshakeResponse(cmd, message); + break; + } else if (cmd.command == COMMAND_TIMED_SYNC::ID) { + handleTimedSync(cmd); + } else { + message.data = std::move(cmd.buf); + break; + } + } +} + +void P2pConnectionProxy::write(const P2pMessage &message) { + if (message.type == COMMAND_HANDSHAKE::ID) { + writeHandshake(message); + } else { + m_context.writeMessage(P2pContext::Message(P2pMessage(message), P2pContext::Message::NOTIFY)); + } +} + +void P2pConnectionProxy::ban() { + // not implemented +} + +void P2pConnectionProxy::stop() { + m_context.stop(); +} + +void P2pConnectionProxy::writeHandshake(const P2pMessage &message) { + CORE_SYNC_DATA coreSync; + LevinProtocol::decode(message.data, coreSync); + + if (m_context.isIncoming()) { + // response + COMMAND_HANDSHAKE::response res; + res.node_data = m_node.getNodeData(); + res.payload_data = coreSync; + res.local_peerlist = m_node.getLocalPeerList(); + m_context.writeMessage(makeReply(COMMAND_HANDSHAKE::ID, LevinProtocol::encode(res), LEVIN_PROTOCOL_RETCODE_SUCCESS)); + m_node.tryPing(m_context); + } else { + // request + COMMAND_HANDSHAKE::request req; + req.node_data = m_node.getNodeData(); + req.payload_data = coreSync; + m_context.writeMessage(makeRequest(COMMAND_HANDSHAKE::ID, LevinProtocol::encode(req))); + } +} + +void P2pConnectionProxy::handleHandshakeRequest(const LevinProtocol::Command& cmd) { + COMMAND_HANDSHAKE::request req; + if (!LevinProtocol::decode(cmd.buf, req)) { + throw std::runtime_error("Failed to decode COMMAND_HANDSHAKE request"); + } + + m_node.handleNodeData(req.node_data, m_context); + m_readQueue.push(P2pMessage{ + cmd.command, LevinProtocol::encode(req.payload_data) }); // enqueue payload info +} + +void P2pConnectionProxy::handleHandshakeResponse(const LevinProtocol::Command& cmd, P2pMessage& message) { + if (m_context.isIncoming()) { + // handshake should be already received by P2pNode + throw std::runtime_error("Unexpected COMMAND_HANDSHAKE from incoming connection"); + } + + COMMAND_HANDSHAKE::response res; + if (!LevinProtocol::decode(cmd.buf, res)) { + throw std::runtime_error("Invalid handshake message format"); + } + + m_node.handleNodeData(res.node_data, m_context); + m_node.handleRemotePeerList(res.local_peerlist, res.node_data.local_time); + + message.data = LevinProtocol::encode(res.payload_data); +} + +void P2pConnectionProxy::handleTimedSync(const LevinProtocol::Command& cmd) { + if (cmd.isResponse) { + COMMAND_TIMED_SYNC::response res; + LevinProtocol::decode(cmd.buf, res); + m_node.handleRemotePeerList(res.local_peerlist, res.local_time); + } else { + // we ignore information from the request + // COMMAND_TIMED_SYNC::request req; + // LevinProtocol::decode(cmd.buf, req); + COMMAND_TIMED_SYNC::response res; + + res.local_time = time(nullptr); + res.local_peerlist = m_node.getLocalPeerList(); + res.payload_data = m_node.getGenesisPayload(); + + m_context.writeMessage(makeReply(COMMAND_TIMED_SYNC::ID, LevinProtocol::encode(res), LEVIN_PROTOCOL_RETCODE_SUCCESS)); + } +} + +} diff --git a/src/P2p/P2pConnectionProxy.h b/src/P2p/P2pConnectionProxy.h new file mode 100644 index 00000000..b91f9fa4 --- /dev/null +++ b/src/P2p/P2pConnectionProxy.h @@ -0,0 +1,59 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +#include "IP2pNodeInternal.h" +#include "LevinProtocol.h" +#include "P2pContextOwner.h" +#include "P2pInterfaces.h" + +namespace CryptoNote { + +class P2pContext; +class P2pNode; + +class P2pConnectionProxy : public IP2pConnection { +public: + + P2pConnectionProxy(P2pContextOwner&& ctx, IP2pNodeInternal& node); + ~P2pConnectionProxy(); + + bool processIncomingHandshake(); + + // IP2pConnection + virtual void read(P2pMessage& message) override; + virtual void write(const P2pMessage &message) override; + virtual void ban() override; + virtual void stop() override; + +private: + + void writeHandshake(const P2pMessage &message); + void handleHandshakeRequest(const LevinProtocol::Command& cmd); + void handleHandshakeResponse(const LevinProtocol::Command& cmd, P2pMessage& message); + void handleTimedSync(const LevinProtocol::Command& cmd); + + std::queue m_readQueue; + P2pContextOwner m_contextOwner; + P2pContext& m_context; + IP2pNodeInternal& m_node; +}; + +} diff --git a/src/P2p/P2pContext.cpp b/src/P2p/P2pContext.cpp new file mode 100755 index 00000000..d7d36b61 --- /dev/null +++ b/src/P2p/P2pContext.cpp @@ -0,0 +1,193 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "P2pContext.h" + +#include +#include +#include +#include + +#include "LevinProtocol.h" + +using namespace System; + +namespace CryptoNote { + +P2pContext::Message::Message(P2pMessage&& msg, Type messageType, uint32_t returnCode) : + messageType(messageType), returnCode(returnCode) { + type = msg.type; + data = std::move(msg.data); +} + +size_t P2pContext::Message::size() const { + return data.size(); +} + +P2pContext::P2pContext( + Dispatcher& dispatcher, + TcpConnection&& conn, + bool isIncoming, + const NetworkAddress& remoteAddress, + std::chrono::nanoseconds timedSyncInterval, + const CORE_SYNC_DATA& timedSyncData) + : + incoming(isIncoming), + remoteAddress(remoteAddress), + dispatcher(dispatcher), + contextGroup(dispatcher), + timeStarted(Clock::now()), + timedSyncInterval(timedSyncInterval), + timedSyncData(timedSyncData), + timedSyncTimer(dispatcher), + timedSyncFinished(dispatcher), + connection(std::move(conn)), + writeEvent(dispatcher), + readEvent(dispatcher) { + writeEvent.set(); + readEvent.set(); + lastReadTime = timeStarted; // use current time + contextGroup.spawn(std::bind(&P2pContext::timedSyncLoop, this)); +} + +P2pContext::~P2pContext() { + stop(); + // wait for timedSyncLoop finish + timedSyncFinished.wait(); + // ensure that all read/write operations completed + readEvent.wait(); + writeEvent.wait(); +} + +PeerIdType P2pContext::getPeerId() const { + return peerId; +} + +uint16_t P2pContext::getPeerPort() const { + return peerPort; +} + +const NetworkAddress& P2pContext::getRemoteAddress() const { + return remoteAddress; +} + +bool P2pContext::isIncoming() const { + return incoming; +} + +void P2pContext::setPeerInfo(uint8_t protocolVersion, PeerIdType id, uint16_t port) { + version = protocolVersion; + peerId = id; + if (isIncoming()) { + peerPort = port; + } +} + +bool P2pContext::readCommand(LevinProtocol::Command& cmd) { + if (stopped) { + throw InterruptedException(); + } + + EventLock lk(readEvent); + bool result = LevinProtocol(connection).readCommand(cmd); + lastReadTime = Clock::now(); + return result; +} + +void P2pContext::writeMessage(const Message& msg) { + if (stopped) { + throw InterruptedException(); + } + + EventLock lk(writeEvent); + LevinProtocol proto(connection); + + switch (msg.messageType) { + case P2pContext::Message::NOTIFY: + proto.sendMessage(msg.type, msg.data, false); + break; + case P2pContext::Message::REQUEST: + proto.sendMessage(msg.type, msg.data, true); + break; + case P2pContext::Message::REPLY: + proto.sendReply(msg.type, msg.data, msg.returnCode); + break; + } +} + +void P2pContext::start() { + // stub for OperationTimeout class +} + +void P2pContext::stop() { + if (!stopped) { + stopped = true; + contextGroup.interrupt(); + } +} + +void P2pContext::timedSyncLoop() { + // construct message + P2pContext::Message timedSyncMessage{ + P2pMessage{ + COMMAND_TIMED_SYNC::ID, + LevinProtocol::encode(COMMAND_TIMED_SYNC::request{ timedSyncData }) + }, + P2pContext::Message::REQUEST + }; + + while (!stopped) { + try { + timedSyncTimer.sleep(timedSyncInterval); + + OperationTimeout timeout(dispatcher, *this, timedSyncInterval); + writeMessage(timedSyncMessage); + + // check if we had read operation in given time interval + if ((lastReadTime + timedSyncInterval * 2) < Clock::now()) { + stop(); + break; + } + } catch (InterruptedException&) { + // someone stopped us + } catch (std::exception&) { + stop(); // stop connection on write error + break; + } + } + + timedSyncFinished.set(); +} + +P2pContext::Message makeReply(uint32_t command, const BinaryArray& data, uint32_t returnCode) { + return P2pContext::Message( + P2pMessage{ command, data }, + P2pContext::Message::REPLY, + returnCode); +} + +P2pContext::Message makeRequest(uint32_t command, const BinaryArray& data) { + return P2pContext::Message( + P2pMessage{ command, data }, + P2pContext::Message::REQUEST); +} + +std::ostream& operator <<(std::ostream& s, const P2pContext& conn) { + return s << "[" << conn.getRemoteAddress() << "]"; +} + +} diff --git a/src/P2p/P2pContext.h b/src/P2p/P2pContext.h new file mode 100755 index 00000000..b74c1a0a --- /dev/null +++ b/src/P2p/P2pContext.h @@ -0,0 +1,104 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +#include "CryptoNoteConfig.h" +#include "LevinProtocol.h" +#include "P2pInterfaces.h" +#include "P2pProtocolDefinitions.h" +#include "P2pProtocolTypes.h" + +namespace CryptoNote { + +class P2pContext { +public: + using Clock = std::chrono::steady_clock; + using TimePoint = Clock::time_point; + + struct Message : P2pMessage { + enum Type { + NOTIFY, + REQUEST, + REPLY + }; + + Type messageType; + uint32_t returnCode; + + Message(P2pMessage&& msg, Type messageType, uint32_t returnCode = 0); + size_t size() const; + }; + + P2pContext(System::Dispatcher& dispatcher, System::TcpConnection&& conn, + bool isIncoming, const NetworkAddress& remoteAddress, std::chrono::nanoseconds timedSyncInterval, const CORE_SYNC_DATA& timedSyncData); + ~P2pContext(); + + PeerIdType getPeerId() const; + uint16_t getPeerPort() const; + const NetworkAddress& getRemoteAddress() const; + bool isIncoming() const; + + void setPeerInfo(uint8_t protocolVersion, PeerIdType id, uint16_t port); + bool readCommand(LevinProtocol::Command& cmd); + void writeMessage(const Message& msg); + + void start(); + void stop(); + +private: + + uint8_t version = 0; + const bool incoming; + const NetworkAddress remoteAddress; + PeerIdType peerId = 0; + uint16_t peerPort = 0; + + System::Dispatcher& dispatcher; + System::ContextGroup contextGroup; + const TimePoint timeStarted; + bool stopped = false; + TimePoint lastReadTime; + + // timed sync info + const std::chrono::nanoseconds timedSyncInterval; + const CORE_SYNC_DATA& timedSyncData; + System::Timer timedSyncTimer; + System::Event timedSyncFinished; + + System::TcpConnection connection; + System::Event writeEvent; + System::Event readEvent; + + void timedSyncLoop(); +}; + +P2pContext::Message makeReply(uint32_t command, const BinaryArray& data, uint32_t returnCode); +P2pContext::Message makeRequest(uint32_t command, const BinaryArray& data); + +std::ostream& operator <<(std::ostream& s, const P2pContext& conn); + +} diff --git a/src/CryptoNote/UnsignedKeyInput.cpp b/src/P2p/P2pContextOwner.cpp old mode 100755 new mode 100644 similarity index 52% rename from src/CryptoNote/UnsignedKeyInput.cpp rename to src/P2p/P2pContextOwner.cpp index 72224dec..dabc8322 --- a/src/CryptoNote/UnsignedKeyInput.cpp +++ b/src/P2p/P2pContextOwner.cpp @@ -15,27 +15,33 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "UnsignedKeyInput.h" +#include "P2pContextOwner.h" +#include +#include "P2pContext.h" namespace CryptoNote { -UnsignedKeyInput::UnsignedKeyInput(uint64_t amount, std::vector&& outputs, const crypto::key_image& keyImage) : amount(amount), outputs(std::move(outputs)), keyImage(keyImage) { +P2pContextOwner::P2pContextOwner(P2pContext* ctx, ContextList& contextList) : contextList(contextList) { + contextIterator = contextList.insert(contextList.end(), ContextList::value_type(ctx)); } -uint64_t UnsignedKeyInput::getAmount() const { - return amount; +P2pContextOwner::P2pContextOwner(P2pContextOwner&& other) : contextList(other.contextList), contextIterator(other.contextIterator) { + other.contextIterator = contextList.end(); } -uint32_t UnsignedKeyInput::getOutputCount() const { - return static_cast(outputs.size()); +P2pContextOwner::~P2pContextOwner() { + if (contextIterator != contextList.end()) { + contextList.erase(contextIterator); + } } -uint32_t UnsignedKeyInput::getOutputIndex(uint32_t index) const { - return outputs[index]; +P2pContext& P2pContextOwner::get() { + assert(contextIterator != contextList.end()); + return *contextIterator->get(); } -const crypto::key_image& UnsignedKeyInput::getKeyImage() const { - return keyImage; +P2pContext* P2pContextOwner::operator -> () { + return &get(); } } diff --git a/src/wallet/MultiWallet.h b/src/P2p/P2pContextOwner.h similarity index 65% rename from src/wallet/MultiWallet.h rename to src/P2p/P2pContextOwner.h index 59840c4f..abb1adc0 100644 --- a/src/wallet/MultiWallet.h +++ b/src/P2p/P2pContextOwner.h @@ -17,28 +17,30 @@ #pragma once -#include "IMultiWallet.h" - -#include +#include +#include namespace CryptoNote { -class MultiWallet : public IMultiWallet { -public: - virtual ~MultiWallet(); +class P2pContext; - virtual void start() override; - virtual void stop() override; - virtual void refresh() override; +class P2pContextOwner { +public: + + typedef std::list> ContextList; + + P2pContextOwner(P2pContext* ctx, ContextList& contextList); + P2pContextOwner(P2pContextOwner&& other); + P2pContextOwner(const P2pContextOwner& other) = delete; + ~P2pContextOwner(); + + P2pContext& get(); + P2pContext* operator -> (); private: - enum MultiWalletState { - NOT_INITIALIZED = 0, - INITIALIZED - }; - MultiWalletState m_state; - std::string m_password; + ContextList& contextList; + ContextList::iterator contextIterator; }; -} //namespace CryptoNote +} diff --git a/src/System/LatchGuard.h b/src/P2p/P2pInterfaces.cpp similarity index 83% rename from src/System/LatchGuard.h rename to src/P2p/P2pInterfaces.cpp index 22ee3a26..c563e851 100644 --- a/src/System/LatchGuard.h +++ b/src/P2p/P2pInterfaces.cpp @@ -15,19 +15,11 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#pragma once +#include "P2pInterfaces.h" -namespace System { +namespace CryptoNote { -class Latch; - -class LatchGuard { -public: - explicit LatchGuard(Latch& latch); - ~LatchGuard(); - -private: - Latch& m_latch; -}; +IP2pConnection::~IP2pConnection() { +} } diff --git a/src/CryptoNote/UnsignedMultisignatureInput.h b/src/P2p/P2pInterfaces.h old mode 100755 new mode 100644 similarity index 66% rename from src/CryptoNote/UnsignedMultisignatureInput.h rename to src/P2p/P2pInterfaces.h index 8f7433e4..b42152ca --- a/src/CryptoNote/UnsignedMultisignatureInput.h +++ b/src/P2p/P2pInterfaces.h @@ -18,20 +18,30 @@ #pragma once #include +#include + +#include namespace CryptoNote { -class UnsignedMultisignatureInput { -public: - UnsignedMultisignatureInput(uint64_t amount, uint32_t outputIndex); - UnsignedMultisignatureInput(const UnsignedMultisignatureInput& other) = delete; - UnsignedMultisignatureInput& operator=(const UnsignedMultisignatureInput& other) = delete; - uint64_t getAmount() const; - uint32_t getOutputIndex() const; +struct P2pMessage { + uint32_t type; + BinaryArray data; +}; -private: - uint64_t amount; - uint32_t outputIndex; +class IP2pConnection { +public: + virtual ~IP2pConnection(); + virtual void read(P2pMessage &message) = 0; + virtual void write(const P2pMessage &message) = 0; + virtual void ban() = 0; + virtual void stop() = 0; +}; + +class IP2pNode { +public: + virtual std::unique_ptr receiveConnection() = 0; + virtual void stop() = 0; }; } diff --git a/src/p2p/p2p_networks.h b/src/P2p/P2pNetworks.h old mode 100644 new mode 100755 similarity index 100% rename from src/p2p/p2p_networks.h rename to src/P2p/P2pNetworks.h diff --git a/src/P2p/P2pNode.cpp b/src/P2p/P2pNode.cpp new file mode 100755 index 00000000..7ecafffd --- /dev/null +++ b/src/P2p/P2pNode.cpp @@ -0,0 +1,553 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "P2pNode.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "Serialization/BinaryInputStreamSerializer.h" +#include "Serialization/BinaryOutputStreamSerializer.h" + +#include "LevinProtocol.h" +#include "P2pConnectionProxy.h" +#include "P2pContext.h" +#include "P2pContextOwner.h" +#include "P2pNetworks.h" + +using namespace Common; +using namespace Logging; +using namespace System; + +namespace CryptoNote { + +namespace { + +class PeerIndexGenerator { +public: + + PeerIndexGenerator(size_t maxIndex) + : maxIndex(maxIndex), randCount(0) { + assert(maxIndex > 0); + } + + bool generate(size_t& num) { + while (randCount < (maxIndex + 1) * 3) { + ++randCount; + auto idx = getRandomIndex(); + if (visited.count(idx) == 0) { + visited.insert(idx); + num = idx; + return true; + } + } + + return false; + } + +private: + + size_t getRandomIndex() { + //divide by zero workaround + if (maxIndex == 0) { + return 0; + } + + size_t x = Crypto::rand() % (maxIndex + 1); + return (x * x * x) / (maxIndex * maxIndex); + } + + const size_t maxIndex; + size_t randCount; + std::set visited; +}; + +NetworkAddress getRemoteAddress(const TcpConnection& connection) { + auto addressAndPort = connection.getPeerAddressAndPort(); + NetworkAddress remoteAddress; + remoteAddress.ip = hostToNetwork(addressAndPort.first.getValue()); + remoteAddress.port = addressAndPort.second; + return remoteAddress; +} + +void doWithTimeoutAndThrow(System::Dispatcher& dispatcher, std::chrono::nanoseconds timeout, std::function f) { + std::string result; + System::ContextGroup cg(dispatcher); + System::ContextGroupTimeout cgTimeout(dispatcher, cg, timeout); + + cg.spawn([&] { + try { + f(); + } catch (System::InterruptedException&) { + result = "Operation timeout"; + } catch (std::exception& e) { + result = e.what(); + } + }); + + cg.wait(); + + if (!result.empty()) { + throw std::runtime_error(result); + } +} + +} + +P2pNode::P2pNode(const P2pNodeConfig& cfg, Dispatcher& dispatcher, Logging::ILogger& log, const Crypto::Hash& genesisHash, PeerIdType peerId) : + logger(log, "P2pNode:" + std::to_string(cfg.getBindPort())), + m_stopRequested(false), + m_cfg(cfg), + m_myPeerId(peerId), + m_genesisHash(genesisHash), + m_genesisPayload(CORE_SYNC_DATA{ 1, genesisHash }), + m_dispatcher(dispatcher), + workingContextGroup(dispatcher), + m_connectorTimer(dispatcher), + m_queueEvent(dispatcher) { + m_peerlist.init(cfg.getAllowLocalIp()); + m_listener = TcpListener(m_dispatcher, Ipv4Address(m_cfg.getBindIp()), m_cfg.getBindPort()); + for (auto& peer : cfg.getPeers()) { + m_peerlist.append_with_peer_white(peer); + } +} + +P2pNode::~P2pNode() { + assert(m_contexts.empty()); + assert(m_connectionQueue.empty()); +} + +std::unique_ptr P2pNode::receiveConnection() { + while (m_connectionQueue.empty()) { + m_queueEvent.wait(); + m_queueEvent.clear(); + if (m_stopRequested) { + throw InterruptedException(); + } + } + + auto connection = std::move(m_connectionQueue.front()); + m_connectionQueue.pop_front(); + + return connection; +} + +void P2pNode::start() { + workingContextGroup.spawn(std::bind(&P2pNode::acceptLoop, this)); + workingContextGroup.spawn(std::bind(&P2pNode::connectorLoop, this)); +} + +void P2pNode::stop() { + if (m_stopRequested) { + return; // already stopped + } + + m_stopRequested = true; + // clear prepared connections + m_connectionQueue.clear(); + // stop processing + m_queueEvent.set(); + workingContextGroup.interrupt(); + workingContextGroup.wait(); +} + +void P2pNode::serialize(ISerializer& s) { + uint8_t version = 1; + s(version, "version"); + + if (version != 1) { + return; + } + + s(m_peerlist, "peerlist"); +} + +void P2pNode::save(std::ostream& os) { + StdOutputStream stream(os); + BinaryOutputStreamSerializer a(stream); + CryptoNote::serialize(*this, a); +} + +void P2pNode::load(std::istream& in) { + StdInputStream stream(in); + BinaryInputStreamSerializer a(stream); + CryptoNote::serialize(*this, a); +} + +void P2pNode::acceptLoop() { + while (!m_stopRequested) { + try { + auto connection = m_listener.accept(); + auto ctx = new P2pContext(m_dispatcher, std::move(connection), true, + getRemoteAddress(connection), m_cfg.getTimedSyncInterval(), getGenesisPayload()); + logger(INFO) << "Incoming connection from " << ctx->getRemoteAddress(); + workingContextGroup.spawn([this, ctx] { + preprocessIncomingConnection(ContextPtr(ctx)); + }); + } catch (InterruptedException&) { + break; + } catch (const std::exception& e) { + logger(WARNING) << "Exception in acceptLoop: " << e.what(); + } + } + + logger(DEBUGGING) << "acceptLoop finished"; +} + +void P2pNode::connectorLoop() { + while (!m_stopRequested) { + try { + connectPeers(); + m_connectorTimer.sleep(m_cfg.getConnectInterval()); + } catch (InterruptedException&) { + break; + } catch (const std::exception& e) { + logger(WARNING) << "Exception in connectorLoop: " << e.what(); + } + } +} + +void P2pNode::connectPeers() { + if (!m_cfg.getExclusiveNodes().empty()) { + connectPeerList(m_cfg.getExclusiveNodes()); + return; + } + + // if white peer list is empty, get peers from seeds + if (m_peerlist.get_white_peers_count() == 0 && !m_cfg.getSeedNodes().empty()) { + auto seedNodes = m_cfg.getSeedNodes(); + std::random_shuffle(seedNodes.begin(), seedNodes.end()); + for (const auto& seed : seedNodes) { + auto conn = tryToConnectPeer(seed); + if (conn != nullptr && fetchPeerList(std::move(conn))) { + break; + } + } + } + + connectPeerList(m_cfg.getPriorityNodes()); + + const size_t totalExpectedConnectionsCount = m_cfg.getExpectedOutgoingConnectionsCount(); + const size_t expectedWhiteConnections = (totalExpectedConnectionsCount * m_cfg.getWhiteListConnectionsPercent()) / 100; + const size_t outgoingConnections = getOutgoingConnectionsCount(); + + if (outgoingConnections < totalExpectedConnectionsCount) { + if (outgoingConnections < expectedWhiteConnections) { + //start from white list + makeExpectedConnectionsCount(m_peerlist.getWhite(), expectedWhiteConnections); + //and then do grey list + makeExpectedConnectionsCount(m_peerlist.getGray(), totalExpectedConnectionsCount); + } else { + //start from grey list + makeExpectedConnectionsCount(m_peerlist.getGray(), totalExpectedConnectionsCount); + //and then do white list + makeExpectedConnectionsCount(m_peerlist.getWhite(), totalExpectedConnectionsCount); + } + } +} + +void P2pNode::makeExpectedConnectionsCount(const PeerlistManager::Peerlist& peerlist, size_t connectionsCount) { + while (getOutgoingConnectionsCount() < connectionsCount) { + if (peerlist.count() == 0) { + return; + } + + if (!makeNewConnectionFromPeerlist(peerlist)) { + break; + } + } +} + +bool P2pNode::makeNewConnectionFromPeerlist(const PeerlistManager::Peerlist& peerlist) { + size_t peerIndex; + PeerIndexGenerator idxGen(std::min(peerlist.count() - 1, m_cfg.getPeerListConnectRange())); + + for (size_t tryCount = 0; idxGen.generate(peerIndex) && tryCount < m_cfg.getPeerListGetTryCount(); ++tryCount) { + PeerlistEntry peer; + if (!peerlist.get(peer, peerIndex)) { + logger(WARNING) << "Failed to get peer from list, idx = " << peerIndex; + continue; + } + + if (isPeerUsed(peer)) { + continue; + } + + logger(DEBUGGING) << "Selected peer: [" << peer.id << " " << peer.adr << "] last_seen: " << + (peer.last_seen ? Common::timeIntervalToString(time(NULL) - peer.last_seen) : "never"); + + auto conn = tryToConnectPeer(peer.adr); + if (conn.get()) { + enqueueConnection(createProxy(std::move(conn))); + return true; + } + } + + return false; +} + +void P2pNode::preprocessIncomingConnection(ContextPtr ctx) { + try { + logger(DEBUGGING) << *ctx << "preprocessIncomingConnection"; + + OperationTimeout timeout(m_dispatcher, *ctx, m_cfg.getHandshakeTimeout()); + + // create proxy and process handshake + auto proxy = createProxy(std::move(ctx)); + if (proxy->processIncomingHandshake()) { + enqueueConnection(std::move(proxy)); + } + } catch (std::exception& e) { + logger(WARNING) << " Failed to process connection: " << e.what(); + } +} + +void P2pNode::connectPeerList(const std::vector& peers) { + for (const auto& address : peers) { + if (!isPeerConnected(address)) { + auto conn = tryToConnectPeer(address); + if (conn != nullptr) { + enqueueConnection(createProxy(std::move(conn))); + } + } + } +} + +bool P2pNode::isPeerConnected(const NetworkAddress& address) { + for (const auto& c : m_contexts) { + if (!c->isIncoming() && c->getRemoteAddress() == address) { + return true; + } + } + + return false; +} + +bool P2pNode::isPeerUsed(const PeerlistEntry& peer) { + if (m_myPeerId == peer.id) { + return true; //dont make connections to ourself + } + + for (const auto& c : m_contexts) { + if (c->getPeerId() == peer.id || (!c->isIncoming() && c->getRemoteAddress() == peer.adr)) { + return true; + } + } + + return false; +} + +P2pNode::ContextPtr P2pNode::tryToConnectPeer(const NetworkAddress& address) { + try { + TcpConnector connector(m_dispatcher); + TcpConnection tcpConnection; + + doWithTimeoutAndThrow(m_dispatcher, m_cfg.getConnectTimeout(), [&] { + tcpConnection = connector.connect( + Ipv4Address(Common::ipAddressToString(address.ip)), + static_cast(address.port)); + }); + + logger(DEBUGGING) << "connection established to " << address; + + return ContextPtr(new P2pContext(m_dispatcher, std::move(tcpConnection), false, address, m_cfg.getTimedSyncInterval(), getGenesisPayload())); + } catch (std::exception& e) { + logger(DEBUGGING) << "Connection to " << address << " failed: " << e.what(); + } + + return ContextPtr(); +} + +bool P2pNode::fetchPeerList(ContextPtr connection) { + try { + COMMAND_HANDSHAKE::request request{ getNodeData(), getGenesisPayload() }; + COMMAND_HANDSHAKE::response response; + + OperationTimeout timeout(m_dispatcher, *connection, m_cfg.getHandshakeTimeout()); + + connection->writeMessage(makeRequest(COMMAND_HANDSHAKE::ID, LevinProtocol::encode(request))); + + LevinProtocol::Command cmd; + if (!connection->readCommand(cmd)) { + throw std::runtime_error("Connection closed unexpectedly"); + } + + if (!cmd.isResponse || cmd.command != COMMAND_HANDSHAKE::ID) { + throw std::runtime_error("Received unexpected reply"); + } + + if (!LevinProtocol::decode(cmd.buf, response)) { + throw std::runtime_error("Invalid reply format"); + } + + if (response.node_data.network_id != request.node_data.network_id) { + logger(ERROR) << *connection << "COMMAND_HANDSHAKE failed, wrong network: " << response.node_data.network_id; + return false; + } + + return handleRemotePeerList(response.local_peerlist, response.node_data.local_time); + } catch (std::exception& e) { + logger(INFO) << *connection << "Failed to obtain peer list: " << e.what(); + } + + return false; +} + +namespace { + +std::list fixTimeDelta(const std::list& peerlist, time_t remoteTime) { + //fix time delta + int64_t delta = time(nullptr) - remoteTime; + std::list peerlistCopy(peerlist); + + for (PeerlistEntry& be : peerlistCopy) { + if (be.last_seen > uint64_t(remoteTime)) { + throw std::runtime_error("Invalid peerlist entry (time in future)"); + } + + be.last_seen += delta; + } + + return peerlistCopy; +} +} + +bool P2pNode::handleRemotePeerList(const std::list& peerlist, time_t remoteTime) { + return m_peerlist.merge_peerlist(fixTimeDelta(peerlist, remoteTime)); +} + +const CORE_SYNC_DATA& P2pNode::getGenesisPayload() const { + return m_genesisPayload; +} + +std::list P2pNode::getLocalPeerList() const { + std::list peerlist; + m_peerlist.get_peerlist_head(peerlist); + return peerlist; +} + +basic_node_data P2pNode::getNodeData() const { + basic_node_data nodeData; + nodeData.network_id = m_cfg.getNetworkId(); + nodeData.version = P2PProtocolVersion::CURRENT; + nodeData.local_time = time(nullptr); + nodeData.peer_id = m_myPeerId; + + if (m_cfg.getHideMyPort()) { + nodeData.my_port = 0; + } else { + nodeData.my_port = m_cfg.getExternalPort() ? m_cfg.getExternalPort() : m_cfg.getBindPort(); + } + + return nodeData; +} + +PeerIdType P2pNode::getPeerId() const { + return m_myPeerId; +} + +size_t P2pNode::getOutgoingConnectionsCount() const { + size_t count = 0; + + for (const auto& ctx : m_contexts) { + if (!ctx->isIncoming()) { + ++count; + } + } + + return count; +} + +std::unique_ptr P2pNode::createProxy(ContextPtr ctx) { + return std::unique_ptr( + new P2pConnectionProxy(P2pContextOwner(ctx.release(), m_contexts), *this)); +} + +void P2pNode::enqueueConnection(std::unique_ptr proxy) { + if (m_stopRequested) { + return; // skip operation + } + + m_connectionQueue.push_back(std::move(proxy)); + m_queueEvent.set(); +} + +void P2pNode::tryPing(P2pContext& ctx) { + if (ctx.getPeerId() == m_myPeerId || !m_peerlist.is_ip_allowed(ctx.getRemoteAddress().ip) || ctx.getPeerPort() == 0) { + return; + } + + NetworkAddress peerAddress; + peerAddress.ip = ctx.getRemoteAddress().ip; + peerAddress.port = ctx.getPeerPort(); + + try { + TcpConnector connector(m_dispatcher); + TcpConnection connection; + + doWithTimeoutAndThrow(m_dispatcher, m_cfg.getConnectTimeout(), [&] { + connection = connector.connect(Ipv4Address(Common::ipAddressToString(peerAddress.ip)), static_cast(peerAddress.port)); + }); + + doWithTimeoutAndThrow(m_dispatcher, m_cfg.getHandshakeTimeout(), [&] { + LevinProtocol proto(connection); + COMMAND_PING::request request; + COMMAND_PING::response response; + proto.invoke(COMMAND_PING::ID, request, response); + + if (response.status == PING_OK_RESPONSE_STATUS_TEXT && response.peer_id == ctx.getPeerId()) { + PeerlistEntry entry; + entry.adr = peerAddress; + entry.id = ctx.getPeerId(); + entry.last_seen = time(nullptr); + m_peerlist.append_with_peer_white(entry); + } else { + logger(Logging::DEBUGGING) << ctx << "back ping invoke wrong response \"" << response.status << "\" from" + << peerAddress << ", expected peerId=" << ctx.getPeerId() << ", got " << response.peer_id; + } + }); + } catch (std::exception& e) { + logger(DEBUGGING) << "Ping to " << peerAddress << " failed: " << e.what(); + } +} + +void P2pNode::handleNodeData(const basic_node_data& node, P2pContext& context) { + if (node.network_id != m_cfg.getNetworkId()) { + std::ostringstream msg; + msg << context << "COMMAND_HANDSHAKE Failed, wrong network! (" << node.network_id << ")"; + throw std::runtime_error(msg.str()); + } + + if (node.peer_id == m_myPeerId) { + throw std::runtime_error("Connection to self detected"); + } + + context.setPeerInfo(node.version, node.peer_id, static_cast(node.my_port)); + if (!context.isIncoming()) { + m_peerlist.set_peer_just_seen(node.peer_id, context.getRemoteAddress()); + } +} + +} diff --git a/src/P2p/P2pNode.h b/src/P2p/P2pNode.h new file mode 100755 index 00000000..27ae7c1a --- /dev/null +++ b/src/P2p/P2pNode.h @@ -0,0 +1,122 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "IP2pNodeInternal.h" +#include "IStreamSerializable.h" +#include "NetNodeConfig.h" +#include "P2pInterfaces.h" +#include "P2pNodeConfig.h" +#include "P2pProtocolDefinitions.h" +#include "PeerListManager.h" + +namespace CryptoNote { + +class P2pContext; +class P2pConnectionProxy; + +class P2pNode : + public IP2pNode, + public IStreamSerializable, + IP2pNodeInternal { + +public: + + P2pNode( + const P2pNodeConfig& cfg, + System::Dispatcher& dispatcher, + Logging::ILogger& log, + const Crypto::Hash& genesisHash, + PeerIdType peerId); + + ~P2pNode(); + + // IP2pNode + virtual std::unique_ptr receiveConnection() override; + virtual void stop() override; + + // IStreamSerializable + virtual void save(std::ostream& os) override; + virtual void load(std::istream& in) override; + + // P2pNode + void start(); + void serialize(ISerializer& s); + +private: + typedef std::unique_ptr ContextPtr; + typedef std::list ContextList; + + Logging::LoggerRef logger; + bool m_stopRequested; + const P2pNodeConfig m_cfg; + const PeerIdType m_myPeerId; + const Crypto::Hash m_genesisHash; + const CORE_SYNC_DATA m_genesisPayload; + + System::Dispatcher& m_dispatcher; + System::ContextGroup workingContextGroup; + System::TcpListener m_listener; + System::Timer m_connectorTimer; + PeerlistManager m_peerlist; + ContextList m_contexts; + System::Event m_queueEvent; + std::deque> m_connectionQueue; + + // IP2pNodeInternal + virtual const CORE_SYNC_DATA& getGenesisPayload() const override; + virtual std::list getLocalPeerList() const override; + virtual basic_node_data getNodeData() const override; + virtual PeerIdType getPeerId() const override; + + virtual void handleNodeData(const basic_node_data& node, P2pContext& ctx) override; + virtual bool handleRemotePeerList(const std::list& peerlist, time_t local_time) override; + virtual void tryPing(P2pContext& ctx) override; + + // spawns + void acceptLoop(); + void connectorLoop(); + + // connection related + void connectPeers(); + void connectPeerList(const std::vector& peers); + bool isPeerConnected(const NetworkAddress& address); + bool isPeerUsed(const PeerlistEntry& peer); + ContextPtr tryToConnectPeer(const NetworkAddress& address); + bool fetchPeerList(ContextPtr connection); + + // making and processing connections + size_t getOutgoingConnectionsCount() const; + void makeExpectedConnectionsCount(const PeerlistManager::Peerlist& peerlist, size_t connectionsCount); + bool makeNewConnectionFromPeerlist(const PeerlistManager::Peerlist& peerlist); + void preprocessIncomingConnection(ContextPtr ctx); + void enqueueConnection(std::unique_ptr proxy); + std::unique_ptr createProxy(ContextPtr ctx); +}; + +} diff --git a/src/P2p/P2pNodeConfig.cpp b/src/P2p/P2pNodeConfig.cpp new file mode 100644 index 00000000..51c715cc --- /dev/null +++ b/src/P2p/P2pNodeConfig.cpp @@ -0,0 +1,130 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "P2pNodeConfig.h" +#include "P2pNetworks.h" + +#include + +namespace CryptoNote { + +namespace { + +const std::chrono::nanoseconds P2P_DEFAULT_CONNECT_INTERVAL = std::chrono::seconds(2); +const size_t P2P_DEFAULT_CONNECT_RANGE = 20; +const size_t P2P_DEFAULT_PEERLIST_GET_TRY_COUNT = 10; + +} + +P2pNodeConfig::P2pNodeConfig() : + timedSyncInterval(std::chrono::seconds(P2P_DEFAULT_HANDSHAKE_INTERVAL)), + handshakeTimeout(std::chrono::milliseconds(P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT)), + connectInterval(P2P_DEFAULT_CONNECT_INTERVAL), + connectTimeout(std::chrono::milliseconds(P2P_DEFAULT_CONNECTION_TIMEOUT)), + networkId(BYTECOIN_NETWORK), + expectedOutgoingConnectionsCount(P2P_DEFAULT_CONNECTIONS_COUNT), + whiteListConnectionsPercent(P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT), + peerListConnectRange(P2P_DEFAULT_CONNECT_RANGE), + peerListGetTryCount(P2P_DEFAULT_PEERLIST_GET_TRY_COUNT) { +} + +// getters + +std::chrono::nanoseconds P2pNodeConfig::getTimedSyncInterval() const { + return timedSyncInterval; +} + +std::chrono::nanoseconds P2pNodeConfig::getHandshakeTimeout() const { + return handshakeTimeout; +} + +std::chrono::nanoseconds P2pNodeConfig::getConnectInterval() const { + return connectInterval; +} + +std::chrono::nanoseconds P2pNodeConfig::getConnectTimeout() const { + return connectTimeout; +} + +size_t P2pNodeConfig::getExpectedOutgoingConnectionsCount() const { + return expectedOutgoingConnectionsCount; +} + +size_t P2pNodeConfig::getWhiteListConnectionsPercent() const { + return whiteListConnectionsPercent; +} + +boost::uuids::uuid P2pNodeConfig::getNetworkId() const { + if (getTestnet()) { + boost::uuids::uuid copy = networkId; + copy.data[0] += 1; + return copy; + } + return networkId; +} + +size_t P2pNodeConfig::getPeerListConnectRange() const { + return peerListConnectRange; +} + +size_t P2pNodeConfig::getPeerListGetTryCount() const { + return peerListGetTryCount; +} + +// setters + +void P2pNodeConfig::setTimedSyncInterval(std::chrono::nanoseconds interval) { + timedSyncInterval = interval; +} + +void P2pNodeConfig::setHandshakeTimeout(std::chrono::nanoseconds timeout) { + handshakeTimeout = timeout; +} + +void P2pNodeConfig::setConnectInterval(std::chrono::nanoseconds interval) { + connectInterval = interval; +} + +void P2pNodeConfig::setConnectTimeout(std::chrono::nanoseconds timeout) { + connectTimeout = timeout; +} + +void P2pNodeConfig::setExpectedOutgoingConnectionsCount(size_t count) { + expectedOutgoingConnectionsCount = count; +} + +void P2pNodeConfig::setWhiteListConnectionsPercent(size_t percent) { + if (percent > 100) { + throw std::invalid_argument("whiteListConnectionsPercent cannot be greater than 100"); + } + + whiteListConnectionsPercent = percent; +} + +void P2pNodeConfig::setNetworkId(const boost::uuids::uuid& id) { + networkId = id; +} + +void P2pNodeConfig::setPeerListConnectRange(size_t range) { + peerListConnectRange = range; +} + +void P2pNodeConfig::setPeerListGetTryCount(size_t count) { + peerListGetTryCount = count; +} + +} diff --git a/src/P2p/P2pNodeConfig.h b/src/P2p/P2pNodeConfig.h new file mode 100644 index 00000000..d8f5fa1c --- /dev/null +++ b/src/P2p/P2pNodeConfig.h @@ -0,0 +1,63 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include "NetNodeConfig.h" + +namespace CryptoNote { + +class P2pNodeConfig : public NetNodeConfig { +public: + P2pNodeConfig(); + + // getters + std::chrono::nanoseconds getTimedSyncInterval() const; + std::chrono::nanoseconds getHandshakeTimeout() const; + std::chrono::nanoseconds getConnectInterval() const; + std::chrono::nanoseconds getConnectTimeout() const; + size_t getExpectedOutgoingConnectionsCount() const; + size_t getWhiteListConnectionsPercent() const; + boost::uuids::uuid getNetworkId() const; + size_t getPeerListConnectRange() const; + size_t getPeerListGetTryCount() const; + + // setters + void setTimedSyncInterval(std::chrono::nanoseconds interval); + void setHandshakeTimeout(std::chrono::nanoseconds timeout); + void setConnectInterval(std::chrono::nanoseconds interval); + void setConnectTimeout(std::chrono::nanoseconds timeout); + void setExpectedOutgoingConnectionsCount(size_t count); + void setWhiteListConnectionsPercent(size_t percent); + void setNetworkId(const boost::uuids::uuid& id); + void setPeerListConnectRange(size_t range); + void setPeerListGetTryCount(size_t count); + +private: + std::chrono::nanoseconds timedSyncInterval; + std::chrono::nanoseconds handshakeTimeout; + std::chrono::nanoseconds connectInterval; + std::chrono::nanoseconds connectTimeout; + boost::uuids::uuid networkId; + size_t expectedOutgoingConnectionsCount; + size_t whiteListConnectionsPercent; + size_t peerListConnectRange; + size_t peerListGetTryCount; +}; + +} diff --git a/src/p2p/p2p_protocol_defs.h b/src/P2p/P2pProtocolDefinitions.h old mode 100644 new mode 100755 similarity index 85% rename from src/p2p/p2p_protocol_defs.h rename to src/P2p/P2pProtocolDefinitions.h index 84619338..e83d6f79 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/P2p/P2pProtocolDefinitions.h @@ -17,16 +17,16 @@ #pragma once -#include "p2p_protocol_types.h" +#include "P2pProtocolTypes.h" #include "crypto/crypto.h" -#include "cryptonote_config.h" -#include "cryptonote_core/cryptonote_stat_info.h" +#include "CryptoNoteConfig.h" +#include "CryptoNoteCore/CryptoNoteStatInfo.h" // new serialization -#include "serialization/ISerializer.h" -#include "serialization/SerializationOverloads.h" -#include "cryptonote_core/cryptonote_serialization.h" +#include "Serialization/ISerializer.h" +#include "Serialization/SerializationOverloads.h" +#include "CryptoNoteCore/CryptoNoteSerialization.h" namespace CryptoNote { @@ -64,7 +64,7 @@ namespace CryptoNote uint8_t version; uint64_t local_time; uint32_t my_port; - peerid_type peer_id; + PeerIdType peer_id; void serialize(ISerializer& s) { KV_MEMBER(network_id) @@ -80,8 +80,8 @@ namespace CryptoNote struct CORE_SYNC_DATA { - uint64_t current_height; - crypto::hash top_id; + uint32_t current_height; + Crypto::Hash top_id; void serialize(ISerializer& s) { KV_MEMBER(current_height) @@ -96,7 +96,7 @@ namespace CryptoNote /************************************************************************/ struct COMMAND_HANDSHAKE { - const static int ID = P2P_COMMANDS_POOL_BASE + 1; + enum { ID = P2P_COMMANDS_POOL_BASE + 1 }; struct request { @@ -114,7 +114,7 @@ namespace CryptoNote { basic_node_data node_data; CORE_SYNC_DATA payload_data; - std::list local_peerlist; + std::list local_peerlist; void serialize(ISerializer& s) { KV_MEMBER(node_data) @@ -130,7 +130,7 @@ namespace CryptoNote /************************************************************************/ struct COMMAND_TIMED_SYNC { - const static int ID = P2P_COMMANDS_POOL_BASE + 2; + enum { ID = P2P_COMMANDS_POOL_BASE + 2 }; struct request { @@ -146,7 +146,7 @@ namespace CryptoNote { uint64_t local_time; CORE_SYNC_DATA payload_data; - std::list local_peerlist; + std::list local_peerlist; void serialize(ISerializer& s) { KV_MEMBER(local_time) @@ -167,7 +167,7 @@ namespace CryptoNote have accessible connection point. Only other nodes can add peer to peerlist, and ONLY in case when peer has accepted connection and answered to ping. */ - const static int ID = P2P_COMMANDS_POOL_BASE + 3; + enum { ID = P2P_COMMANDS_POOL_BASE + 3 }; #define PING_OK_RESPONSE_STATUS_TEXT "OK" @@ -180,7 +180,7 @@ namespace CryptoNote struct response { std::string status; - peerid_type peer_id; + PeerIdType peer_id; void serialize(ISerializer& s) { KV_MEMBER(status) @@ -196,9 +196,9 @@ namespace CryptoNote struct proof_of_trust { - peerid_type peer_id; + PeerIdType peer_id; uint64_t time; - crypto::signature sign; + Crypto::Signature sign; void serialize(ISerializer& s) { KV_MEMBER(peer_id) @@ -207,16 +207,16 @@ namespace CryptoNote } }; - inline crypto::hash get_proof_of_trust_hash(const proof_of_trust& pot) { + inline Crypto::Hash get_proof_of_trust_hash(const proof_of_trust& pot) { std::string s; s.append(reinterpret_cast(&pot.peer_id), sizeof(pot.peer_id)); s.append(reinterpret_cast(&pot.time), sizeof(pot.time)); - return crypto::cn_fast_hash(s.data(), s.size()); + return Crypto::cn_fast_hash(s.data(), s.size()); } struct COMMAND_REQUEST_STAT_INFO { - const static int ID = P2P_COMMANDS_POOL_BASE + 4; + enum { ID = P2P_COMMANDS_POOL_BASE + 4 }; struct request { @@ -251,7 +251,7 @@ namespace CryptoNote /************************************************************************/ struct COMMAND_REQUEST_NETWORK_STATE { - const static int ID = P2P_COMMANDS_POOL_BASE + 5; + enum { ID = P2P_COMMANDS_POOL_BASE + 5 }; struct request { @@ -264,10 +264,10 @@ namespace CryptoNote struct response { - std::list local_peerlist_white; - std::list local_peerlist_gray; + std::list local_peerlist_white; + std::list local_peerlist_gray; std::list connections_list; - peerid_type my_id; + PeerIdType my_id; uint64_t local_time; void serialize(ISerializer& s) { @@ -285,7 +285,7 @@ namespace CryptoNote /************************************************************************/ struct COMMAND_REQUEST_PEER_ID { - const static int ID = P2P_COMMANDS_POOL_BASE + 6; + enum { ID = P2P_COMMANDS_POOL_BASE + 6 }; struct request { @@ -294,7 +294,7 @@ namespace CryptoNote struct response { - peerid_type my_id; + PeerIdType my_id; void serialize(ISerializer& s) { KV_MEMBER(my_id) diff --git a/src/p2p/p2p_protocol_types.h b/src/P2p/P2pProtocolTypes.h old mode 100644 new mode 100755 similarity index 78% rename from src/p2p/p2p_protocol_types.h rename to src/P2p/P2pProtocolTypes.h index 27a55100..b8034102 --- a/src/p2p/p2p_protocol_types.h +++ b/src/P2p/P2pProtocolTypes.h @@ -26,41 +26,41 @@ namespace CryptoNote { typedef boost::uuids::uuid uuid; typedef boost::uuids::uuid net_connection_id; - typedef uint64_t peerid_type; + typedef uint64_t PeerIdType; #pragma pack (push, 1) - struct net_address + struct NetworkAddress { uint32_t ip; uint32_t port; }; - struct peerlist_entry + struct PeerlistEntry { - net_address adr; - peerid_type id; - time_t last_seen; + NetworkAddress adr; + PeerIdType id; + uint64_t last_seen; }; struct connection_entry { - net_address adr; - peerid_type id; + NetworkAddress adr; + PeerIdType id; bool is_income; }; #pragma pack(pop) - inline bool operator < (const net_address& a, const net_address& b) { + inline bool operator < (const NetworkAddress& a, const NetworkAddress& b) { return std::tie(a.ip, a.port) < std::tie(b.ip, b.port); } - inline bool operator == (const net_address& a, const net_address& b) { + inline bool operator == (const NetworkAddress& a, const NetworkAddress& b) { return memcmp(&a, &b, sizeof(a)) == 0; } - inline std::ostream& operator << (std::ostream& s, const net_address& na) { + inline std::ostream& operator << (std::ostream& s, const NetworkAddress& na) { return s << Common::ipAddressToString(na.ip) << ":" << std::to_string(na.port); } diff --git a/src/p2p/PeerListManager.cpp b/src/P2p/PeerListManager.cpp old mode 100644 new mode 100755 similarity index 59% rename from src/p2p/PeerListManager.cpp rename to src/P2p/PeerListManager.cpp index 70f31eb2..c1b5de09 --- a/src/p2p/PeerListManager.cpp +++ b/src/P2p/PeerListManager.cpp @@ -21,38 +21,101 @@ #include #include +#include "Serialization/SerializationOverloads.h" + using namespace CryptoNote; +namespace CryptoNote { + template + bool serialize(boost::multi_index_container& value, Common::StringView name, ISerializer& s) { + if (s.type() == ISerializer::INPUT) { + readSequence(std::inserter(value, value.end()), name, s); + } else { + writeSequence(value.begin(), value.end(), name, s); + } + + return true; + } + + void serialize(NetworkAddress& na, ISerializer& s) { + s(na.ip, "ip"); + s(na.port, "port"); + } + + void serialize(PeerlistEntry& pe, ISerializer& s) { + s(pe.adr, "adr"); + s(pe.id, "id"); + s(pe.last_seen, "last_seen"); + } + +} + +PeerlistManager::Peerlist::Peerlist(peers_indexed& peers, size_t maxSize) : + m_peers(peers), m_maxSize(maxSize) { +} + +void PeerlistManager::serialize(ISerializer& s) { + const uint8_t currentVersion = 1; + uint8_t version = currentVersion; + + s(version, "version"); + + if (version != currentVersion) { + return; + } + + s(m_peers_white, "whitelist"); + s(m_peers_gray, "graylist"); +} + +size_t PeerlistManager::Peerlist::count() const { + return m_peers.size(); +} + +bool PeerlistManager::Peerlist::get(PeerlistEntry& entry, size_t i) const { + if (i >= m_peers.size()) + return false; + + peers_indexed::index::type& by_time_index = m_peers.get(); + + auto it = by_time_index.rbegin(); + std::advance(it, i); + entry = *it; + + return true; +} + +void PeerlistManager::Peerlist::trim() { + peers_indexed::index::type& sorted_index = m_peers.get(); + while (m_peers.size() > m_maxSize) { + sorted_index.erase(sorted_index.begin()); + } +} + +PeerlistManager::PeerlistManager() : + m_whitePeerlist(m_peers_white, CryptoNote::P2P_LOCAL_WHITE_PEERLIST_LIMIT), + m_grayPeerlist(m_peers_gray, CryptoNote::P2P_LOCAL_GRAY_PEERLIST_LIMIT) {} + //-------------------------------------------------------------------------------------------------- -bool peerlist_manager::init(bool allow_local_ip) +bool PeerlistManager::init(bool allow_local_ip) { m_allow_local_ip = allow_local_ip; return true; } //-------------------------------------------------------------------------------------------------- - void peerlist_manager::trim_white_peerlist() -{ - while (m_peers_gray.size() > CryptoNote::P2P_LOCAL_GRAY_PEERLIST_LIMIT) - { - peers_indexed::index::type& sorted_index = m_peers_gray.get(); - sorted_index.erase(sorted_index.begin()); - } +void PeerlistManager::trim_white_peerlist() { + m_whitePeerlist.trim(); } //-------------------------------------------------------------------------------------------------- - void peerlist_manager::trim_gray_peerlist() -{ - while (m_peers_white.size() > CryptoNote::P2P_LOCAL_WHITE_PEERLIST_LIMIT) - { - peers_indexed::index::type& sorted_index = m_peers_white.get(); - sorted_index.erase(sorted_index.begin()); - } +void PeerlistManager::trim_gray_peerlist() { + m_grayPeerlist.trim(); } -//-------------------------------------------------------------------------------------------------- -bool peerlist_manager::merge_peerlist(const std::list& outer_bs) +//-------------------------------------------------------------------------------------------------- +bool PeerlistManager::merge_peerlist(const std::list& outer_bs) { - for(const peerlist_entry& be : outer_bs) { + for(const PeerlistEntry& be : outer_bs) { append_with_peer_gray(be); } @@ -62,37 +125,19 @@ bool peerlist_manager::merge_peerlist(const std::list& outer_bs) } //-------------------------------------------------------------------------------------------------- -bool peerlist_manager::get_white_peer_by_index(peerlist_entry& p, size_t i) -{ - if (i >= m_peers_white.size()) - return false; - - peers_indexed::index::type& by_time_index = m_peers_white.get(); - - auto it = by_time_index.rbegin(); - std::advance(it, i); - p = *it; - - return true; +bool PeerlistManager::get_white_peer_by_index(PeerlistEntry& p, size_t i) const { + return m_whitePeerlist.get(p, i); } + //-------------------------------------------------------------------------------------------------- -bool peerlist_manager::get_gray_peer_by_index(peerlist_entry& p, size_t i) -{ - if (i >= m_peers_gray.size()) - return false; - - peers_indexed::index::type& by_time_index = m_peers_gray.get(); - - auto it = by_time_index.rbegin(); - std::advance(it, i); - p = *it; - - return true; +bool PeerlistManager::get_gray_peer_by_index(PeerlistEntry& p, size_t i) const { + return m_grayPeerlist.get(p, i); } + //-------------------------------------------------------------------------------------------------- -bool peerlist_manager::is_ip_allowed(uint32_t ip) +bool PeerlistManager::is_ip_allowed(uint32_t ip) const { System::Ipv4Address addr(networkToHost(ip)); @@ -109,9 +154,9 @@ bool peerlist_manager::is_ip_allowed(uint32_t ip) } //-------------------------------------------------------------------------------------------------- -bool peerlist_manager::get_peerlist_head(std::list& bs_head, uint32_t depth) +bool PeerlistManager::get_peerlist_head(std::list& bs_head, uint32_t depth) const { - peers_indexed::index::type& by_time_index = m_peers_white.get(); + const peers_indexed::index::type& by_time_index = m_peers_white.get(); uint32_t cnt = 0; BOOST_REVERSE_FOREACH(const peers_indexed::value_type& vl, by_time_index) @@ -126,10 +171,10 @@ bool peerlist_manager::get_peerlist_head(std::list& bs_head, uin } //-------------------------------------------------------------------------------------------------- -bool peerlist_manager::get_peerlist_full(std::list& pl_gray, std::list& pl_white) +bool PeerlistManager::get_peerlist_full(std::list& pl_gray, std::list& pl_white) const { - peers_indexed::index::type& by_time_index_gr = m_peers_gray.get(); - peers_indexed::index::type& by_time_index_wt = m_peers_white.get(); + const peers_indexed::index::type& by_time_index_gr = m_peers_gray.get(); + const peers_indexed::index::type& by_time_index_wt = m_peers_white.get(); std::copy(by_time_index_gr.rbegin(), by_time_index_gr.rend(), std::back_inserter(pl_gray)); std::copy(by_time_index_wt.rbegin(), by_time_index_wt.rend(), std::back_inserter(pl_white)); @@ -138,20 +183,20 @@ bool peerlist_manager::get_peerlist_full(std::list& pl_gray, std } //-------------------------------------------------------------------------------------------------- -bool peerlist_manager::set_peer_just_seen(peerid_type peer, uint32_t ip, uint32_t port) +bool PeerlistManager::set_peer_just_seen(PeerIdType peer, uint32_t ip, uint32_t port) { - net_address addr; + NetworkAddress addr; addr.ip = ip; addr.port = port; return set_peer_just_seen(peer, addr); } //-------------------------------------------------------------------------------------------------- -bool peerlist_manager::set_peer_just_seen(peerid_type peer, const net_address& addr) +bool PeerlistManager::set_peer_just_seen(PeerIdType peer, const NetworkAddress& addr) { try { //find in white list - peerlist_entry ple; + PeerlistEntry ple; ple.adr = addr; ple.id = peer; ple.last_seen = time(NULL); @@ -163,7 +208,7 @@ bool peerlist_manager::set_peer_just_seen(peerid_type peer, const net_address& a } //-------------------------------------------------------------------------------------------------- -bool peerlist_manager::append_with_peer_white(const peerlist_entry& ple) +bool PeerlistManager::append_with_peer_white(const PeerlistEntry& ple) { try { if (!is_ip_allowed(ple.adr.ip)) @@ -191,7 +236,7 @@ bool peerlist_manager::append_with_peer_white(const peerlist_entry& ple) } //-------------------------------------------------------------------------------------------------- -bool peerlist_manager::append_with_peer_gray(const peerlist_entry& ple) +bool PeerlistManager::append_with_peer_gray(const PeerlistEntry& ple) { try { if (!is_ip_allowed(ple.adr.ip)) @@ -220,3 +265,11 @@ bool peerlist_manager::append_with_peer_gray(const peerlist_entry& ple) return false; } //-------------------------------------------------------------------------------------------------- + +PeerlistManager::Peerlist& PeerlistManager::getWhite() { + return m_whitePeerlist; +} + +PeerlistManager::Peerlist& PeerlistManager::getGray() { + return m_grayPeerlist; +} diff --git a/src/p2p/PeerListManager.h b/src/P2p/PeerListManager.h similarity index 53% rename from src/p2p/PeerListManager.h rename to src/P2p/PeerListManager.h index 17863f0b..2ea4f7aa 100644 --- a/src/p2p/PeerListManager.h +++ b/src/P2p/PeerListManager.h @@ -24,71 +24,75 @@ #include #include -#include "p2p_protocol_types.h" -#include "cryptonote_config.h" +#include "P2pProtocolTypes.h" +#include "CryptoNoteConfig.h" namespace CryptoNote { +class ISerializer; /************************************************************************/ /* */ /************************************************************************/ -class peerlist_manager -{ -public: - bool init(bool allow_local_ip); - size_t get_white_peers_count(){ return m_peers_white.size(); } - size_t get_gray_peers_count(){ return m_peers_gray.size(); } - bool merge_peerlist(const std::list& outer_bs); - bool get_peerlist_head(std::list& bs_head, uint32_t depth = CryptoNote::P2P_DEFAULT_PEERS_IN_HANDSHAKE); - bool get_peerlist_full(std::list& pl_gray, std::list& pl_white); - bool get_white_peer_by_index(peerlist_entry& p, size_t i); - bool get_gray_peer_by_index(peerlist_entry& p, size_t i); - bool append_with_peer_white(const peerlist_entry& pr); - bool append_with_peer_gray(const peerlist_entry& pr); - bool set_peer_just_seen(peerid_type peer, uint32_t ip, uint32_t port); - bool set_peer_just_seen(peerid_type peer, const net_address& addr); - bool set_peer_unreachable(const peerlist_entry& pr); - bool is_ip_allowed(uint32_t ip); - void trim_white_peerlist(); - void trim_gray_peerlist(); - -private: - +class PeerlistManager { struct by_time{}; struct by_id{}; struct by_addr{}; typedef boost::multi_index_container< - peerlist_entry, + PeerlistEntry, boost::multi_index::indexed_by< // access by peerlist_entry::net_adress - boost::multi_index::ordered_unique, boost::multi_index::member >, + boost::multi_index::ordered_unique, boost::multi_index::member >, // sort by peerlist_entry::last_seen< - boost::multi_index::ordered_non_unique, boost::multi_index::member > + boost::multi_index::ordered_non_unique, boost::multi_index::member > > > peers_indexed; public: - template - void serialize(Archive &a, const t_version_type ver) - { - if (ver < 4) - return; + class Peerlist { + public: + Peerlist(peers_indexed& peers, size_t maxSize); + size_t count() const; + bool get(PeerlistEntry& entry, size_t index) const; + void trim(); - a & m_peers_white; - a & m_peers_gray; - } + private: + peers_indexed& m_peers; + const size_t m_maxSize; + }; + + PeerlistManager(); + + bool init(bool allow_local_ip); + size_t get_white_peers_count() const { return m_peers_white.size(); } + size_t get_gray_peers_count() const { return m_peers_gray.size(); } + bool merge_peerlist(const std::list& outer_bs); + bool get_peerlist_head(std::list& bs_head, uint32_t depth = CryptoNote::P2P_DEFAULT_PEERS_IN_HANDSHAKE) const; + bool get_peerlist_full(std::list& pl_gray, std::list& pl_white) const; + bool get_white_peer_by_index(PeerlistEntry& p, size_t i) const; + bool get_gray_peer_by_index(PeerlistEntry& p, size_t i) const; + bool append_with_peer_white(const PeerlistEntry& pr); + bool append_with_peer_gray(const PeerlistEntry& pr); + bool set_peer_just_seen(PeerIdType peer, uint32_t ip, uint32_t port); + bool set_peer_just_seen(PeerIdType peer, const NetworkAddress& addr); + bool set_peer_unreachable(const PeerlistEntry& pr); + bool is_ip_allowed(uint32_t ip) const; + void trim_white_peerlist(); + void trim_gray_peerlist(); + + void serialize(ISerializer& s); + + Peerlist& getWhite(); + Peerlist& getGray(); private: - - friend class boost::serialization::access; std::string m_config_folder; bool m_allow_local_ip; peers_indexed m_peers_gray; peers_indexed m_peers_white; + Peerlist m_whitePeerlist; + Peerlist m_grayPeerlist; }; } - -BOOST_CLASS_VERSION(CryptoNote::peerlist_manager, 4) diff --git a/src/payment_service/NodeFactory.cpp b/src/PaymentGate/NodeFactory.cpp similarity index 51% rename from src/payment_service/NodeFactory.cpp rename to src/PaymentGate/NodeFactory.cpp index 7c468c7f..23c77c86 100644 --- a/src/payment_service/NodeFactory.cpp +++ b/src/PaymentGate/NodeFactory.cpp @@ -17,7 +17,7 @@ #include "NodeFactory.h" -#include "node_rpc_proxy/NodeRpcProxy.h" +#include "NodeRpcProxy/NodeRpcProxy.h" #include #include @@ -33,41 +33,53 @@ public: virtual bool shutdown() { return true; } virtual size_t getPeerCount() const { return 0; } - virtual uint64_t getLastLocalBlockHeight() const { return 0; } - virtual uint64_t getLastKnownBlockHeight() const { return 0; } - virtual uint64_t getLocalBlockCount() const override { return 0; } - virtual uint64_t getKnownBlockCount() const override { return 0; } - virtual uint64_t getLastLocalBlockTimestamp() const { return 0; } + virtual uint32_t getLastLocalBlockHeight() const override { return 0; } + virtual uint32_t getLastKnownBlockHeight() const override { return 0; } + virtual uint32_t getLocalBlockCount() const override { return 0; } + virtual uint32_t getKnownBlockCount() const override { return 0; } + virtual uint64_t getLastLocalBlockTimestamp() const override { return 0; } - virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) { callback(std::error_code()); } + virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) override { callback(std::error_code()); } virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, - std::vector& result, const Callback& callback) { } - virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { + std::vector& result, const Callback& callback) override { + } + virtual void getNewBlocks(std::vector&& knownBlockIds, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback) override { startHeight = 0; callback(std::error_code()); } - virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) { } + virtual void getTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) override { } - virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, - uint64_t& startHeight, const CryptoNote::INode::Callback& callback) { + virtual void queryBlocks(std::vector&& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, + uint32_t& startHeight, const Callback& callback) override { startHeight = 0; callback(std::error_code()); - } + }; - virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, - bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, - const Callback& callback) { - is_bc_actual = true; + virtual void getPoolSymmetricDifference(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, + std::vector>& newTxs, std::vector& deletedTxIds, const Callback& callback) { + isBcActual = true; callback(std::error_code()); } - virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, + virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) override { } - virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, + virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) override { } - virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, + virtual void getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps, + const Callback& callback) override { } + + virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, + const Callback& callback) override { } + + virtual void getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps, + const Callback& callback) override { } + + virtual void getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions, + const Callback& callback) override { } + + virtual void getMultisignatureOutputByGlobalIndex(uint64_t amount, uint32_t gindex, CryptoNote::MultisignatureOutput& out, const Callback& callback) override { } virtual void isSynchronized(bool& syncStatus, const Callback& callback) override { } @@ -77,16 +89,16 @@ public: class NodeInitObserver { public: - NodeInitObserver() {} + NodeInitObserver() { + initFuture = initPromise.get_future(); + } void initCompleted(std::error_code result) { initPromise.set_value(result); } void waitForInitEnd() { - auto future = initPromise.get_future(); - - std::error_code ec = future.get(); + std::error_code ec = initFuture.get(); if (ec) { throw std::system_error(ec); } @@ -95,6 +107,7 @@ public: private: std::promise initPromise; + std::future initFuture; }; NodeFactory::NodeFactory() { diff --git a/src/payment_service/NodeFactory.h b/src/PaymentGate/NodeFactory.h similarity index 100% rename from src/payment_service/NodeFactory.h rename to src/PaymentGate/NodeFactory.h diff --git a/src/payment_service/JsonRpcMessages.cpp b/src/PaymentGate/PaymentServiceJsonRpcMessages.cpp old mode 100644 new mode 100755 similarity index 79% rename from src/payment_service/JsonRpcMessages.cpp rename to src/PaymentGate/PaymentServiceJsonRpcMessages.cpp index ab4b6807..19084665 --- a/src/payment_service/JsonRpcMessages.cpp +++ b/src/PaymentGate/PaymentServiceJsonRpcMessages.cpp @@ -15,8 +15,8 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "JsonRpcMessages.h" -#include "serialization/SerializationOverloads.h" +#include "PaymentServiceJsonRpcMessages.h" +#include "Serialization/SerializationOverloads.h" namespace PaymentService { @@ -25,7 +25,7 @@ void TransferDestination::serialize(CryptoNote::ISerializer& serializer) { r &= serializer(address, "address"); if (!r) { - throw std::runtime_error("Required parameter is missing"); + throw RequestSerializationError(); } } @@ -37,7 +37,7 @@ void SendTransactionRequest::serialize(CryptoNote::ISerializer& serializer) { serializer(paymentId, "payment_id"); if (!r) { - throw std::runtime_error("Required parameter is missing"); + throw RequestSerializationError(); } } @@ -45,10 +45,45 @@ void SendTransactionResponse::serialize(CryptoNote::ISerializer& serializer) { serializer(transactionId, "transaction_id"); } +void GetAddressRequest::serialize(CryptoNote::ISerializer& serializer) { + serializer(index, "index"); +} + +void DeleteAddressRequest::serialize(CryptoNote::ISerializer& serializer) { + bool r = serializer(address, "address"); + + if (!r) { + throw RequestSerializationError(); + } +} + +void DeleteAddressResponse::serialize(CryptoNote::ISerializer& serializer) { +} + +void CreateAddressResponse::serialize(CryptoNote::ISerializer& serializer) { + serializer(address, "address"); +} + +void GetAddressCountResponse::serialize(CryptoNote::ISerializer& serializer) { + serializer(count, "count"); +} + void GetAddressResponse::serialize(CryptoNote::ISerializer& serializer) { serializer(address, "address"); } +void GetActualBalanceRequest::serialize(CryptoNote::ISerializer& serializer) { + bool r = serializer(address, "address"); + + if (!r) { + throw std::runtime_error("Required parameter is missing"); + } +} + +void GetPendingBalanceRequest::serialize(CryptoNote::ISerializer& serializer) { + serializer(address, "address"); +} + void GetActualBalanceResponse::serialize(CryptoNote::ISerializer& serializer) { serializer(actualBalance, "actual_balance"); } @@ -69,7 +104,7 @@ void GetTransactionIdByTransferIdRequest::serialize(CryptoNote::ISerializer& ser bool r = serializer(transferId, "transfer_id"); if (!r) { - throw std::runtime_error("Required parameter is missing"); + throw RequestSerializationError(); } } @@ -81,7 +116,7 @@ void GetTransactionRequest::serialize(CryptoNote::ISerializer& serializer) { bool r = serializer(transactionId, "transaction_id"); if (!r) { - throw std::runtime_error("Required parameter is missing"); + throw RequestSerializationError(); } } @@ -91,16 +126,16 @@ void TransactionRpcInfo::serialize(CryptoNote::ISerializer& serializer) { serializer(totalAmount, "total_amount"); serializer(fee, "fee"); serializer(hash, "hash"); - serializer(isCoinbase, "is_coin_base"); serializer(blockHeight, "block_height"); serializer(timestamp, "timestamp"); serializer(extra, "extra"); + serializer(transfers, "transfers"); } void GetTransactionResponse::serialize(CryptoNote::ISerializer& serializer) { serializer(found, "found"); - if (!found) { + if (found) { serializer(transactionInfo, "transaction_info"); } } @@ -127,7 +162,7 @@ void GetTransferRequest::serialize(CryptoNote::ISerializer& serializer) { bool r = serializer(transferId, "transfer_id"); if (!r) { - throw std::runtime_error("Required parameter is missing"); + throw RequestSerializationError(); } } @@ -142,7 +177,7 @@ void GetIncomingPaymentsRequest::serialize(CryptoNote::ISerializer& serializer) bool r = serializer(payments, "payments"); if (!r) { - throw std::runtime_error("Required parameter is missing"); + throw RequestSerializationError(); } } diff --git a/src/payment_service/JsonRpcMessages.h b/src/PaymentGate/PaymentServiceJsonRpcMessages.h similarity index 82% rename from src/payment_service/JsonRpcMessages.h rename to src/PaymentGate/PaymentServiceJsonRpcMessages.h index c3862b18..44aa9215 100644 --- a/src/payment_service/JsonRpcMessages.h +++ b/src/PaymentGate/PaymentServiceJsonRpcMessages.h @@ -17,9 +17,10 @@ #pragma once -#include "serialization/ISerializer.h" -#include #include +#include + +#include "Serialization/ISerializer.h" namespace PaymentService { @@ -37,6 +38,7 @@ struct TransferDestination { struct SendTransactionRequest { SendTransactionRequest() : unlockTime(0) {} + std::vector destinations; uint64_t fee; uint64_t mixin; @@ -51,17 +53,53 @@ struct SendTransactionResponse { void serialize(CryptoNote::ISerializer& serializer); }; +struct GetAddressRequest { + GetAddressRequest() : index(0) {} + + size_t index; + void serialize(CryptoNote::ISerializer& serializer); +}; + +struct GetAddressCountResponse { + std::size_t count; + void serialize(CryptoNote::ISerializer& serializer); +}; + +struct DeleteAddressRequest { + std::string address; + void serialize(CryptoNote::ISerializer& serializer); +}; + +struct DeleteAddressResponse { + void serialize(CryptoNote::ISerializer& serializer); +}; + +struct CreateAddressResponse { + std::string address; + void serialize(CryptoNote::ISerializer& serializer); +}; + struct GetAddressResponse { std::string address; void serialize(CryptoNote::ISerializer& serializer); }; +struct GetActualBalanceRequest { + std::string address; + void serialize(CryptoNote::ISerializer& serializer); +}; + struct GetActualBalanceResponse { uint64_t actualBalance; void serialize(CryptoNote::ISerializer& serializer); }; +struct GetPendingBalanceRequest { + std::string address; + void serialize(CryptoNote::ISerializer& serializer); +}; + struct GetPendingBalanceResponse { uint64_t pendingBalance; @@ -98,16 +136,23 @@ struct GetTransactionRequest { void serialize(CryptoNote::ISerializer& serializer); }; +struct TransferRpcInfo { + std::string address; + int64_t amount; + + void serialize(CryptoNote::ISerializer& serializer); +}; + struct TransactionRpcInfo { uint64_t firstTransferId; uint64_t transferCount; int64_t totalAmount; uint64_t fee; std::string hash; - bool isCoinbase; uint64_t blockHeight; uint64_t timestamp; std::string extra; + std::vector transfers; void serialize(CryptoNote::ISerializer& serializer); }; @@ -132,13 +177,6 @@ struct ListTransactionsResponse { void serialize(CryptoNote::ISerializer& serializer); }; -struct TransferRpcInfo { - std::string address; - int64_t amount; - - void serialize(CryptoNote::ISerializer& serializer); -}; - struct GetTransferRequest { uint64_t transferId; diff --git a/src/payment_service/JsonRpcServer.cpp b/src/PaymentGate/PaymentServiceJsonRpcServer.cpp old mode 100644 new mode 100755 similarity index 56% rename from src/payment_service/JsonRpcServer.cpp rename to src/PaymentGate/PaymentServiceJsonRpcServer.cpp index 83cc9deb..3e1eb888 --- a/src/payment_service/JsonRpcServer.cpp +++ b/src/PaymentGate/PaymentServiceJsonRpcServer.cpp @@ -15,86 +15,25 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "JsonRpcServer.h" +#include "PaymentServiceJsonRpcServer.h" -#include -#include -#include -#include -#include -#include "HTTP/HttpParserErrorCodes.h" - -#include -#include -#include -#include -#include -#include "HTTP/HttpParser.h" -#include "HTTP/HttpResponse.h" -#include "JsonRpcMessages.h" +#include "PaymentServiceJsonRpcMessages.h" #include "WalletService.h" -#include "WalletServiceErrorCodes.h" #include "Common/JsonValue.h" -#include "serialization/JsonInputValueSerializer.h" -#include "serialization/JsonOutputStreamSerializer.h" +#include "Serialization/JsonInputValueSerializer.h" +#include "Serialization/JsonOutputStreamSerializer.h" namespace PaymentService { -JsonRpcServer::JsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, WalletService& service, Logging::ILogger& loggerGroup) : - HttpServer(sys, loggerGroup), - system(sys), - stopEvent(stopEvent), - service(service), - logger(loggerGroup, "JsonRpcServer") +PaymentServiceJsonRpcServer::PaymentServiceJsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, WalletService& service, Logging::ILogger& loggerGroup) + : JsonRpcServer(sys, stopEvent, loggerGroup) + , service(service) + , logger(loggerGroup, "PaymentServiceJsonRpcServer") { } -void JsonRpcServer::start(const Configuration& config) { - HttpServer::start(config.bindAddress, config.bindPort); - stopEvent.wait(); - HttpServer::stop(); -} - -void JsonRpcServer::processRequest(const CryptoNote::HttpRequest& req, CryptoNote::HttpResponse& resp) { - try { - logger(Logging::TRACE) << "HTTP request came: \n" << req; - - if (req.getUrl() == "/json_rpc") { - std::stringstream jsonInputStream(req.getBody()); - Common::JsonValue jsonRpcRequest; - Common::JsonValue jsonRpcResponse(Common::JsonValue::OBJECT); - - try { - jsonInputStream >> jsonRpcRequest; - } catch (std::runtime_error&) { - logger(Logging::WARNING) << "Couldn't parse request: \"" << req.getBody() << "\""; - makeJsonParsingErrorResponse(jsonRpcResponse); - resp.setStatus(CryptoNote::HttpResponse::STATUS_200); - resp.setBody(jsonRpcResponse.toString()); - return; - } - - processJsonRpcRequest(jsonRpcRequest, jsonRpcResponse); - - std::stringstream jsonOutputStream; - jsonOutputStream << jsonRpcResponse; - - resp.setStatus(CryptoNote::HttpResponse::STATUS_200); - resp.setBody(jsonOutputStream.str()); - - } else { - logger(Logging::WARNING) << "Requested url \"" << req.getUrl() << "\" is not found"; - resp.setStatus(CryptoNote::HttpResponse::STATUS_404); - return; - } - } catch (std::exception& e) { - logger(Logging::WARNING) << "Error while processing http request: " << e.what(); - resp.setStatus(CryptoNote::HttpResponse::STATUS_500); - } -} - -void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common::JsonValue& resp) { +void PaymentServiceJsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common::JsonValue& resp) { try { prepareJsonResponse(req, resp); @@ -123,19 +62,82 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: serialize(sendResp, outputSerializer); } else if (method == "get_address") { + GetAddressRequest getAddrReq; GetAddressResponse getAddrResp; - std::error_code ec = service.getAddress(getAddrResp.address); + //XXX: refactor it when migrate to different exception types in different subsystems! + try { + serialize(getAddrReq, inputSerializer); + } catch (std::exception&) { + makeGenericErrorReponse(resp, "Invalid Request", -32600); + return; + } + + std::error_code ec = service.getAddress(getAddrReq.index, getAddrResp.address); if (ec) { makeErrorResponse(ec, resp); return; } serialize(getAddrResp, outputSerializer); + } else if (method == "create_address") { + CreateAddressResponse createAddrResp; + + std::error_code ec = service.createAddress(createAddrResp.address); + if (ec) { + makeErrorResponse(ec, resp); + return; + } + + serialize(createAddrResp, outputSerializer); + } else if (method == "get_address_count") { + GetAddressCountResponse addressCountResp; + + std::error_code ec = service.getAddressCount(addressCountResp.count); + if (ec) { + makeErrorResponse(ec, resp); + return; + } + + serialize(addressCountResp, outputSerializer); + } else if (method == "delete_address") { + DeleteAddressRequest delAddrReq; + DeleteAddressResponse delAddrResp; + + //XXX: refactor it when migrate to different exception types in different subsystems! + try { + serialize(delAddrReq, inputSerializer); + } catch (std::exception&) { + makeGenericErrorReponse(resp, "Invalid Request", -32600); + return; + } + + std::error_code ec = service.deleteAddress(delAddrReq.address); + if (ec) { + makeErrorResponse(ec, resp); + return; + } + + serialize(delAddrResp, outputSerializer); } else if (method == "get_actual_balance") { + GetActualBalanceRequest actualReq; GetActualBalanceResponse actualResp; - std::error_code ec = service.getActualBalance(actualResp.actualBalance); + //XXX: refactor it when migrate to different exception types in different subsystems! + try { + serialize(actualReq, inputSerializer); + } catch (std::exception&) { + makeGenericErrorReponse(resp, "Invalid Request", -32600); + return; + } + + std::error_code ec; + if (actualReq.address == "") { + ec = service.getActualBalance(actualResp.actualBalance); + } else { + ec = service.getActualBalance(actualReq.address, actualResp.actualBalance); + } + if (ec) { makeErrorResponse(ec, resp); return; @@ -143,9 +145,24 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: serialize(actualResp, outputSerializer); } else if (method == "get_pending_balance") { + GetPendingBalanceRequest pendingReq; GetPendingBalanceResponse pendingResp; - std::error_code ec = service.getPendingBalance(pendingResp.pendingBalance); + //XXX: refactor it when migrate to different exception types in different subsystems! + try { + serialize(pendingReq, inputSerializer); + } catch (std::exception&) { + makeGenericErrorReponse(resp, "Invalid Request", -32600); + return; + } + + std::error_code ec; + if (pendingReq.address == "") { + ec = service.getPendingBalance(pendingResp.pendingBalance); + } else { + ec = service.getPendingBalance(pendingReq.address, pendingResp.pendingBalance); + } + if (ec) { makeErrorResponse(ec, resp); return; @@ -184,7 +201,7 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: return; } - CryptoNote::TransactionId txId; + size_t txId; std::error_code ec = service.getTransactionByTransferId(getReq.transferId, txId); getResp.transactionid = txId; if (ec) { @@ -224,8 +241,7 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: return; } - std::error_code ec = service.listTransactions(static_cast(listReq.startingTransactionId), - listReq.maxTransactionCount, listResp.transactions); + std::error_code ec = service.listTransactions(static_cast(listReq.startingTransactionId), listReq.maxTransactionCount, listResp.transactions); if (ec) { makeErrorResponse(ec, resp); return; @@ -266,7 +282,7 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: WalletService::IncomingPayments payments; std::error_code ec = service.getIncomingPayments(getReq.payments, payments); if (ec) { - if (ec == make_error_code(PaymentService::error::REQUEST_ERROR)) { + if (ec == make_error_code(std::errc::argument_out_of_domain)) { makeGenericErrorReponse(resp, "Invalid Request", -32600); } else { makeErrorResponse(ec, resp); @@ -296,108 +312,9 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: logger(Logging::WARNING) << "Wrong request came"; makeGenericErrorReponse(resp, "Invalid Request", -32600); } catch (std::exception& e) { - logger(Logging::WARNING) << "Error occured while processing JsonRpc request"; + logger(Logging::WARNING) << "Error occurred while processing JsonRpc request: " << e.what(); makeGenericErrorReponse(resp, e.what()); } } -void JsonRpcServer::prepareJsonResponse(const Common::JsonValue& req, Common::JsonValue& resp) { - using Common::JsonValue; - - if (req.contains("id")) { - resp.insert("id", req("id")); - } - - resp.insert("jsonrpc", "2.0"); -} - -void JsonRpcServer::makeErrorResponse(const std::error_code& ec, Common::JsonValue& resp) { - using Common::JsonValue; - - JsonValue error(JsonValue::OBJECT); - - JsonValue code; - code = static_cast(-32000); //Application specific error code - - JsonValue message; - message = ec.message(); - - JsonValue data(JsonValue::OBJECT); - JsonValue appCode; - appCode = static_cast(ec.value()); - data.insert("application_code", appCode); - - error.insert("code", code); - error.insert("message", message); - error.insert("data", data); - - resp.insert("error", error); -} - -void JsonRpcServer::makeGenericErrorReponse(Common::JsonValue& resp, const char* what, int errorCode) { - using Common::JsonValue; - - JsonValue error(JsonValue::OBJECT); - - JsonValue code; - code = static_cast(errorCode); - - std::string msg; - if (what) { - msg = what; - } else { - msg = "Unknown application error"; - } - - JsonValue message; - message = msg; - - error.insert("code", code); - error.insert("message", message); - - resp.insert("error", error); - -} - -void JsonRpcServer::makeMethodNotFoundResponse(Common::JsonValue& resp) { - using Common::JsonValue; - - JsonValue error(JsonValue::OBJECT); - - JsonValue code; - code = static_cast(-32601); //ambigous declaration of JsonValue::operator= (between int and JsonValue) - - JsonValue message; - message = "Method not found"; - - error.insert("code", code); - error.insert("message", message); - - resp.insert("error", error); -} - -void JsonRpcServer::fillJsonResponse(const Common::JsonValue& v, Common::JsonValue& resp) { - resp.insert("result", v); -} - -void JsonRpcServer::makeJsonParsingErrorResponse(Common::JsonValue& resp) { - using Common::JsonValue; - - resp = JsonValue(JsonValue::OBJECT); - resp.insert("jsonrpc", "2.0"); - resp.insert("id", nullptr); - - JsonValue error(JsonValue::OBJECT); - JsonValue code; - code = static_cast(-32700); //ambigous declaration of JsonValue::operator= (between int and JsonValue) - - JsonValue message; - message = "Parse error"; - - error.insert("code", code); - error.insert("message", message); - - resp.insert("error", error); -} - } diff --git a/src/PaymentGate/PaymentServiceJsonRpcServer.h b/src/PaymentGate/PaymentServiceJsonRpcServer.h new file mode 100644 index 00000000..8e90b8d8 --- /dev/null +++ b/src/PaymentGate/PaymentServiceJsonRpcServer.h @@ -0,0 +1,39 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "JsonRpcServer/JsonRpcServer.h" + +namespace PaymentService { + +class WalletService; + +class PaymentServiceJsonRpcServer : public CryptoNote::JsonRpcServer { +public: + PaymentServiceJsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, WalletService& service, Logging::ILogger& loggerGroup); + PaymentServiceJsonRpcServer(const PaymentServiceJsonRpcServer&) = delete; + +protected: + virtual void processJsonRpcRequest(const Common::JsonValue& req, Common::JsonValue& resp) override; + +private: + WalletService& service; + Logging::LoggerRef logger; +}; + +} //namespace PaymentService diff --git a/src/payment_service/WalletFactory.cpp b/src/PaymentGate/WalletFactory.cpp old mode 100644 new mode 100755 similarity index 79% rename from src/payment_service/WalletFactory.cpp rename to src/PaymentGate/WalletFactory.cpp index 590960fa..288393e9 --- a/src/payment_service/WalletFactory.cpp +++ b/src/PaymentGate/WalletFactory.cpp @@ -17,9 +17,9 @@ #include "WalletFactory.h" -#include "node_rpc_proxy/NodeRpcProxy.h" -#include "wallet/Wallet.h" -#include "cryptonote_core/Currency.h" +#include "NodeRpcProxy/NodeRpcProxy.h" +#include "Wallet/WalletGreen.h" +#include "CryptoNoteCore/Currency.h" #include #include @@ -34,8 +34,8 @@ WalletFactory::WalletFactory() { WalletFactory::~WalletFactory() { } -CryptoNote::IWallet* WalletFactory::createWallet(const CryptoNote::Currency& currency, CryptoNote::INode& node) { - CryptoNote::Wallet* wallet = new CryptoNote::Wallet(currency, node); +CryptoNote::IWallet* WalletFactory::createWallet(const CryptoNote::Currency& currency, CryptoNote::INode& node, System::Dispatcher& dispatcher) { + CryptoNote::IWallet* wallet = new CryptoNote::WalletGreen(dispatcher, currency, node); return wallet; } diff --git a/src/payment_service/WalletFactory.h b/src/PaymentGate/WalletFactory.h similarity index 91% rename from src/payment_service/WalletFactory.h rename to src/PaymentGate/WalletFactory.h index 9df4b19e..1a37e30c 100644 --- a/src/payment_service/WalletFactory.h +++ b/src/PaymentGate/WalletFactory.h @@ -19,6 +19,7 @@ #include "IWallet.h" #include "INode.h" +#include #include #include @@ -31,7 +32,7 @@ namespace PaymentService { class WalletFactory { public: - static CryptoNote::IWallet* createWallet(const CryptoNote::Currency& currency, CryptoNote::INode& node); + static CryptoNote::IWallet* createWallet(const CryptoNote::Currency& currency, CryptoNote::INode& node, System::Dispatcher& dispatcher); private: WalletFactory(); ~WalletFactory(); diff --git a/src/payment_service/WalletService.cpp b/src/PaymentGate/WalletService.cpp old mode 100644 new mode 100755 similarity index 52% rename from src/payment_service/WalletService.cpp rename to src/PaymentGate/WalletService.cpp index 0d7b0de0..68dfaf88 --- a/src/payment_service/WalletService.cpp +++ b/src/PaymentGate/WalletService.cpp @@ -17,15 +17,6 @@ #include "WalletService.h" -#include "WalletServiceErrorCodes.h" -#include "JsonRpcMessages.h" -#include "WalletFactory.h" -#include "NodeFactory.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "crypto/crypto.h" -#include "wallet/LegacyKeysImporter.h" -#include "Common/util.h" #include #include @@ -42,6 +33,21 @@ #include #endif +#include +#include +#include "Common/Util.h" + +#include "crypto/crypto.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteCore/CryptoNoteBasicImpl.h" +#include "CryptoNoteCore/TransactionExtra.h" + +#include "PaymentServiceJsonRpcMessages.h" +#include "WalletFactory.h" +#include "NodeFactory.h" + +#include "Wallet/LegacyKeysImporter.h" + namespace { void addPaymentIdToExtra(const std::string& paymentId, std::string& extra) { @@ -116,7 +122,7 @@ bool deleteFile(const std::string& filename) { } void replaceWalletFiles(const std::string &path, const std::string &tempFilePath) { - tools::replace_file(tempFilePath, path); + Tools::replace_file(tempFilePath, path); } } @@ -137,12 +143,7 @@ void createWalletFile(std::fstream& walletFile, const std::string& filename) { } void saveWallet(CryptoNote::IWallet* wallet, std::fstream& walletFile, bool saveDetailed = true, bool saveCache = true) { - WalletSaveObserver saveObserver; - wallet->addObserver(&saveObserver); wallet->save(walletFile, saveDetailed, saveCache); - saveObserver.waitForSaveEnd(); - wallet->removeObserver(&saveObserver); - walletFile.flush(); } @@ -162,13 +163,13 @@ void secureSaveWallet(CryptoNote::IWallet* wallet, const std::string& path, bool replaceWalletFiles(path, tempFilePath); } -void generateNewWallet(CryptoNote::Currency ¤cy, const Configuration &conf, Logging::ILogger& logger) { +void generateNewWallet(const CryptoNote::Currency ¤cy, const WalletConfiguration &conf, Logging::ILogger& logger, System::Dispatcher& dispatcher) { Logging::LoggerRef log(logger, "generateNewWallet"); CryptoNote::INode* nodeStub = NodeFactory::createNodeStub(); std::unique_ptr nodeGuard(nodeStub); - CryptoNote::IWallet* wallet = WalletFactory::createWallet(currency, *nodeStub); + CryptoNote::IWallet* wallet = WalletFactory::createWallet(currency, *nodeStub, dispatcher); std::unique_ptr walletGuard(wallet); log(Logging::INFO) << "Generating new wallet"; @@ -176,24 +177,19 @@ void generateNewWallet(CryptoNote::Currency ¤cy, const Configuration &conf std::fstream walletFile; createWalletFile(walletFile, conf.walletFile); - WalletLoadObserver loadObserver; - wallet->addObserver(&loadObserver); + wallet->initialize(conf.walletPassword); + auto address = wallet->createAddress(); - wallet->initAndGenerate(conf.walletPassword); - - loadObserver.waitForLoadEnd(); - wallet->removeObserver(&loadObserver); - - log(Logging::INFO) << "New wallet is generated. Address: " << wallet->getAddress(); + log(Logging::INFO) << "New wallet is generated. Address: " << address; saveWallet(wallet, walletFile, false, false); log(Logging::INFO) << "Wallet is saved"; } -void importLegacyKeys(const Configuration& conf) { +void importLegacyKeys(const std::string &legacyKeysFile, const WalletConfiguration &conf) { std::stringstream archive; - CryptoNote::importLegacyKeys(conf.importKeys, conf.walletPassword, archive); + CryptoNote::importLegacyKeys(legacyKeysFile, conf.walletPassword, archive); std::fstream walletFile; createWalletFile(walletFile, conf.walletFile); @@ -204,22 +200,23 @@ void importLegacyKeys(const Configuration& conf) { } WalletService::WalletService(const CryptoNote::Currency& currency, System::Dispatcher& sys, CryptoNote::INode& node, - const Configuration& conf, Logging::ILogger& logger) : + const WalletConfiguration& conf, Logging::ILogger& logger) : config(conf), inited(false), - sendObserver(sys), - logger(logger, "WaleltService"), + logger(logger, "WalletService"), txIdIndex(boost::get<0>(paymentsCache)), - paymentIdIndex(boost::get<1>(paymentsCache)) + paymentIdIndex(boost::get<1>(paymentsCache)), + dispatcher(sys), + refreshContext(dispatcher) { - wallet.reset(WalletFactory::createWallet(currency, node)); + wallet.reset(WalletFactory::createWallet(currency, node, dispatcher)); } WalletService::~WalletService() { if (wallet) { if (inited) { - wallet->removeObserver(&sendObserver); - wallet->removeObserver(this); + wallet->stop(); + refreshContext.wait(); wallet->shutdown(); } } @@ -227,10 +224,8 @@ WalletService::~WalletService() { void WalletService::init() { loadWallet(); - loadPaymentsCache(); - - wallet->addObserver(&sendObserver); - wallet->addObserver(this); + loadPaymentsCacheAndTransferIndices(); + refreshContext.spawn([this] { refresh(); }); inited = true; } @@ -249,30 +244,23 @@ void WalletService::loadWallet() { logger(Logging::INFO) << "Loading wallet"; - WalletLoadObserver loadObserver; - wallet->addObserver(&loadObserver); + wallet->load(inputWalletFile, config.walletPassword); - wallet->initAndLoad(inputWalletFile, config.walletPassword); - - loadObserver.waitForLoadEnd(); - - wallet->removeObserver(&loadObserver); - - logger(Logging::INFO) << "Wallet loading is finished. Address: " << wallet->getAddress(); + logger(Logging::INFO) << "Wallet loading is finished."; } -void WalletService::loadPaymentsCache() { +void WalletService::loadPaymentsCacheAndTransferIndices() { size_t txCount = wallet->getTransactionCount(); + transfersIndices.resize(1); + transfersIndices[0] = 0; logger(Logging::DEBUGGING) << "seeking for payments among " << txCount << " transactions"; for (size_t id = 0; id < txCount; ++id) { - CryptoNote::TransactionInfo tx; - if (!wallet->getTransaction(id, tx)) { - logger(Logging::DEBUGGING) << "tx " << id << " doesn't exist"; - continue; - } + CryptoNote::WalletTransaction tx = wallet->getTransaction(id); + transfersIndices.push_back(transfersIndices[id] + wallet->getTransactionTransferCount(id)); + if (tx.totalAmount < 0) { logger(Logging::DEBUGGING) << "tx " << id << " has negative amount"; continue; @@ -280,14 +268,14 @@ void WalletService::loadPaymentsCache() { std::vector extraVector(tx.extra.begin(), tx.extra.end()); - crypto::hash paymentId; + Crypto::Hash paymentId; if (!CryptoNote::getPaymentIdFromTxExtra(extraVector, paymentId)) { logger(Logging::DEBUGGING) << "tx " << id << " has no payment id"; continue; } logger(Logging::DEBUGGING) << "transaction " << id << " has been inserted with payment id " << paymentId; - insertTransaction(id, paymentId); + insertTransaction(id, paymentId, tx.blockHeight != CryptoNote::WALLET_UNCONFIRMED_TRANSACTION_HEIGHT); } } @@ -296,7 +284,7 @@ std::error_code WalletService::sendTransaction(const SendTransactionRequest& req logger(Logging::DEBUGGING) << "Send transaction request came"; try { - std::vector transfers; + std::vector transfers; makeTransfers(req.destinations, transfers); std::string extra; @@ -304,19 +292,12 @@ std::error_code WalletService::sendTransaction(const SendTransactionRequest& req addPaymentIdToExtra(req.paymentId, extra); } - CryptoNote::TransactionId txId = wallet->sendTransaction(transfers, req.fee, extra, req.mixin, req.unlockTime); - if (txId == CryptoNote::INVALID_TRANSACTION_ID) { + size_t txId = wallet->transfer(transfers, req.fee, req.mixin, extra, req.unlockTime); + if (txId == CryptoNote::WALLET_INVALID_TRANSACTION_ID) { logger(Logging::WARNING) << "Unable to send transaction"; throw std::runtime_error("Error occured while sending transaction"); } - std::error_code ec; - sendObserver.waitForTransactionFinished(txId, ec); - - if (ec) { - return ec; - } - resp.transactionId = txId; } catch (std::system_error& x) { logger(Logging::WARNING) << "Error while sending transaction: " << x.what(); @@ -326,7 +307,7 @@ std::error_code WalletService::sendTransaction(const SendTransactionRequest& req return std::error_code(); } -void WalletService::makeTransfers(const std::vector& destinations, std::vector& transfers) { +void WalletService::makeTransfers(const std::vector& destinations, std::vector& transfers) { transfers.reserve(destinations.size()); for (auto dest: destinations) { @@ -334,11 +315,11 @@ void WalletService::makeTransfers(const std::vectorgetAddress(); + address = wallet->getAddress(index); } catch (std::system_error& x) { logger(Logging::WARNING) << "Error while getting address: " << x.what(); return x.code(); @@ -347,11 +328,69 @@ std::error_code WalletService::getAddress(std::string& address) { return std::error_code(); } +std::error_code WalletService::getAddressCount(std::size_t& count) { + logger(Logging::DEBUGGING) << "Get address count request came"; + count = wallet->getAddressCount(); + return std::error_code(); +} + +std::error_code WalletService::createAddress(std::string& address) { + logger(Logging::DEBUGGING) << "Create address request came"; + + try { + address = wallet->createAddress(); + } catch (std::system_error& x) { + logger(Logging::WARNING) << "Error while creating address: " << x.what(); + return x.code(); + } + + return std::error_code(); +} + +std::error_code WalletService::deleteAddress(const std::string& address) { + logger(Logging::DEBUGGING) << "Delete address request came"; + + try { + wallet->deleteAddress(address); + } catch (std::system_error& x) { + logger(Logging::WARNING) << "Error while deleting address: " << x.what(); + return x.code(); + } + + return std::error_code(); +} + +std::error_code WalletService::getActualBalance(const std::string& address, uint64_t& actualBalance) { + logger(Logging::DEBUGGING) << "Get actual balance for address: " << address << " request came"; + + try { + actualBalance = wallet->getActualBalance(address); + } catch (std::system_error& x) { + logger(Logging::WARNING) << "Unable to get actual balance: " << x.what(); + return x.code(); + } + + return std::error_code(); +} + +std::error_code WalletService::getPendingBalance(const std::string& address, uint64_t& pendingBalance) { + logger(Logging::DEBUGGING) << "Get pending balance for address: " << address <<" request came"; + + try { + pendingBalance = wallet->getPendingBalance(address); + } catch (std::system_error& x) { + logger(Logging::WARNING) << "Unable to get pending balance: " << x.what(); + return x.code(); + } + + return std::error_code(); +} + std::error_code WalletService::getActualBalance(uint64_t& actualBalance) { logger(Logging::DEBUGGING) << "Get actual balance request came"; try { - actualBalance = wallet->actualBalance(); + actualBalance = wallet->getActualBalance(); } catch (std::system_error& x) { logger(Logging::WARNING) << "Unable to get actual balance: " << x.what(); return x.code(); @@ -364,7 +403,7 @@ std::error_code WalletService::getPendingBalance(uint64_t& pendingBalance) { logger(Logging::DEBUGGING) << "Get pending balance request came"; try { - pendingBalance = wallet->pendingBalance(); + pendingBalance = wallet->getPendingBalance(); } catch (std::system_error& x) { logger(Logging::WARNING) << "Unable to get pending balance: " << x.what(); return x.code(); @@ -387,43 +426,55 @@ std::error_code WalletService::getTransactionsCount(uint64_t& txCount) { } std::error_code WalletService::getTransfersCount(uint64_t& trCount) { - logger(Logging::DEBUGGING) << "Get get transfers count request came"; - - try { - trCount = wallet->getTransferCount(); - } catch (std::system_error& x) { - logger(Logging::WARNING) << "Unable to get transfers count: " << x.what(); - return x.code(); - } - + logger(Logging::DEBUGGING) << "Get transfers count request came"; + trCount = static_cast(transfersIndices.back()); return std::error_code(); } -std::error_code WalletService::getTransactionByTransferId(CryptoNote::TransferId transfer, CryptoNote::TransactionId& transaction) { +std::error_code WalletService::getTransactionByTransferId(size_t transferId, size_t& transactionId) { logger(Logging::DEBUGGING) << "getTransactionByTransferId request came"; - try { - transaction = wallet->findTransactionByTransferId(transfer); - } catch (std::system_error& x) { - logger(Logging::WARNING) << "Unable to get transaction id by transfer id count: " << x.what(); - return x.code(); + if (transferId >= transfersIndices.back()) { + logger(Logging::WARNING) << "Transfer ID:" << transferId<<" is out of domain"; + return std::make_error_code(std::errc::argument_out_of_domain); } + auto nextTxId = std::lower_bound(transfersIndices.begin(), transfersIndices.end(), transferId); + transactionId = (nextTxId - transfersIndices.begin()) - 1; + return std::error_code(); } -std::error_code WalletService::getTransaction(CryptoNote::TransactionId txId, bool& found, TransactionRpcInfo& rpcInfo) { +void WalletService::fillTransactionRpcInfo(size_t txId, const CryptoNote::WalletTransaction& tx, TransactionRpcInfo& rpcInfo) { + rpcInfo.firstTransferId = transfersIndices[txId]; + rpcInfo.transferCount = wallet->getTransactionTransferCount(txId); + rpcInfo.totalAmount = tx.totalAmount; + rpcInfo.fee = tx.fee; + rpcInfo.blockHeight = tx.blockHeight; + rpcInfo.timestamp = tx.timestamp; + rpcInfo.extra = Common::toHex(tx.extra.data(), tx.extra.size()); + rpcInfo.hash = Common::podToHex(tx.hash); + for (size_t transferId = 0; transferId < rpcInfo.transferCount; ++transferId) { + auto transfer = wallet->getTransactionTransfer(txId, transferId); + TransferRpcInfo rpcTransfer{ transfer.address, transfer.amount }; + rpcInfo.transfers.push_back(rpcTransfer); + } +} + +std::error_code WalletService::getTransaction(size_t txId, bool& found, TransactionRpcInfo& rpcInfo) { logger(Logging::DEBUGGING) << "getTransaction request came"; + found = false; try { - CryptoNote::TransactionInfo txInfo; - - found = wallet->getTransaction(txId, txInfo); - if (!found) { - return std::error_code(); + auto tx = wallet->getTransaction(txId); + if (txId + 1 >= transfersIndices.size()) { + logger(Logging::WARNING) << "Unable to get transaction " << txId << ": argument out of domain."; + return std::make_error_code(std::errc::argument_out_of_domain); } - fillTransactionRpcInfo(txInfo, rpcInfo); + fillTransactionRpcInfo(txId, tx, rpcInfo); + + found = true; } catch (std::system_error& x) { logger(Logging::WARNING) << "Unable to get transaction: " << x.what(); return x.code(); @@ -432,19 +483,7 @@ std::error_code WalletService::getTransaction(CryptoNote::TransactionId txId, bo return std::error_code(); } -void WalletService::fillTransactionRpcInfo(const CryptoNote::TransactionInfo& txInfo, TransactionRpcInfo& rpcInfo) { - rpcInfo.firstTransferId = txInfo.firstTransferId; - rpcInfo.transferCount = txInfo.transferCount; - rpcInfo.totalAmount = txInfo.totalAmount; - rpcInfo.fee = txInfo.fee; - rpcInfo.isCoinbase = txInfo.isCoinbase; - rpcInfo.blockHeight = txInfo.blockHeight; - rpcInfo.timestamp = txInfo.timestamp; - rpcInfo.extra = Common::toHex(txInfo.extra.data(), txInfo.extra.size()); - rpcInfo.hash = Common::podToHex(txInfo.hash); -} - -std::error_code WalletService::listTransactions(CryptoNote::TransactionId startingTxId, uint32_t maxTxCount, std::vector& txsRpcInfo) { +std::error_code WalletService::listTransactions(size_t startingTxId, uint32_t maxTxCount, std::vector& txsRpcInfo) { logger(Logging::DEBUGGING) << "listTransactions request came"; if (maxTxCount == 0) { @@ -453,22 +492,20 @@ std::error_code WalletService::listTransactions(CryptoNote::TransactionId starti } try { - CryptoNote::TransactionId endTxId; - if (startingTxId > std::numeric_limits::max() - static_cast(maxTxCount)) { - endTxId = static_cast(wallet->getTransactionCount()); + size_t endTxId; + if (startingTxId > std::numeric_limits::max() - static_cast(maxTxCount)) { + endTxId = static_cast(wallet->getTransactionCount()); } else { - endTxId = startingTxId + static_cast(maxTxCount); - endTxId = std::min(endTxId, static_cast(wallet->getTransactionCount())); + endTxId = startingTxId + static_cast(maxTxCount); + endTxId = std::min(endTxId, static_cast(wallet->getTransactionCount())); } txsRpcInfo.resize(endTxId - startingTxId); for (auto txId = startingTxId; txId < endTxId; ++txId) { - CryptoNote::TransactionInfo txInfo; assert(txId < wallet->getTransactionCount()); - wallet->getTransaction(txId, txInfo); - - fillTransactionRpcInfo(txInfo, txsRpcInfo[txId - startingTxId]); + auto tx = wallet->getTransaction(txId); + fillTransactionRpcInfo(txId, tx, txsRpcInfo[txId - startingTxId]); } } catch (std::system_error& x) { logger(Logging::WARNING) << "Unable to list transaction: " << x.what(); @@ -478,18 +515,23 @@ std::error_code WalletService::listTransactions(CryptoNote::TransactionId starti return std::error_code(); } -std::error_code WalletService::getTransfer(CryptoNote::TransferId txId, bool& found, TransferRpcInfo& rpcInfo) { +std::error_code WalletService::getTransfer(size_t globalTransferId, bool& found, TransferRpcInfo& rpcInfo) { logger(Logging::DEBUGGING) << "getTransfer request came"; - + found = false; try { - CryptoNote::Transfer transfer; + size_t txId = (std::upper_bound(transfersIndices.begin(), transfersIndices.end(), globalTransferId) - transfersIndices.begin()) - 1; + size_t fakeTxId = transfersIndices.size() - 1; - found = wallet->getTransfer(txId, transfer); - if (!found) { + if (txId == fakeTxId) { return std::error_code(); } - fillTransferRpcInfo(transfer, rpcInfo); + auto transferId = globalTransferId - transfersIndices[txId]; + auto transfer = wallet->getTransactionTransfer(txId, transferId); + + rpcInfo.address = transfer.address; + rpcInfo.amount = transfer.amount; + found = true; } catch (std::system_error& x) { logger(Logging::WARNING) << "Unable to get transfer: " << x.what(); return x.code(); @@ -498,106 +540,81 @@ std::error_code WalletService::getTransfer(CryptoNote::TransferId txId, bool& fo return std::error_code(); } -void WalletService::fillTransferRpcInfo(const CryptoNote::Transfer& transfer, TransferRpcInfo& rpcInfo) { - rpcInfo.address = transfer.address; - rpcInfo.amount = transfer.amount; -} - std::error_code WalletService::getIncomingPayments(const std::vector& payments, IncomingPayments& result) { logger(Logging::DEBUGGING) << "getIncomingPayments request came"; - for (const std::string& payment: payments) { - if (!checkPaymentId(payment)) { - return make_error_code(error::REQUEST_ERROR); - } + try { + for (const std::string& payment: payments) { - std::string paymentString = payment; - std::transform(paymentString.begin(), paymentString.end(), paymentString.begin(), ::tolower); - - auto pair = paymentIdIndex.equal_range(paymentString); - - for (auto it = pair.first; it != pair.second; ++it) { - CryptoNote::TransactionInfo tx; - if (!wallet->getTransaction(it->transactionId, tx)) { - continue; + if (!checkPaymentId(payment)) { + return make_error_code(std::errc::argument_out_of_domain); } - std::string hashString = Common::podToHex(tx.hash); + std::string paymentString = payment; + std::transform(paymentString.begin(), paymentString.end(), paymentString.begin(), ::tolower); + auto pair = paymentIdIndex.equal_range(paymentString); - PaymentDetails details; - details.txHash = std::move(hashString); - details.amount = static_cast(tx.totalAmount); - details.blockHeight = tx.blockHeight; - details.unlockTime = 0; //TODO: this is stub. fix it when wallet api allows to retrieve it + for (auto it = pair.first; it != pair.second; ++it) { + auto tx = wallet->getTransaction(it->transactionId); - result[it->paymentId].push_back(std::move(details)); + std::string hashString = Common::podToHex(tx.hash); + + PaymentDetails details; + details.txHash = std::move(hashString); + details.amount = static_cast(tx.totalAmount); + details.blockHeight = tx.blockHeight; + details.unlockTime = tx.unlockTime; + + result[it->paymentId].push_back(std::move(details)); + } } + } catch (std::system_error& x) { + logger(Logging::WARNING) << "Unable to get payments: " << x.what(); + return x.code(); } return std::error_code(); } -void WalletService::externalTransactionCreated(CryptoNote::TransactionId transactionId) { - logger(Logging::DEBUGGING) << "external transaction created " << transactionId; - CryptoNote::TransactionInfo tx; - if (!wallet->getTransaction(transactionId, tx)) { - return; - } +void WalletService::refresh() { + try { + for (;;) { + auto event = wallet->getEvent(); + if (event.type == CryptoNote::TRANSACTION_CREATED || event.type == CryptoNote::TRANSACTION_UPDATED) { + size_t transactionId; + if (event.type == CryptoNote::TRANSACTION_CREATED) { + transactionId = event.transactionCreated.transactionIndex; + transfersIndices.push_back(transfersIndices[transactionId] + wallet->getTransactionTransferCount(transactionId)); + } else { + transactionId = event.transactionUpdated.transactionIndex; + } - if (tx.totalAmount < 0) { - return; - } + auto tx = wallet->getTransaction(transactionId); + logger(Logging::DEBUGGING) << "Transaction updated " << transactionId << " extra size: " << tx.extra.size(); + if (tx.totalAmount < 0) { + continue; + } - logger(Logging::DEBUGGING) << "external transaction created " << transactionId << " extra size: " << tx.extra.size(); - std::vector extraVector(tx.extra.begin(), tx.extra.end()); - crypto::hash paymentId; - if (!CryptoNote::getPaymentIdFromTxExtra(extraVector, paymentId)) { - logger(Logging::DEBUGGING) << "transaction " << transactionId << " has no payment id"; - return; - } + std::vector extraVector(tx.extra.begin(), tx.extra.end()); + Crypto::Hash paymentId; + if (!CryptoNote::getPaymentIdFromTxExtra(extraVector, paymentId)) { + logger(Logging::DEBUGGING) << "transaction " << transactionId << " has no payment id"; + continue; + } - insertTransaction(transactionId, paymentId); - - logger(Logging::DEBUGGING) << "transaction " << transactionId << " has been added to payments cache"; -} - -void WalletService::transactionUpdated(CryptoNote::TransactionId transactionId) { - CryptoNote::TransactionInfo tx; - if (!wallet->getTransaction(transactionId, tx)) { - return; - } - - if (tx.totalAmount < 0) { - return; - } - - if (tx.blockHeight != CryptoNote::UNCONFIRMED_TRANSACTION_HEIGHT) { - auto it = txIdIndex.find(transactionId); - if (it != txIdIndex.end()) { - return; - } - - //insert confirmed transaction - std::vector extraVector(tx.extra.begin(), tx.extra.end()); - crypto::hash paymentId; - if (!CryptoNote::getPaymentIdFromTxExtra(extraVector, paymentId)) { - logger(Logging::DEBUGGING) << "transaction " << transactionId << " has no payment id"; - return; - } - - insertTransaction(transactionId, paymentId); - logger(Logging::DEBUGGING) << "transaction " << transactionId << " has been inserted to payments cache"; - } else { - auto it = txIdIndex.find(transactionId); - if (it != txIdIndex.end()) { - txIdIndex.erase(it); - logger(Logging::DEBUGGING) << "transaction " << transactionId << " has been erased from payments cache"; + insertTransaction(transactionId, paymentId, tx.blockHeight != CryptoNote::WALLET_UNCONFIRMED_TRANSACTION_HEIGHT); + logger(Logging::DEBUGGING) << "transaction " << transactionId << " has been added to payments cache"; + } } + } catch (std::system_error& e) { + logger(Logging::TRACE) << "refresh is stopped: " << e.what(); + } catch (std::exception& e) { + logger(Logging::WARNING) << "exception thrown in refresh(): " << e.what(); } } -void WalletService::insertTransaction(CryptoNote::TransactionId id, const crypto::hash& paymentIdBin) { - paymentsCache.insert(PaymentItem{ Common::podToHex(paymentIdBin), id }); +void WalletService::insertTransaction(size_t id, const Crypto::Hash& paymentIdBin, bool confirmed) { + paymentsCache.insert(PaymentItem{ Common::podToHex(paymentIdBin), id, confirmed}); } } //namespace PaymentService diff --git a/src/payment_service/WalletService.h b/src/PaymentGate/WalletService.h old mode 100644 new mode 100755 similarity index 58% rename from src/payment_service/WalletService.h rename to src/PaymentGate/WalletService.h index d70175f6..6fe3791c --- a/src/payment_service/WalletService.h +++ b/src/PaymentGate/WalletService.h @@ -17,13 +17,13 @@ #pragma once +#include #include -#include "PaymentServiceConfiguration.h" +#include #include "IWallet.h" #include "INode.h" -#include "WalletObservers.h" -#include "cryptonote_core/Currency.h" -#include "JsonRpcMessages.h" +#include "CryptoNoteCore/Currency.h" +#include "PaymentServiceJsonRpcMessages.h" #undef ERROR //TODO: workaround for windows build. fix it #include "Logging/LoggerRef.h" @@ -41,14 +41,18 @@ struct TransferDestination; struct TransactionRpcInfo; struct TransferRpcInfo; -void importLegacyKeys(const Configuration& conf); -void generateNewWallet (CryptoNote::Currency ¤cy, const Configuration &conf, Logging::ILogger &logger); +struct WalletConfiguration { + std::string walletFile; + std::string walletPassword; +}; -class WalletService : public CryptoNote::IWalletObserver { +void generateNewWallet(const CryptoNote::Currency ¤cy, const WalletConfiguration &conf, Logging::ILogger &logger, System::Dispatcher& dispatcher); + +class WalletService { public: typedef std::map > IncomingPayments; - explicit WalletService(const CryptoNote::Currency& currency, System::Dispatcher& sys, CryptoNote::INode& node, const Configuration& conf, Logging::ILogger& logger); + explicit WalletService(const CryptoNote::Currency& currency, System::Dispatcher& sys, CryptoNote::INode& node, const WalletConfiguration& conf, Logging::ILogger& logger); virtual ~WalletService(); void init(); @@ -56,34 +60,38 @@ public: std::error_code sendTransaction(const SendTransactionRequest& req, SendTransactionResponse& resp); std::error_code getIncomingPayments(const std::vector& payments, IncomingPayments& result); - std::error_code getAddress(std::string& address); + std::error_code getAddress(size_t index, std::string& address); + std::error_code getAddressCount(size_t& count); + std::error_code createAddress(std::string& address); + std::error_code deleteAddress(const std::string& address); + std::error_code getActualBalance(const std::string& address, uint64_t& actualBalance); + std::error_code getPendingBalance(const std::string& address, uint64_t& pendingBalance); std::error_code getActualBalance(uint64_t& actualBalance); std::error_code getPendingBalance(uint64_t& pendingBalance); std::error_code getTransactionsCount(uint64_t& txCount); std::error_code getTransfersCount(uint64_t& trCount); - std::error_code getTransactionByTransferId(CryptoNote::TransferId transfer, CryptoNote::TransactionId& transaction); - std::error_code getTransaction(CryptoNote::TransactionId txId, bool& found, TransactionRpcInfo& rpcInfo); - std::error_code listTransactions(CryptoNote::TransactionId startingTxId, uint32_t maxTxCount, std::vector& txsRpcInfo); - std::error_code getTransfer(CryptoNote::TransferId txId, bool& found, TransferRpcInfo& rpcInfo); + std::error_code getTransactionByTransferId(size_t transfer, size_t& transaction); + std::error_code getTransaction(size_t txId, bool& found, TransactionRpcInfo& rpcInfo); + std::error_code listTransactions(size_t startingTxId, uint32_t maxTxCount, std::vector& txsRpcInfo); + std::error_code getTransfer(size_t txId, bool& found, TransferRpcInfo& rpcInfo); private: + void refresh(); + void loadWallet(); - void loadPaymentsCache(); - void insertTransaction(CryptoNote::TransactionId id, const crypto::hash& paymentIdBin); + void loadPaymentsCacheAndTransferIndices(); + void insertTransaction(size_t id, const Crypto::Hash& paymentIdBin, bool confirmed); - void makeTransfers(const std::vector& destinations, std::vector& transfers); - void fillTransactionRpcInfo(const CryptoNote::TransactionInfo& txInfo, TransactionRpcInfo& rpcInfo); - void fillTransferRpcInfo(const CryptoNote::Transfer& transfer, TransferRpcInfo& rpcInfo); - - virtual void externalTransactionCreated(CryptoNote::TransactionId transactionId); - virtual void transactionUpdated(CryptoNote::TransactionId transactionId); + void fillTransactionRpcInfo(size_t txId, const CryptoNote::WalletTransaction& tx, TransactionRpcInfo& rpcInfo); + void makeTransfers(const std::vector& destinations, std::vector& transfers); struct PaymentItem { std::string paymentId; - CryptoNote::TransactionId transactionId; + size_t transactionId; + bool confirmed; }; - typedef boost::multi_index::hashed_unique TxIdIndex; + typedef boost::multi_index::hashed_unique TxIdIndex; typedef boost::multi_index::hashed_non_unique PaymentIndex; typedef boost::multi_index::multi_index_container< PaymentItem, @@ -93,12 +101,14 @@ private: > > PaymentsContainer; - std::unique_ptr wallet; + std::unique_ptr wallet; CryptoNote::INode* node; - const Configuration& config; + const WalletConfiguration& config; bool inited; - WalletTransactionSendObserver sendObserver; Logging::LoggerRef logger; + std::vector transfersIndices; + System::Dispatcher& dispatcher; + System::ContextGroup refreshContext; PaymentsContainer paymentsCache; PaymentsContainer::nth_index<0>::type& txIdIndex; diff --git a/src/payment_service/ConfigurationManager.cpp b/src/PaymentGateService/ConfigurationManager.cpp old mode 100644 new mode 100755 similarity index 96% rename from src/payment_service/ConfigurationManager.cpp rename to src/PaymentGateService/ConfigurationManager.cpp index 70b169b7..4f092d5a --- a/src/payment_service/ConfigurationManager.cpp +++ b/src/PaymentGateService/ConfigurationManager.cpp @@ -20,8 +20,8 @@ #include #include -#include "Common/command_line.h" -#include "Common/util.h" +#include "Common/CommandLine.h" +#include "Common/Util.h" namespace PaymentService { @@ -47,8 +47,8 @@ bool ConfigurationManager::init(int argc, char** argv) { ("local", "start with local node (remote is default)") ("testnet", "testnet mode"); - command_line::add_arg(cmdGeneralOptions, command_line::arg_data_dir, tools::get_default_data_dir()); - command_line::add_arg(confGeneralOptions, command_line::arg_data_dir, tools::get_default_data_dir()); + command_line::add_arg(cmdGeneralOptions, command_line::arg_data_dir, Tools::getDefaultDataDirectory()); + command_line::add_arg(confGeneralOptions, command_line::arg_data_dir, Tools::getDefaultDataDirectory()); Configuration::initOptions(cmdGeneralOptions); Configuration::initOptions(confGeneralOptions); diff --git a/src/payment_service/ConfigurationManager.h b/src/PaymentGateService/ConfigurationManager.h old mode 100644 new mode 100755 similarity index 94% rename from src/payment_service/ConfigurationManager.h rename to src/PaymentGateService/ConfigurationManager.h index 5b3b2c24..d33bf136 --- a/src/payment_service/ConfigurationManager.h +++ b/src/PaymentGateService/ConfigurationManager.h @@ -17,9 +17,9 @@ #pragma once -#include "cryptonote_core/CoreConfig.h" +#include "CryptoNoteCore/CoreConfig.h" #include "PaymentServiceConfiguration.h" -#include "p2p/NetNodeConfig.h" +#include "P2p/NetNodeConfig.h" #include "RpcNodeConfiguration.h" namespace PaymentService { diff --git a/src/PaymentGateService/PaymentGateService.cpp b/src/PaymentGateService/PaymentGateService.cpp new file mode 100755 index 00000000..734bcef7 --- /dev/null +++ b/src/PaymentGateService/PaymentGateService.cpp @@ -0,0 +1,242 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "PaymentGateService.h" + +#include + +#include "Common/SignalHandler.h" +#include "InProcessNode/InProcessNode.h" +#include "Logging/LoggerRef.h" +#include "PaymentGate/PaymentServiceJsonRpcServer.h" + +#include "CryptoNoteCore/CoreConfig.h" +#include "CryptoNoteCore/Core.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolHandler.h" +#include "P2p/NetNode.h" +#include + +#ifdef ERROR +#undef ERROR +#endif + +#ifdef _WIN32 +#include +#else +#include +#endif + +using namespace PaymentService; + +void changeDirectory(const std::string& path) { + if (chdir(path.c_str())) { + throw std::runtime_error("Couldn't change directory to \'" + path + "\': " + strerror(errno)); + } +} + +void stopSignalHandler(PaymentGateService* pg) { + pg->stop(); +} + +bool PaymentGateService::init(int argc, char** argv) { + if (!config.init(argc, argv)) { + return false; + } + + logger.setMaxLevel(static_cast(config.gateConfiguration.logLevel)); + logger.addLogger(consoleLogger); + + Logging::LoggerRef log(logger, "main"); + + if (config.gateConfiguration.testnet) { + log(Logging::INFO) << "Starting in testnet mode"; + currencyBuilder.testnet(true); + } + + if (!config.gateConfiguration.serverRoot.empty()) { + changeDirectory(config.gateConfiguration.serverRoot); + log(Logging::INFO) << "Current working directory now is " << config.gateConfiguration.serverRoot; + } + + fileStream.open(config.gateConfiguration.logFile, std::ofstream::app); + + if (!fileStream) { + throw std::runtime_error("Couldn't open log file"); + } + + fileLogger.attachToStream(fileStream); + logger.addLogger(fileLogger); + + return true; +} + +WalletConfiguration PaymentGateService::getWalletConfig() const { + return WalletConfiguration{ + config.gateConfiguration.walletFile, + config.gateConfiguration.walletPassword + }; +} + +const CryptoNote::Currency PaymentGateService::getCurrency() { + return currencyBuilder.currency(); +} + +void PaymentGateService::run() { + + System::Dispatcher localDispatcher; + System::Event localStopEvent(localDispatcher); + + this->dispatcher = &localDispatcher; + this->stopEvent = &localStopEvent; + + Tools::SignalHandler::install(std::bind(&stopSignalHandler, this)); + + Logging::LoggerRef log(logger, "run"); + + if (config.startInprocess) { + runInProcess(log); + } else { + runRpcProxy(log); + } + + this->dispatcher = nullptr; + this->stopEvent = nullptr; +} + +void PaymentGateService::stop() { + Logging::LoggerRef log(logger, "stop"); + + log(Logging::INFO) << "Stop signal caught"; + + if (dispatcher != nullptr) { + dispatcher->remoteSpawn([&]() { + if (stopEvent != nullptr) { + stopEvent->set(); + } + }); + } +} + +void PaymentGateService::runInProcess(Logging::LoggerRef& log) { + log(Logging::INFO) << "Starting Payment Gate with local node"; + + CryptoNote::Currency currency = currencyBuilder.currency(); + CryptoNote::core core(currency, NULL, logger); + + CryptoNote::CryptoNoteProtocolHandler protocol(currency, *dispatcher, core, NULL, logger); + CryptoNote::NodeServer p2pNode(*dispatcher, protocol, logger); + + protocol.set_p2p_endpoint(&p2pNode); + core.set_cryptonote_protocol(&protocol); + + log(Logging::INFO) << "initializing p2pNode"; + if (!p2pNode.init(config.netNodeConfig)) { + throw std::runtime_error("Failed to init p2pNode"); + } + + log(Logging::INFO) << "initializing core"; + CryptoNote::MinerConfig emptyMiner; + core.init(config.coreConfig, emptyMiner, true); + + std::promise initPromise; + auto initFuture = initPromise.get_future(); + + std::unique_ptr node(new CryptoNote::InProcessNode(core, protocol)); + + node->init([&initPromise, &log](std::error_code ec) { + if (ec) { + log(Logging::INFO) << "Failed to init node: " << ec.message(); + } else { + log(Logging::INFO) << "node is inited successfully"; + } + + initPromise.set_value(ec); + }); + + auto ec = initFuture.get(); + if (ec) { + throw std::system_error(ec); + } + + log(Logging::INFO) << "Spawning p2p server"; + + System::Event p2pStarted(*dispatcher); + + System::Context<> context(*dispatcher, [&]() { + p2pStarted.set(); + p2pNode.run(); + }); + + p2pStarted.wait(); + + runWalletService(currency, *node); + + p2pNode.sendStopSignal(); + context.get(); + node->shutdown(); + core.deinit(); + p2pNode.deinit(); +} + +void PaymentGateService::runRpcProxy(Logging::LoggerRef& log) { + log(Logging::INFO) << "Starting Payment Gate with remote node"; + CryptoNote::Currency currency = currencyBuilder.currency(); + + std::unique_ptr node( + PaymentService::NodeFactory::createNode( + config.remoteNodeConfig.daemonHost, + config.remoteNodeConfig.daemonPort)); + + runWalletService(currency, *node); +} + +void PaymentGateService::runWalletService(const CryptoNote::Currency& currency, CryptoNote::INode& node) { + PaymentService::WalletConfiguration walletConfiguration{ + config.gateConfiguration.walletFile, + config.gateConfiguration.walletPassword + }; + + service = new PaymentService::WalletService(currency, *dispatcher, node, walletConfiguration, logger); + std::unique_ptr serviceGuard(service); + try { + service->init(); + } catch (std::exception& e) { + Logging::LoggerRef(logger, "run")(Logging::ERROR) << "Failed to init walletService reason: " << e.what(); + return; + } + + if (config.gateConfiguration.printAddresses) { + // print addresses and exit + size_t addressCount = 0; + service->getAddressCount(addressCount); + for (size_t i = 0; i < addressCount; ++i) { + std::string address; + if (service->getAddress(i, address) == std::error_code()) { + std::cout << "Address: " << address << std::endl; + } + } + } else { + PaymentService::PaymentServiceJsonRpcServer rpcServer(*dispatcher, *stopEvent, *service, logger); + rpcServer.start(config.gateConfiguration.bindAddress, config.gateConfiguration.bindPort); + + try { + service->saveWallet(); + } catch (std::exception& ex) { + Logging::LoggerRef(logger, "saveWallet")(Logging::WARNING) << "Couldn't save wallet: " << ex.what(); + } + } +} diff --git a/src/PaymentGateService/PaymentGateService.h b/src/PaymentGateService/PaymentGateService.h new file mode 100644 index 00000000..f594f7db --- /dev/null +++ b/src/PaymentGateService/PaymentGateService.h @@ -0,0 +1,64 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "ConfigurationManager.h" +#include "PaymentServiceConfiguration.h" + +#include "Logging/ConsoleLogger.h" +#include "Logging/LoggerGroup.h" +#include "Logging/StreamLogger.h" + +#include "PaymentGate/NodeFactory.h" +#include "PaymentGate/WalletService.h" + +class PaymentGateService { +public: + + PaymentGateService() : dispatcher(nullptr), stopEvent(nullptr), config(), service(nullptr), logger(), currencyBuilder(logger) { + } + + bool init(int argc, char** argv); + + const PaymentService::ConfigurationManager& getConfig() const { return config; } + PaymentService::WalletConfiguration getWalletConfig() const; + const CryptoNote::Currency getCurrency(); + + void run(); + void stop(); + + Logging::ILogger& getLogger() { return logger; } + +private: + + void runInProcess(Logging::LoggerRef& log); + void runRpcProxy(Logging::LoggerRef& log); + + void runWalletService(const CryptoNote::Currency& currency, CryptoNote::INode& node); + + System::Dispatcher* dispatcher; + System::Event* stopEvent; + PaymentService::ConfigurationManager config; + PaymentService::WalletService* service; + CryptoNote::CurrencyBuilder currencyBuilder; + + Logging::LoggerGroup logger; + std::ofstream fileStream; + Logging::StreamLogger fileLogger; + Logging::ConsoleLogger consoleLogger; +}; diff --git a/src/payment_service/PaymentServiceConfiguration.cpp b/src/PaymentGateService/PaymentServiceConfiguration.cpp similarity index 89% rename from src/payment_service/PaymentServiceConfiguration.cpp rename to src/PaymentGateService/PaymentServiceConfiguration.cpp index f749750b..d9401fc8 100644 --- a/src/payment_service/PaymentServiceConfiguration.cpp +++ b/src/PaymentGateService/PaymentServiceConfiguration.cpp @@ -34,6 +34,7 @@ Configuration::Configuration() { unregisterService = false; logFile = "payment_gate.log"; testnet = false; + printAddresses = false; logLevel = Logging::INFO; } @@ -45,12 +46,14 @@ void Configuration::initOptions(boost::program_options::options_description& des ("wallet-password,p", po::value(), "wallet password") ("generate-wallet,g", "generate new wallet file and exit") ("daemon,d", "run as daemon in Unix or as service in Windows") +#ifdef _WIN32 ("register-service", "register service and exit (Windows only)") ("unregister-service", "unregister service and exit (Windows only)") - ("import-keys,i", po::value(), "import legacy keys file and exit") +#endif ("log-file,l", po::value(), "log file") ("server-root", po::value(), "server root. The service will use it as working directory. Don't set it if don't want to change it") - ("log-level", po::value(), "log level"); + ("log-level", po::value(), "log level") + ("address", "print wallet addresses and exit"); } void Configuration::init(const boost::program_options::variables_map& options) { @@ -79,7 +82,7 @@ void Configuration::init(const boost::program_options::variables_map& options) { } if (options.count("log-level")) { - logLevel = options["log-level"].as(); + logLevel = options["log-level"].as(); if (logLevel > Logging::TRACE) { std::string error = "log-level option must be in " + std::to_string(Logging::FATAL) + ".." + std::to_string(Logging::TRACE) + " interval"; throw ConfigurationError(error.c_str()); @@ -110,12 +113,8 @@ void Configuration::init(const boost::program_options::variables_map& options) { generateNewWallet = true; } - if (options.count("import-keys")) { - importKeys = options["import-keys"].as(); - } - - if (!importKeys.empty() && generateNewWallet) { - throw ConfigurationError("It's impossible to use both \"import\" and \"generate-wallet\" at the same time"); + if (options.count("address")) { + printAddresses = true; } if (!registerService && !unregisterService) { diff --git a/src/payment_service/PaymentServiceConfiguration.h b/src/PaymentGateService/PaymentServiceConfiguration.h similarity index 96% rename from src/payment_service/PaymentServiceConfiguration.h rename to src/PaymentGateService/PaymentServiceConfiguration.h index 42316518..dc804fba 100644 --- a/src/payment_service/PaymentServiceConfiguration.h +++ b/src/PaymentGateService/PaymentServiceConfiguration.h @@ -41,7 +41,6 @@ struct Configuration { std::string walletFile; std::string walletPassword; - std::string importKeys; std::string logFile; std::string serverRoot; @@ -50,8 +49,9 @@ struct Configuration { bool registerService; bool unregisterService; bool testnet; + bool printAddresses; - std::size_t logLevel; + size_t logLevel; }; } //namespace PaymentService diff --git a/src/payment_service/RpcNodeConfiguration.cpp b/src/PaymentGateService/RpcNodeConfiguration.cpp similarity index 100% rename from src/payment_service/RpcNodeConfiguration.cpp rename to src/PaymentGateService/RpcNodeConfiguration.cpp diff --git a/src/payment_service/RpcNodeConfiguration.h b/src/PaymentGateService/RpcNodeConfiguration.h similarity index 100% rename from src/payment_service/RpcNodeConfiguration.h rename to src/PaymentGateService/RpcNodeConfiguration.h diff --git a/src/PaymentGateService/main.cpp b/src/PaymentGateService/main.cpp new file mode 100644 index 00000000..b8078363 --- /dev/null +++ b/src/PaymentGateService/main.cpp @@ -0,0 +1,335 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include +#include + +#include + +#include "PaymentGateService.h" +#include "version.h" + +#ifdef WIN32 +#include +#include +#else +#include +#include +#include +#include +#include +#include +#endif + +#define SERVICE_NAME "Payment Gate" + +PaymentGateService* ppg; + +#ifdef WIN32 +SERVICE_STATUS_HANDLE serviceStatusHandle; + +std::string GetLastErrorMessage(DWORD errorMessageID) +{ + LPSTR messageBuffer = nullptr; + size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorMessageID, 0, (LPSTR)&messageBuffer, 0, NULL); + + std::string message(messageBuffer, size); + + LocalFree(messageBuffer); + + return message; +} + +void __stdcall serviceHandler(DWORD fdwControl) { + if (fdwControl == SERVICE_CONTROL_STOP) { + Logging::LoggerRef log(ppg->getLogger(), "serviceHandler"); + log(Logging::INFO) << "Stop signal caught"; + + SERVICE_STATUS serviceStatus{ SERVICE_WIN32_OWN_PROCESS, SERVICE_STOP_PENDING, 0, NO_ERROR, 0, 0, 0 }; + SetServiceStatus(serviceStatusHandle, &serviceStatus); + + ppg->stop(); + } +} + +void __stdcall serviceMain(DWORD dwArgc, char **lpszArgv) { + Logging::LoggerRef logRef(ppg->getLogger(), "WindowsService"); + + serviceStatusHandle = RegisterServiceCtrlHandler("PaymentGate", serviceHandler); + if (serviceStatusHandle == NULL) { + logRef(Logging::FATAL) << "Couldn't make RegisterServiceCtrlHandler call: " << GetLastErrorMessage(GetLastError()); + return; + } + + SERVICE_STATUS serviceStatus{ SERVICE_WIN32_OWN_PROCESS, SERVICE_START_PENDING, 0, NO_ERROR, 0, 1, 3000 }; + if (SetServiceStatus(serviceStatusHandle, &serviceStatus) != TRUE) { + logRef(Logging::FATAL) << "Couldn't make SetServiceStatus call: " << GetLastErrorMessage(GetLastError()); + return; + } + + serviceStatus = { SERVICE_WIN32_OWN_PROCESS, SERVICE_RUNNING, SERVICE_ACCEPT_STOP, NO_ERROR, 0, 0, 0 }; + if (SetServiceStatus(serviceStatusHandle, &serviceStatus) != TRUE) { + logRef(Logging::FATAL) << "Couldn't make SetServiceStatus call: " << GetLastErrorMessage(GetLastError()); + return; + } + + try { + ppg->run(); + } catch (std::exception& ex) { + logRef(Logging::FATAL) << "Error occured: " << ex.what(); + } + + serviceStatus = { SERVICE_WIN32_OWN_PROCESS, SERVICE_STOPPED, 0, NO_ERROR, 0, 0, 0 }; + SetServiceStatus(serviceStatusHandle, &serviceStatus); +} +#else +int daemonize() { + pid_t pid; + pid = fork(); + + if (pid < 0) + return pid; + + if (pid > 0) + return pid; + + if (setsid() < 0) + return -1; + + signal(SIGCHLD, SIG_IGN); + signal(SIGHUP, SIG_IGN); + signal(SIGPIPE, SIG_IGN); + + pid = fork(); + + if (pid < 0) + return pid; + + if (pid > 0) + return pid; + + umask(0); + + return 0; +} +#endif + +int runDaemon() { +#ifdef WIN32 + + SERVICE_TABLE_ENTRY serviceTable[] { + { "Payment Gate", serviceMain }, + { NULL, NULL } + }; + + if (StartServiceCtrlDispatcher(serviceTable) != TRUE) { + return 1; + } + + return 0; + +#else + + int daemonResult = daemonize(); + if (daemonResult > 0) { + //parent + return 0; + } else if (daemonResult < 0) { + //error occured + return 1; + } + + ppg->run(); + + return 0; + +#endif +} + +int registerService() { +#ifdef WIN32 + Logging::LoggerRef logRef(ppg->getLogger(), "ServiceRegistrator"); + + char pathBuff[MAX_PATH]; + std::string modulePath; + SC_HANDLE scManager = NULL; + SC_HANDLE scService = NULL; + int ret = 0; + + for (;;) { + if (GetModuleFileName(NULL, pathBuff, ARRAYSIZE(pathBuff)) == 0) { + logRef(Logging::FATAL) << "GetModuleFileName failed with error: " << GetLastErrorMessage(GetLastError()); + ret = 1; + break; + } + + modulePath.assign(pathBuff); + + std::string moduleDir = modulePath.substr(0, modulePath.find_last_of('\\') + 1); + modulePath += " --config=" + moduleDir + "payment_service.conf -d"; + + scManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE); + if (scManager == NULL) { + logRef(Logging::FATAL) << "OpenSCManager failed with error: " << GetLastErrorMessage(GetLastError()); + ret = 1; + break; + } + + scService = CreateService(scManager, SERVICE_NAME, NULL, SERVICE_QUERY_STATUS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, modulePath.c_str(), NULL, NULL, NULL, NULL, NULL); + + if (scService == NULL) { + logRef(Logging::FATAL) << "CreateService failed with error: " << GetLastErrorMessage(GetLastError()); + ret = 1; + break; + } + + logRef(Logging::INFO) << "Service is registered successfully"; + logRef(Logging::INFO) << "Please make sure " << moduleDir + "payment_service.conf" << " exists"; + break; + } + + if (scManager) { + CloseServiceHandle(scManager); + } + + if (scService) { + CloseServiceHandle(scService); + } + + return ret; +#else + return 0; +#endif +} + +int unregisterService() { +#ifdef WIN32 + Logging::LoggerRef logRef(ppg->getLogger(), "ServiceDeregistrator"); + + SC_HANDLE scManager = NULL; + SC_HANDLE scService = NULL; + SERVICE_STATUS ssSvcStatus = { }; + int ret = 0; + + for (;;) { + scManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); + if (scManager == NULL) { + logRef(Logging::FATAL) << "OpenSCManager failed with error: " << GetLastErrorMessage(GetLastError()); + ret = 1; + break; + } + + scService = OpenService(scManager, SERVICE_NAME, SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE); + if (scService == NULL) { + logRef(Logging::FATAL) << "OpenService failed with error: " << GetLastErrorMessage(GetLastError()); + ret = 1; + break; + } + + if (ControlService(scService, SERVICE_CONTROL_STOP, &ssSvcStatus)) { + logRef(Logging::INFO) << "Stopping " << SERVICE_NAME; + Sleep(1000); + + while (QueryServiceStatus(scService, &ssSvcStatus)) { + if (ssSvcStatus.dwCurrentState == SERVICE_STOP_PENDING) { + logRef(Logging::INFO) << "Waiting..."; + Sleep(1000); + } else { + break; + } + } + + std::cout << std::endl; + if (ssSvcStatus.dwCurrentState == SERVICE_STOPPED) { + logRef(Logging::INFO) << SERVICE_NAME << " is stopped"; + } else { + logRef(Logging::FATAL) << SERVICE_NAME << " failed to stop" << std::endl; + } + } + + if (!DeleteService(scService)) { + logRef(Logging::FATAL) << "DeleteService failed with error: " << GetLastErrorMessage(GetLastError()); + ret = 1; + break; + } + + logRef(Logging::INFO) << SERVICE_NAME << " is removed"; + break; + } + + if (scManager) { + CloseServiceHandle(scManager); + } + + if (scService) { + CloseServiceHandle(scService); + } + + return ret; +#else + return 0; +#endif +} + +int main(int argc, char** argv) { + PaymentGateService pg; + ppg = &pg; + + try { + if (!pg.init(argc, argv)) { + return 0; //help message requested or so + } + + Logging::LoggerRef(pg.getLogger(), "main")(Logging::INFO) << "PaymentService " << " v" << PROJECT_VERSION_LONG; + + const auto& config = pg.getConfig(); + + if (config.gateConfiguration.generateNewWallet) { + System::Dispatcher d; + generateNewWallet(pg.getCurrency(), pg.getWalletConfig(), pg.getLogger(), d); + return 0; + } + + if (config.gateConfiguration.registerService) { + return registerService(); + } + + if (config.gateConfiguration.unregisterService) { + return unregisterService(); + } + + if (config.gateConfiguration.daemonize) { + if (runDaemon() != 0) { + throw std::runtime_error("Failed to start daemon"); + } + } else { + pg.run(); + } + + } catch (PaymentService::ConfigurationError& ex) { + std::cerr << "Configuration error: " << ex.what() << std::endl; + return 1; + } catch (std::exception& ex) { + std::cerr << "Fatal error: " << ex.what() << std::endl; + return 1; + } + + return 0; +} diff --git a/src/Platform/Linux/System/Dispatcher.cpp b/src/Platform/Linux/System/Dispatcher.cpp index e130bfb8..28f5f0de 100755 --- a/src/Platform/Linux/System/Dispatcher.cpp +++ b/src/Platform/Linux/System/Dispatcher.cpp @@ -21,38 +21,81 @@ #include #include #include +#include +#include #include #include -#include namespace System { +namespace { + +struct ContextMakingData { + Dispatcher* dispatcher; + void* ucontext; +}; + +class MutextGuard { +public: + MutextGuard(pthread_mutex_t& _mutex) : mutex(_mutex) { + auto ret = pthread_mutex_lock(&mutex); + if (ret != 0) { + throw std::runtime_error("failed to acquire mutex, errno=" + std::to_string(ret) + ": " + strerror(ret)); + } + } + + ~MutextGuard() { + pthread_mutex_unlock(&mutex); + } + +private: + pthread_mutex_t& mutex; +}; + +static_assert(Dispatcher::SIZEOF_PTHREAD_MUTEX_T == sizeof(pthread_mutex_t), "invalid pthread mutex size"); + +const size_t STACK_SIZE = 64 * 1024; + +}; + Dispatcher::Dispatcher() { std::string message; epoll = ::epoll_create1(0); if (epoll == -1) { message = "epoll_create1() fail errno=" + std::to_string(errno); } else { - currentContext = new ucontext_t; - if (getcontext(reinterpret_cast(currentContext)) == -1) { + mainContext.ucontext = new ucontext_t; + if (getcontext(reinterpret_cast(mainContext.ucontext)) == -1) { message = "getcontext() fail errno=" + std::to_string(errno); } else { remoteSpawnEvent = eventfd(0, O_NONBLOCK); if(remoteSpawnEvent == -1) { message = "eventfd() fail errno=" + std::to_string(errno); } else { - eventContext.writeContext = nullptr; - eventContext.readContext = nullptr; + remoteSpawnEventContext.writeContext = nullptr; + remoteSpawnEventContext.readContext = nullptr; epoll_event remoteSpawnEventEpollEvent; remoteSpawnEventEpollEvent.events = EPOLLIN; - remoteSpawnEventEpollEvent.data.ptr = &eventContext; + remoteSpawnEventEpollEvent.data.ptr = &remoteSpawnEventContext; if (epoll_ctl(epoll, EPOLL_CTL_ADD, remoteSpawnEvent, &remoteSpawnEventEpollEvent) == -1) { message = "epoll_ctl() failed, errno=" + std::to_string(errno); } else { - contextCount = 0; *reinterpret_cast(this->mutex) = pthread_mutex_t(PTHREAD_MUTEX_INITIALIZER); + + mainContext.interrupted = false; + mainContext.group = &contextGroup; + mainContext.groupPrev = nullptr; + mainContext.groupNext = nullptr; + contextGroup.firstContext = nullptr; + contextGroup.lastContext = nullptr; + contextGroup.firstWaiter = nullptr; + contextGroup.lastWaiter = nullptr; + currentContext = &mainContext; + firstResumingContext = nullptr; + firstReusableContext = nullptr; + runningContextCount = 0; return; } @@ -69,15 +112,21 @@ Dispatcher::Dispatcher() { } Dispatcher::~Dispatcher() { - assert(resumingContexts.empty()); - assert(reusableContexts.size() == contextCount); - assert(spawningProcedures.empty()); - assert(reusableContexts.size() == allocatedStacks.size()); - while (!reusableContexts.empty()) { - delete[] allocatedStacks.top(); - allocatedStacks.pop(); - delete static_cast(reusableContexts.top()); - reusableContexts.pop(); + for (NativeContext* context = contextGroup.firstContext; context != nullptr; context = context->groupNext) { + interrupt(context); + } + + yield(); + assert(contextGroup.firstContext == nullptr); + assert(contextGroup.firstWaiter == nullptr); + assert(firstResumingContext == nullptr); + assert(runningContextCount == 0); + while (firstReusableContext != nullptr) { + auto ucontext = static_cast(firstReusableContext->ucontext); + auto stackPtr = static_cast(firstReusableContext->stackPtr); + firstReusableContext = firstReusableContext->next; + delete[] stackPtr; + delete ucontext; } while (!timers.empty()) { @@ -95,12 +144,12 @@ Dispatcher::~Dispatcher() { } void Dispatcher::clear() { - while (!reusableContexts.empty()) { - delete[] allocatedStacks.top(); - allocatedStacks.pop(); - delete static_cast(reusableContexts.top()); - reusableContexts.pop(); - --contextCount; + while (firstReusableContext != nullptr) { + auto ucontext = static_cast(firstReusableContext->ucontext); + auto stackPtr = static_cast(firstReusableContext->stackPtr); + firstReusableContext = firstReusableContext->next; + delete[] stackPtr; + delete ucontext; } while (!timers.empty()) { @@ -114,11 +163,11 @@ void Dispatcher::clear() { } void Dispatcher::dispatch() { - void* context; + NativeContext* context; for (;;) { - if (!resumingContexts.empty()) { - context = resumingContexts.front(); - resumingContexts.pop(); + if (firstResumingContext != nullptr) { + context = firstResumingContext; + firstResumingContext = context->next; break; } @@ -133,13 +182,12 @@ void Dispatcher::dispatch() { throw std::runtime_error("Dispatcher::dispatch() read(remoteSpawnEvent) fail errno=" + std::to_string(errno)); } - pthread_mutex_lock(reinterpret_cast(this->mutex)); + MutextGuard guard(*reinterpret_cast(this->mutex)); while (!remoteSpawningProcedures.empty()) { spawn(std::move(remoteSpawningProcedures.front())); remoteSpawningProcedures.pop(); } - pthread_mutex_unlock(reinterpret_cast(this->mutex)); continue; } @@ -163,26 +211,61 @@ void Dispatcher::dispatch() { } if (context != currentContext) { - ucontext_t* oldContext = static_cast(currentContext); + ucontext_t* oldContext = static_cast(currentContext->ucontext); currentContext = context; - if (swapcontext(oldContext, static_cast(context)) == -1) { + if (swapcontext(oldContext, static_cast(context->ucontext)) == -1) { throw std::runtime_error("Dispatcher::dispatch() swapcontext() failed, errno=" + std::to_string(errno)); } } } -void* Dispatcher::getCurrentContext() const { +NativeContext* Dispatcher::getCurrentContext() const { return currentContext; } -void Dispatcher::pushContext(void* context) { - resumingContexts.push(context); +void Dispatcher::interrupt() { + interrupt(currentContext); +} + +void Dispatcher::interrupt(NativeContext* context) { + assert(context!=nullptr); + if (!context->interrupted) { + if (context->interruptProcedure != nullptr) { + context->interruptProcedure(); + context->interruptProcedure = nullptr; + } else { + context->interrupted = true; + } + } +} + +bool Dispatcher::interrupted() { + if (currentContext->interrupted) { + currentContext->interrupted = false; + return true; + } + + return false; +} + +void Dispatcher::pushContext(NativeContext* context) { + assert(context != nullptr); + context->next = nullptr; + if(firstResumingContext != nullptr) { + assert(lastResumingContext != nullptr); + lastResumingContext->next = context; + } else { + firstResumingContext = context; + } + + lastResumingContext = context; } void Dispatcher::remoteSpawn(std::function&& procedure) { - pthread_mutex_lock(reinterpret_cast(this->mutex)); - remoteSpawningProcedures.push(std::move(procedure)); - pthread_mutex_unlock(reinterpret_cast(this->mutex)); + { + MutextGuard guard(*reinterpret_cast(this->mutex)); + remoteSpawningProcedures.push(std::move(procedure)); + } uint64_t one = 1; auto transferred = write(remoteSpawnEvent, &one, sizeof one); if(transferred == - 1) { @@ -191,25 +274,23 @@ void Dispatcher::remoteSpawn(std::function&& procedure) { } void Dispatcher::spawn(std::function&& procedure) { - ucontext_t *context; - if (reusableContexts.empty()) { - context = new ucontext_t; - if (getcontext(context) == -1) { //makecontext precondition - throw std::runtime_error("Dispatcher::spawn(), getcontext() fail errno=" + std::to_string(errno)); - } - auto stackPointer = new uint8_t[64 * 1024]; - context->uc_stack.ss_sp = stackPointer; - allocatedStacks.push(stackPointer); - context->uc_stack.ss_size = 64 * 1024; - makecontext(context, (void(*)())contextProcedureStatic, 1, reinterpret_cast(this)); - ++contextCount; + NativeContext* context = &getReusableContext(); + if(contextGroup.firstContext != nullptr) { + context->groupPrev = contextGroup.lastContext; + assert(contextGroup.lastContext->groupNext == nullptr); + contextGroup.lastContext->groupNext = context; } else { - context = static_cast(reusableContexts.top()); - reusableContexts.pop(); + context->groupPrev = nullptr; + contextGroup.firstContext = context; + contextGroup.firstWaiter = nullptr; } - resumingContexts.push(context); - spawningProcedures.emplace(std::move(procedure)); + context->interrupted = false; + context->group = &contextGroup; + context->groupNext = nullptr; + context->procedure = std::move(procedure); + contextGroup.lastContext = context; + pushContext(context); } void Dispatcher::yield() { @@ -230,21 +311,22 @@ void Dispatcher::yield() { throw std::runtime_error("Dispatcher::dispatch() read(remoteSpawnEvent) fail errno=" + std::to_string(errno)); } - pthread_mutex_lock(reinterpret_cast(this->mutex)); + MutextGuard guard(*reinterpret_cast(this->mutex)); while (!remoteSpawningProcedures.empty()) { spawn(std::move(remoteSpawningProcedures.front())); remoteSpawningProcedures.pop(); } - pthread_mutex_unlock(reinterpret_cast(this->mutex)); continue; } if ((events[i].events & EPOLLOUT) != 0) { - resumingContexts.push(contextPair->writeContext->context); + contextPair->writeContext->context->interruptProcedure = nullptr; + pushContext(contextPair->writeContext->context); contextPair->writeContext->events = events[i].events; } else if ((events[i].events & EPOLLIN) != 0) { - resumingContexts.push(contextPair->readContext->context); + contextPair->readContext->context->interruptProcedure = nullptr; + pushContext(contextPair->readContext->context); contextPair->readContext->events = events[i].events; } else { continue; @@ -257,8 +339,8 @@ void Dispatcher::yield() { } } - if(!resumingContexts.empty()){ - resumingContexts.push(getCurrentContext()); + if (firstResumingContext != nullptr) { + pushContext(currentContext); dispatch(); } } @@ -267,6 +349,41 @@ int Dispatcher::getEpoll() const { return epoll; } +NativeContext& Dispatcher::getReusableContext() { + if(firstReusableContext == nullptr) { + ucontext_t* newlyCreatedContext = new ucontext_t; + if (getcontext(newlyCreatedContext) == -1) { //makecontext precondition + throw std::runtime_error("Dispatcher::getReusableContext(), getcontext() fail errno=" + std::to_string(errno)); + } + + auto stackPointer = new uint8_t[STACK_SIZE]; + newlyCreatedContext->uc_stack.ss_sp = stackPointer; + newlyCreatedContext->uc_stack.ss_size = STACK_SIZE; + + ContextMakingData makingContextData {this, newlyCreatedContext}; + makecontext(newlyCreatedContext, (void(*)())contextProcedureStatic, 1, reinterpret_cast(&makingContextData)); + + ucontext_t* oldContext = static_cast(currentContext->ucontext); + if (swapcontext(oldContext, newlyCreatedContext) == -1) { + throw std::runtime_error("Dispatcher::getReusableContext() swapcontext() failed, errno=" + std::to_string(errno)); + } + + assert(firstReusableContext != nullptr); + assert(firstReusableContext->ucontext == newlyCreatedContext); + firstReusableContext->stackPtr = stackPointer; + }; + + NativeContext* context = firstReusableContext; + firstReusableContext = firstReusableContext-> next; + return *context; +} + +void Dispatcher::pushReusableContext(NativeContext& context) { + context.next = firstReusableContext; + firstReusableContext = &context; + --runningContextCount; +} + int Dispatcher::getTimer() { int timer; if (timers.empty()) { @@ -290,20 +407,68 @@ void Dispatcher::pushTimer(int timer) { timers.push(timer); } -void Dispatcher::contextProcedure() { - void* context = currentContext; +void Dispatcher::contextProcedure(void* ucontext) { + assert(firstReusableContext == nullptr); + NativeContext context; + context.ucontext = ucontext; + context.interrupted = false; + context.next = nullptr; + firstReusableContext = &context; + ucontext_t* oldContext = static_cast(context.ucontext); + if (swapcontext(oldContext, static_cast(currentContext->ucontext)) == -1) { + throw std::runtime_error("Dispatcher::contextProcedure() swapcontext() failed, errno=" + std::to_string(errno)); + } + for (;;) { - assert(!spawningProcedures.empty()); - std::function procedure = std::move(spawningProcedures.front()); - spawningProcedures.pop(); - procedure(); - reusableContexts.push(context); + ++runningContextCount; + try { + context.procedure(); + } catch(std::exception&) { + } + + if (context.group != nullptr) { + if (context.groupPrev != nullptr) { + assert(context.groupPrev->groupNext == &context); + context.groupPrev->groupNext = context.groupNext; + if (context.groupNext != nullptr) { + assert(context.groupNext->groupPrev == &context); + context.groupNext->groupPrev = context.groupPrev; + } else { + assert(context.group->lastContext == &context); + context.group->lastContext = context.groupPrev; + } + } else { + assert(context.group->firstContext == &context); + context.group->firstContext = context.groupNext; + if (context.groupNext != nullptr) { + assert(context.groupNext->groupPrev == &context); + context.groupNext->groupPrev = nullptr; + } else { + assert(context.group->lastContext == &context); + if (context.group->firstWaiter != nullptr) { + if (firstResumingContext != nullptr) { + assert(lastResumingContext->next == nullptr); + lastResumingContext->next = context.group->firstWaiter; + } else { + firstResumingContext = context.group->firstWaiter; + } + + lastResumingContext = context.group->lastWaiter; + context.group->firstWaiter = nullptr; + } + } + } + + pushReusableContext(context); + } + dispatch(); } -} +}; void Dispatcher::contextProcedureStatic(void *context) { - reinterpret_cast(context)->contextProcedure(); + ContextMakingData* makingContextData = reinterpret_cast(context); + makingContextData->dispatcher->contextProcedure(makingContextData->ucontext); } } diff --git a/src/Platform/Linux/System/Dispatcher.h b/src/Platform/Linux/System/Dispatcher.h index c7574f11..ce6f0a08 100755 --- a/src/Platform/Linux/System/Dispatcher.h +++ b/src/Platform/Linux/System/Dispatcher.h @@ -17,12 +17,45 @@ #pragma once +#include #include #include #include namespace System { +struct NativeContextGroup; + +struct NativeContext { + void* ucontext; + void* stackPtr; + bool interrupted; + NativeContext* next; + NativeContextGroup* group; + NativeContext* groupPrev; + NativeContext* groupNext; + std::function procedure; + std::function interruptProcedure; +}; + +struct NativeContextGroup { + NativeContext* firstContext; + NativeContext* lastContext; + NativeContext* firstWaiter; + NativeContext* lastWaiter; +}; + +struct OperationContext { + NativeContext *context; + bool interrupted; + uint32_t events; +}; + +struct ContextPair { + OperationContext *readContext; + OperationContext *writeContext; +}; + class Dispatcher { public: Dispatcher(); @@ -31,25 +64,18 @@ public: Dispatcher& operator=(const Dispatcher&) = delete; void clear(); void dispatch(); - void* getCurrentContext() const; - void pushContext(void* context); + NativeContext* getCurrentContext() const; + void interrupt(); + void interrupt(NativeContext* context); + bool interrupted(); + void pushContext(NativeContext* context); void remoteSpawn(std::function&& procedure); - void spawn(std::function&& procedure); void yield(); - struct OperationContext { - void *context; - bool interrupted; - uint32_t events; - }; - - struct ContextPair { - OperationContext *readContext; - OperationContext *writeContext; - }; - // system-dependent int getEpoll() const; + NativeContext& getReusableContext(); + void pushReusableContext(NativeContext&); int getTimer(); void pushTimer(int timer); @@ -64,20 +90,23 @@ public: #endif private: - std::stack allocatedStacks; - std::size_t contextCount; - void* currentContext; + void spawn(std::function&& procedure); int epoll; - ContextPair eventContext; - uint8_t mutex[SIZEOF_PTHREAD_MUTEX_T]; + alignas(void*) uint8_t mutex[SIZEOF_PTHREAD_MUTEX_T]; int remoteSpawnEvent; + ContextPair remoteSpawnEventContext; std::queue> remoteSpawningProcedures; - std::queue resumingContexts; - std::stack reusableContexts; - std::queue> spawningProcedures; std::stack timers; - void contextProcedure(); + NativeContext mainContext; + NativeContextGroup contextGroup; + NativeContext* currentContext; + NativeContext* firstResumingContext; + NativeContext* lastResumingContext; + NativeContext* firstReusableContext; + size_t runningContextCount; + + void contextProcedure(void* ucontext); static void contextProcedureStatic(void* context); }; diff --git a/src/Platform/Linux/System/Ipv4Resolver.cpp b/src/Platform/Linux/System/Ipv4Resolver.cpp index d78195e4..b9f1e435 100755 --- a/src/Platform/Linux/System/Ipv4Resolver.cpp +++ b/src/Platform/Linux/System/Ipv4Resolver.cpp @@ -22,6 +22,7 @@ #include +#include #include #include @@ -30,12 +31,11 @@ namespace System { Ipv4Resolver::Ipv4Resolver() : dispatcher(nullptr) { } -Ipv4Resolver::Ipv4Resolver(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false) { +Ipv4Resolver::Ipv4Resolver(Dispatcher& dispatcher) : dispatcher(&dispatcher) { } Ipv4Resolver::Ipv4Resolver(Ipv4Resolver&& other) : dispatcher(other.dispatcher) { if (dispatcher != nullptr) { - stopped = other.stopped; other.dispatcher = nullptr; } } @@ -46,28 +46,15 @@ Ipv4Resolver::~Ipv4Resolver() { Ipv4Resolver& Ipv4Resolver::operator=(Ipv4Resolver&& other) { dispatcher = other.dispatcher; if (dispatcher != nullptr) { - stopped = other.stopped; other.dispatcher = nullptr; } return *this; } -void Ipv4Resolver::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void Ipv4Resolver::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - stopped = true; -} - Ipv4Address Ipv4Resolver::resolve(const std::string& host) { assert(dispatcher != nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } diff --git a/src/Platform/Linux/System/Ipv4Resolver.h b/src/Platform/Linux/System/Ipv4Resolver.h index d59d50e0..02dd9793 100755 --- a/src/Platform/Linux/System/Ipv4Resolver.h +++ b/src/Platform/Linux/System/Ipv4Resolver.h @@ -33,13 +33,10 @@ public: ~Ipv4Resolver(); Ipv4Resolver& operator=(const Ipv4Resolver&) = delete; Ipv4Resolver& operator=(Ipv4Resolver&& other); - void start(); - void stop(); Ipv4Address resolve(const std::string& host); private: Dispatcher* dispatcher; - bool stopped; }; } diff --git a/src/Platform/Linux/System/TcpConnection.cpp b/src/Platform/Linux/System/TcpConnection.cpp index ecb1ab51..cce62459 100755 --- a/src/Platform/Linux/System/TcpConnection.cpp +++ b/src/Platform/Linux/System/TcpConnection.cpp @@ -16,11 +16,11 @@ // along with Bytecoin. If not, see . #include "TcpConnection.h" -#include #include -#include +#include #include +#include #include #include @@ -35,7 +35,6 @@ TcpConnection::TcpConnection(TcpConnection&& other) : dispatcher(other.dispatche assert(other.contextPair.writeContext == nullptr); assert(other.contextPair.readContext == nullptr); connection = other.connection; - stopped = other.stopped; contextPair = other.contextPair; other.dispatcher = nullptr; } @@ -64,7 +63,6 @@ TcpConnection& TcpConnection::operator=(TcpConnection&& other) { assert(other.contextPair.readContext == nullptr); assert(other.contextPair.writeContext == nullptr); connection = other.connection; - stopped = other.stopped; contextPair = other.contextPair; other.dispatcher = nullptr; } @@ -72,41 +70,10 @@ TcpConnection& TcpConnection::operator=(TcpConnection&& other) { return *this; } -void TcpConnection::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void TcpConnection::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - - epoll_event connectionEvent; - connectionEvent.events = 0; - connectionEvent.data.ptr = nullptr; - - if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { - throw std::runtime_error("TcpConnection::stop, epoll_ctl() fail" + std::to_string(errno)); - } - - if(contextPair.readContext != nullptr) { - contextPair.readContext->interrupted = true; - dispatcher->pushContext(contextPair.readContext->context); - } - - if(contextPair.writeContext != nullptr) { - contextPair.writeContext->interrupted = true; - dispatcher->pushContext(contextPair.writeContext->context); - } - - stopped = true; -} - size_t TcpConnection::read(uint8_t* data, size_t size) { assert(dispatcher != nullptr); assert(contextPair.readContext == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -117,7 +84,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) { message = "recv failed, errno=" + std::to_string(errno); } else { epoll_event connectionEvent; - Dispatcher::OperationContext operationContext; + OperationContext operationContext; operationContext.interrupted = false; operationContext.context = dispatcher->getCurrentContext(); contextPair.readContext = &operationContext; @@ -132,7 +99,23 @@ size_t TcpConnection::read(uint8_t* data, size_t size) { if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { message = "epoll_ctl() failed, errno=" + std::to_string(errno); } else { + dispatcher->getCurrentContext()->interruptProcedure = [&]() { + assert(dispatcher != nullptr); + assert(contextPair.readContext != nullptr); + epoll_event connectionEvent; + connectionEvent.events = 0; + connectionEvent.data.ptr = nullptr; + + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { + throw std::runtime_error("TcpConnection::stop, epoll_ctl() fail, errno=" + std::to_string(errno)); + } + + contextPair.readContext->interrupted = true; + dispatcher->pushContext(contextPair.readContext->context); + }; + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; assert(dispatcher != nullptr); assert(operationContext.context == dispatcher->getCurrentContext()); assert(contextPair.readContext == &operationContext); @@ -178,7 +161,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) { std::size_t TcpConnection::write(const uint8_t* data, size_t size) { assert(dispatcher != nullptr); assert(contextPair.writeContext == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -197,7 +180,7 @@ std::size_t TcpConnection::write(const uint8_t* data, size_t size) { message = "send failed, result=" + std::to_string(errno); } else { epoll_event connectionEvent; - Dispatcher::OperationContext operationContext; + OperationContext operationContext; operationContext.interrupted = false; operationContext.context = dispatcher->getCurrentContext(); contextPair.writeContext = &operationContext; @@ -212,7 +195,23 @@ std::size_t TcpConnection::write(const uint8_t* data, size_t size) { if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { message = "epoll_ctl() failed, errno=" + std::to_string(errno); } else { + dispatcher->getCurrentContext()->interruptProcedure = [&]() { + assert(dispatcher != nullptr); + assert(contextPair.writeContext != nullptr); + epoll_event connectionEvent; + connectionEvent.events = 0; + connectionEvent.data.ptr = nullptr; + + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { + throw std::runtime_error("TcpConnection::stop, epoll_ctl() fail" + std::to_string(errno)); + } + + contextPair.writeContext->interrupted = true; + dispatcher->pushContext(contextPair.writeContext->context); + }; + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; assert(dispatcher != nullptr); assert(operationContext.context == dispatcher->getCurrentContext()); assert(contextPair.writeContext == &operationContext); @@ -230,12 +229,12 @@ std::size_t TcpConnection::write(const uint8_t* data, size_t size) { if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { message = "epoll_ctl() failed, errno=" + std::to_string(errno); - throw std::runtime_error("TcpConnection::write"); + throw std::runtime_error("TcpConnection::write, " + message); } } if((operationContext.events & (EPOLLERR | EPOLLHUP)) != 0) { - throw std::runtime_error("TcpConnection::write"); + throw std::runtime_error("TcpConnection::write: events & (EPOLLERR | EPOLLHUP) != 0"); } ssize_t transferred = ::send(connection, (void *)data, size, 0); @@ -248,25 +247,25 @@ std::size_t TcpConnection::write(const uint8_t* data, size_t size) { } } - throw std::runtime_error("TcpConnection::write, "+message); + throw std::runtime_error("TcpConnection::write, " + message); } assert(transferred <= static_cast(size)); return transferred; } -std::pair TcpConnection::getPeerAddressAndPort() { +std::pair TcpConnection::getPeerAddressAndPort() const { sockaddr_in addr; socklen_t size = sizeof(addr); if (getpeername(connection, reinterpret_cast(&addr), &size) != 0) { - throw std::runtime_error("TcpConnection::getPeerAddress, getpeername failed, result=" + std::to_string(errno)); + throw std::runtime_error("TcpConnection::getPeerAddress, getpeername failed, errno=" + std::to_string(errno)); } assert(size == sizeof(sockaddr_in)); return std::make_pair(Ipv4Address(htonl(addr.sin_addr.s_addr)), htons(addr.sin_port)); } -TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), stopped(false) { +TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket) { contextPair.readContext = nullptr; contextPair.writeContext = nullptr; epoll_event connectionEvent; @@ -274,7 +273,7 @@ TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&d connectionEvent.data.ptr = nullptr; if (epoll_ctl(dispatcher.getEpoll(), EPOLL_CTL_ADD, socket, &connectionEvent) == -1) { - throw std::runtime_error("TcpConnection::TcpConnection, epoll_ctl() fail" + std::to_string(errno)); + throw std::runtime_error("TcpConnection::TcpConnection, epoll_ctl() fail, errno=" + std::to_string(errno)); } } diff --git a/src/Platform/Linux/System/TcpConnection.h b/src/Platform/Linux/System/TcpConnection.h index 034d6ccc..a84b4757 100755 --- a/src/Platform/Linux/System/TcpConnection.h +++ b/src/Platform/Linux/System/TcpConnection.h @@ -34,11 +34,9 @@ public: ~TcpConnection(); TcpConnection& operator=(const TcpConnection&) = delete; TcpConnection& operator=(TcpConnection&& other); - void start(); - void stop(); std::size_t read(uint8_t* data, std::size_t size); std::size_t write(const uint8_t* data, std::size_t size); - std::pair getPeerAddressAndPort(); + std::pair getPeerAddressAndPort() const; private: friend class TcpConnector; @@ -46,8 +44,7 @@ private: Dispatcher* dispatcher; int connection; - bool stopped; - Dispatcher::ContextPair contextPair; + ContextPair contextPair; TcpConnection(Dispatcher& dispatcher, int socket); }; diff --git a/src/Platform/Linux/System/TcpConnector.cpp b/src/Platform/Linux/System/TcpConnector.cpp index 22a6ddbd..76d577fb 100755 --- a/src/Platform/Linux/System/TcpConnector.cpp +++ b/src/Platform/Linux/System/TcpConnector.cpp @@ -33,7 +33,7 @@ namespace System { namespace { -struct TcpConnectorContextExt : public Dispatcher::OperationContext { +struct TcpConnectorContextExt : public OperationContext { int connection; }; @@ -42,13 +42,12 @@ struct TcpConnectorContextExt : public Dispatcher::OperationContext { TcpConnector::TcpConnector() : dispatcher(nullptr) { } -TcpConnector::TcpConnector(Dispatcher& dispatcher) : dispatcher(&dispatcher), context(nullptr), stopped(false) { +TcpConnector::TcpConnector(Dispatcher& dispatcher) : dispatcher(&dispatcher), context(nullptr) { } TcpConnector::TcpConnector(TcpConnector&& other) : dispatcher(other.dispatcher) { if (other.dispatcher != nullptr) { assert(other.context == nullptr); - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -61,7 +60,6 @@ TcpConnector& TcpConnector::operator=(TcpConnector&& other) { dispatcher = other.dispatcher; if (other.dispatcher != nullptr) { assert(other.context == nullptr); - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -69,34 +67,10 @@ TcpConnector& TcpConnector::operator=(TcpConnector&& other) { return *this; } -void TcpConnector::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void TcpConnector::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (context != nullptr) { - TcpConnectorContextExt* connectorContext = static_cast(context); - if (!connectorContext->interrupted) { - if (close(connectorContext->connection) == -1) { - throw std::runtime_error("TcpListener::stop, close failed, errno=" + std::to_string(errno)); - } - - connectorContext->interrupted = true; - dispatcher->pushContext(connectorContext->context); - } - } - - stopped = true; -} - TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) { assert(dispatcher != nullptr); assert(context == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -124,7 +98,7 @@ TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) { if (result == -1) { if (errno == EINPROGRESS) { - Dispatcher::ContextPair contextPair; + ContextPair contextPair; TcpConnectorContextExt connectorContext; connectorContext.interrupted = false; connectorContext.context = dispatcher->getCurrentContext(); @@ -140,7 +114,20 @@ TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) { message = "epoll_ctl() failed, errno=" + std::to_string(errno); } else { context = &connectorContext; + dispatcher->getCurrentContext()->interruptProcedure = [&] { + TcpConnectorContextExt* connectorContext1 = static_cast(context); + if (!connectorContext1->interrupted) { + if (close(connectorContext1->connection) == -1) { + throw std::runtime_error("TcpListener::stop, close failed, errno=" + std::to_string(errno)); + } + + connectorContext1->interrupted = true; + dispatcher->pushContext(connectorContext1->context); + } + }; + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; assert(dispatcher != nullptr); assert(connectorContext.context == dispatcher->getCurrentContext()); assert(contextPair.readContext == nullptr); diff --git a/src/Platform/Linux/System/TcpConnector.h b/src/Platform/Linux/System/TcpConnector.h index 9eef67ef..8dc7b110 100755 --- a/src/Platform/Linux/System/TcpConnector.h +++ b/src/Platform/Linux/System/TcpConnector.h @@ -35,14 +35,11 @@ public: ~TcpConnector(); TcpConnector& operator=(const TcpConnector&) = delete; TcpConnector& operator=(TcpConnector&& other); - void start(); - void stop(); TcpConnection connect(const Ipv4Address& address, uint16_t port); private: void* context; Dispatcher* dispatcher; - bool stopped; }; } diff --git a/src/Platform/Linux/System/TcpListener.cpp b/src/Platform/Linux/System/TcpListener.cpp index 4a3b7508..fde904b7 100755 --- a/src/Platform/Linux/System/TcpListener.cpp +++ b/src/Platform/Linux/System/TcpListener.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "Dispatcher.h" #include "TcpConnection.h" @@ -38,33 +39,32 @@ TcpListener::TcpListener(Dispatcher& dispatcher, const Ipv4Address& addr, uint16 std::string message; listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (listener == -1) { - message = "socket() failed, errno=" + std::to_string(errno); + message = "socket() failed, errno=" + std::to_string(errno) + ": " + strerror(errno); } else { int flags = fcntl(listener, F_GETFL, 0); if (flags == -1 || fcntl(listener, F_SETFL, flags | O_NONBLOCK) == -1) { - message = "fcntl() failed errno=" + std::to_string(errno); + message = "fcntl() failed errno=" + std::to_string(errno) + ": " + strerror(errno); } else { int on = 1; if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on) == -1) { - message = "setsockopt failed, errno=" + std::to_string(errno); + message = "setsockopt failed, errno=" + std::to_string(errno) + ": " + strerror(errno); } else { sockaddr_in address; address.sin_family = AF_INET; address.sin_port = htons(port); address.sin_addr.s_addr = htonl( addr.getValue()); if (bind(listener, reinterpret_cast(&address), sizeof address) != 0) { - message = "bind failed, errno=" + std::to_string(errno); + message = "bind failed, errno=" + std::to_string(errno) + ": " + strerror(errno); } else if (listen(listener, SOMAXCONN) != 0) { - message = "listen failed, errno=" + std::to_string(errno); + message = "listen failed, errno=" + std::to_string(errno) + ": " + strerror(errno); } else { epoll_event listenEvent; listenEvent.events = 0; listenEvent.data.ptr = nullptr; if (epoll_ctl(dispatcher.getEpoll(), EPOLL_CTL_ADD, listener, &listenEvent) == -1) { - message = "epoll_ctl() failed, errno=" + std::to_string(errno); + message = "epoll_ctl() failed, errno=" + std::to_string(errno) + ": " + strerror(errno); } else { - stopped = false; context = nullptr; return; } @@ -83,7 +83,6 @@ TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) { if (other.dispatcher != nullptr) { assert(other.context == nullptr); listener = other.listener; - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -109,7 +108,6 @@ TcpListener& TcpListener::operator=(TcpListener&& other) { if (other.dispatcher != nullptr) { assert(other.context == nullptr); listener = other.listener; - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -117,43 +115,15 @@ TcpListener& TcpListener::operator=(TcpListener&& other) { return *this; } -void TcpListener::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void TcpListener::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (context != nullptr) { - Dispatcher::OperationContext* listenerContext = static_cast(context); - if (!listenerContext->interrupted) { - epoll_event listenEvent; - listenEvent.events = 0; - listenEvent.data.ptr = nullptr; - - if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, listener, &listenEvent) == -1) { - throw std::runtime_error("TcpListener::stop, epoll_ctl() failed, errno=" + std::to_string(errno) ); - } - - listenerContext->interrupted = true; - dispatcher->pushContext(listenerContext->context); - } - } - - stopped = true; -} - TcpConnection TcpListener::accept() { assert(dispatcher != nullptr); assert(context == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } - Dispatcher::ContextPair contextPair; - Dispatcher::OperationContext listenerContext; + ContextPair contextPair; + OperationContext listenerContext; listenerContext.interrupted = false; listenerContext.context = dispatcher->getCurrentContext(); @@ -168,7 +138,26 @@ TcpConnection TcpListener::accept() { message = "epoll_ctl() failed, errno=" + std::to_string(errno); } else { context = &listenerContext; + dispatcher->getCurrentContext()->interruptProcedure = [&]() { + assert(dispatcher != nullptr); + assert(context != nullptr); + OperationContext* listenerContext = static_cast(context); + if (!listenerContext->interrupted) { + epoll_event listenEvent; + listenEvent.events = 0; + listenEvent.data.ptr = nullptr; + + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, listener, &listenEvent) == -1) { + throw std::runtime_error("TcpListener::stop, epoll_ctl() failed, errno=" + std::to_string(errno) ); + } + + listenerContext->interrupted = true; + dispatcher->pushContext(listenerContext->context); + } + }; + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; assert(dispatcher != nullptr); assert(listenerContext.context == dispatcher->getCurrentContext()); assert(contextPair.writeContext == nullptr); diff --git a/src/Platform/Linux/System/TcpListener.h b/src/Platform/Linux/System/TcpListener.h index 33d625e6..9478c423 100755 --- a/src/Platform/Linux/System/TcpListener.h +++ b/src/Platform/Linux/System/TcpListener.h @@ -35,15 +35,12 @@ public: ~TcpListener(); TcpListener& operator=(const TcpListener&) = delete; TcpListener& operator=(TcpListener&& other); - void start(); - void stop(); TcpConnection accept(); private: Dispatcher* dispatcher; void* context; int listener; - bool stopped; }; } diff --git a/src/Platform/Linux/System/Timer.cpp b/src/Platform/Linux/System/Timer.cpp index 8a95a96f..6cc0a350 100755 --- a/src/Platform/Linux/System/Timer.cpp +++ b/src/Platform/Linux/System/Timer.cpp @@ -31,14 +31,13 @@ namespace System { Timer::Timer() : dispatcher(nullptr) { } -Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), context(nullptr), stopped(false), timer(-1) { +Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), context(nullptr), timer(-1) { } Timer::Timer(Timer&& other) : dispatcher(other.dispatcher) { if (other.dispatcher != nullptr) { assert(other.context == nullptr); timer = other.timer; - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -54,7 +53,6 @@ Timer& Timer::operator=(Timer&& other) { if (other.dispatcher != nullptr) { assert(other.context == nullptr); timer = other.timer; - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; other.timer = -1; @@ -63,50 +61,10 @@ Timer& Timer::operator=(Timer&& other) { return *this; } -void Timer::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void Timer::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - - if (context != nullptr) { - Dispatcher::OperationContext* timerContext = static_cast(context); - if (!timerContext->interrupted) { - - uint64_t value = 0; - if(::read(timer, &value, sizeof value) == -1 ){ - if(errno == EAGAIN || errno == EWOULDBLOCK) { - timerContext->interrupted = true; - dispatcher->pushContext(timerContext->context); - } else { - throw std::runtime_error("Timer::stop, read failed, errno=" + std::to_string(errno)); - } - } else { - assert(value>0); - dispatcher->pushContext(timerContext->context); - } - - epoll_event timerEvent; - timerEvent.events = 0; - timerEvent.data.ptr = nullptr; - - if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, timer, &timerEvent) == -1) { - throw std::runtime_error("Timer::stop epoll_ctl() failed, errno=" + std::to_string(errno)); - } - } - } - - stopped = true; -} - void Timer::sleep(std::chrono::nanoseconds duration) { assert(dispatcher != nullptr); assert(context == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -122,8 +80,8 @@ void Timer::sleep(std::chrono::nanoseconds duration) { expires.it_value.tv_nsec = std::chrono::duration_cast(duration - seconds).count(); timerfd_settime(timer, 0, &expires, NULL); - Dispatcher::ContextPair contextPair; - Dispatcher::OperationContext timerContext; + ContextPair contextPair; + OperationContext timerContext; timerContext.interrupted = false; timerContext.context = dispatcher->getCurrentContext(); contextPair.writeContext = nullptr; @@ -136,9 +94,37 @@ void Timer::sleep(std::chrono::nanoseconds duration) { if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, timer, &timerEvent) == -1) { throw std::runtime_error("Timer::sleep, epoll_ctl() failed, errno=" + std::to_string(errno)); } + dispatcher->getCurrentContext()->interruptProcedure = [&]() { + assert(dispatcher != nullptr); + assert(context != nullptr); + OperationContext* timerContext = static_cast(context); + if (!timerContext->interrupted) { + uint64_t value = 0; + if(::read(timer, &value, sizeof value) == -1 ){ + if(errno == EAGAIN || errno == EWOULDBLOCK) { + timerContext->interrupted = true; + dispatcher->pushContext(timerContext->context); + } else { + throw std::runtime_error("Timer::interrupt, read failed, errno=" + std::to_string(errno)); + } + } else { + assert(value>0); + dispatcher->pushContext(timerContext->context); + } + + epoll_event timerEvent; + timerEvent.events = 0; + timerEvent.data.ptr = nullptr; + + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, timer, &timerEvent) == -1) { + throw std::runtime_error("Timer::interrupt epoll_ctl() failed, errno=" + std::to_string(errno)); + } + } + }; context = &timerContext; dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; assert(dispatcher != nullptr); assert(timerContext.context == dispatcher->getCurrentContext()); assert(contextPair.writeContext == nullptr); diff --git a/src/Platform/Linux/System/Timer.h b/src/Platform/Linux/System/Timer.h index 20a0f1a4..ef6255b6 100755 --- a/src/Platform/Linux/System/Timer.h +++ b/src/Platform/Linux/System/Timer.h @@ -32,14 +32,11 @@ public: ~Timer(); Timer& operator=(const Timer&) = delete; Timer& operator=(Timer&& other); - void start(); - void stop(); void sleep(std::chrono::nanoseconds duration); private: Dispatcher* dispatcher; void* context; - bool stopped; int timer; }; diff --git a/src/Platform/OSX/System/context.c b/src/Platform/OSX/System/Context.c old mode 100644 new mode 100755 similarity index 100% rename from src/Platform/OSX/System/context.c rename to src/Platform/OSX/System/Context.c diff --git a/src/Platform/OSX/System/context.h b/src/Platform/OSX/System/Context.h old mode 100644 new mode 100755 similarity index 100% rename from src/Platform/OSX/System/context.h rename to src/Platform/OSX/System/Context.h diff --git a/src/Platform/OSX/System/Dispatcher.cpp b/src/Platform/OSX/System/Dispatcher.cpp index ba2aad22..619200fa 100755 --- a/src/Platform/OSX/System/Dispatcher.cpp +++ b/src/Platform/OSX/System/Dispatcher.cpp @@ -18,27 +18,56 @@ #include "Dispatcher.h" #include #include - -#include -#include -#include #include +#include #include #include +#include +#include +#include #include - -#include "context.h" +#include "Context.h" namespace System { +namespace{ + +struct ContextMakingData { + void* uctx; + Dispatcher* dispatcher; +}; + +class MutextGuard { +public: + MutextGuard(pthread_mutex_t& _mutex) : mutex(_mutex) { + auto ret = pthread_mutex_lock(&mutex); + if (ret != 0) { + throw std::runtime_error("failed to acquire mutex, errno=" + std::to_string(ret) + ": " + strerror(ret)); + } + } + + ~MutextGuard() { + pthread_mutex_unlock(&mutex); + } + +private: + pthread_mutex_t& mutex; +}; + +const size_t STACK_SIZE = 64 * 1024; + +} + +static_assert(Dispatcher::SIZEOF_PTHREAD_MUTEX_T == sizeof(pthread_mutex_t), "invalid pthread mutex size"); + Dispatcher::Dispatcher() : lastCreatedTimer(0) { std::string message; kqueue = ::kqueue(); if (kqueue == -1) { message = "kqueue() fail errno=" + std::to_string(errno); } else { - currentContext = new uctx; - if (getcontext(static_cast(currentContext)) == -1) { + mainContext.uctx = new uctx; + if (getcontext(static_cast(mainContext.uctx)) == -1) { message = "getcontext() fail errno=" + std::to_string(errno); } else { struct kevent event; @@ -50,7 +79,19 @@ Dispatcher::Dispatcher() : lastCreatedTimer(0) { message = "pthread_mutex_init() fail errno=" + std::to_string(errno); } else { remoteSpawned = false; - contextCount = 0; + + mainContext.interrupted = false; + mainContext.group = &contextGroup; + mainContext.groupPrev = nullptr; + mainContext.groupNext = nullptr; + contextGroup.firstContext = nullptr; + contextGroup.lastContext = nullptr; + contextGroup.firstWaiter = nullptr; + contextGroup.lastWaiter = nullptr; + currentContext = &mainContext; + firstResumingContext = nullptr; + firstReusableContext = nullptr; + runningContextCount = 0; return; } } @@ -64,17 +105,23 @@ Dispatcher::Dispatcher() : lastCreatedTimer(0) { } Dispatcher::~Dispatcher() { - assert(resumingContexts.empty()); - assert(reusableContexts.size() == contextCount); - assert(spawningProcedures.empty()); - assert(reusableContexts.size() == allocatedStacks.size()); - while (!reusableContexts.empty()) { - delete[] allocatedStacks.top(); - allocatedStacks.pop(); - delete static_cast(reusableContexts.top()); - reusableContexts.pop(); + for (NativeContext* context = contextGroup.firstContext; context != nullptr; context = context->groupNext) { + interrupt(context); } + yield(); + assert(contextGroup.firstContext == nullptr); + assert(contextGroup.firstWaiter == nullptr); + assert(firstResumingContext == nullptr); + assert(runningContextCount == 0); + while (firstReusableContext != nullptr) { + auto ucontext = static_cast(firstReusableContext->uctx); + auto stackPtr = static_cast(firstReusableContext->stackPtr); + firstReusableContext = firstReusableContext->next; + delete[] stackPtr; + delete ucontext; + } + auto result = close(kqueue); assert(result != -1); result = pthread_mutex_destroy(reinterpret_cast(this->mutex)); @@ -82,33 +129,32 @@ Dispatcher::~Dispatcher() { } void Dispatcher::clear() { - while (!reusableContexts.empty()) { - delete[] allocatedStacks.top(); - allocatedStacks.pop(); - delete static_cast(reusableContexts.top()); - reusableContexts.pop(); - --contextCount; + while (firstReusableContext != nullptr) { + auto ucontext = static_cast(firstReusableContext->uctx); + auto stackPtr = static_cast(firstReusableContext->stackPtr); + firstReusableContext = firstReusableContext->next; + delete[] stackPtr; + delete ucontext; } } void Dispatcher::dispatch() { - void* context; + NativeContext* context; for (;;) { - if (!resumingContexts.empty()) { - context = resumingContexts.front(); - resumingContexts.pop(); + if (firstResumingContext != nullptr) { + context = firstResumingContext; + firstResumingContext = context->next; break; } - + if(remoteSpawned.load() == true) { - pthread_mutex_lock(reinterpret_cast(this->mutex)); + MutextGuard guard(*reinterpret_cast(this->mutex)); while (!remoteSpawningProcedures.empty()) { spawn(std::move(remoteSpawningProcedures.front())); remoteSpawningProcedures.pop(); } remoteSpawned = false; - pthread_mutex_unlock(reinterpret_cast(this->mutex)); continue; } @@ -132,67 +178,97 @@ void Dispatcher::dispatch() { if (errno != EINTR) { throw std::runtime_error("Dispatcher::dispatch(), kqueue() fail errno=" + std::to_string(errno)); } else { - pthread_mutex_lock(reinterpret_cast(this->mutex)); + MutextGuard guard(*reinterpret_cast(this->mutex)); while (!remoteSpawningProcedures.empty()) { spawn(std::move(remoteSpawningProcedures.front())); remoteSpawningProcedures.pop(); } - pthread_mutex_unlock(reinterpret_cast(this->mutex)); } } if (context != currentContext) { - uctx* oldContext = static_cast(currentContext); + uctx* oldContext = static_cast(currentContext->uctx); currentContext = context; - if (swapcontext(oldContext,static_cast(currentContext)) == -1) { + if (swapcontext(oldContext,static_cast(currentContext->uctx)) == -1) { throw std::runtime_error("Dispatcher::dispatch(), swapcontext() failed, errno=" + std::to_string(errno)); } } } -void* Dispatcher::getCurrentContext() const { +NativeContext* Dispatcher::getCurrentContext() const { return currentContext; } -void Dispatcher::pushContext(void* context) { - resumingContexts.push(context); +void Dispatcher::interrupt() { + interrupt(currentContext); +} + +void Dispatcher::interrupt(NativeContext* context) { + assert(context!=nullptr); + if (!context->interrupted) { + if (context->interruptProcedure != nullptr) { + context->interruptProcedure(); + context->interruptProcedure = nullptr; + } else { + context->interrupted = true; + } + } +} + +bool Dispatcher::interrupted() { + if (currentContext->interrupted) { + currentContext->interrupted = false; + return true; + } + + return false; +} + +void Dispatcher::pushContext(NativeContext* context) { + assert(context!=nullptr); + context->next = nullptr; + if (firstResumingContext != nullptr) { + assert(lastResumingContext != nullptr); + lastResumingContext->next = context; + } else { + firstResumingContext = context; + } + + lastResumingContext = context; } void Dispatcher::remoteSpawn(std::function&& procedure) { - pthread_mutex_lock(reinterpret_cast(this->mutex)); + MutextGuard guard(*reinterpret_cast(this->mutex)); remoteSpawningProcedures.push(std::move(procedure)); - if(remoteSpawned == false) { + if (remoteSpawned == false) { remoteSpawned = true; struct kevent event; - EV_SET(&event, 0, EVFILT_USER, EV_ADD | EV_ONESHOT, NOTE_FFCOPY | NOTE_TRIGGER, 0, NULL); + EV_SET(&event, 0, EVFILT_USER, EV_ADD | EV_ENABLE, NOTE_FFCOPY | NOTE_TRIGGER, 0, NULL); if (kevent(kqueue, &event, 1, NULL, 0, NULL) == -1) { throw std::runtime_error("Dispatcher::remoteSpawn(), kevent() fail errno=" + std::to_string(errno)); }; } - - pthread_mutex_unlock(reinterpret_cast(this->mutex)); } void Dispatcher::spawn(std::function&& procedure) { - void* context; - if (reusableContexts.empty()) { - context = new uctx; - uint8_t* stackPointer = new uint8_t[64 * 1024]; - allocatedStacks.push(stackPointer); - - static_cast(context)->uc_stack.ss_sp = stackPointer; - static_cast(context)->uc_stack.ss_size = 64 * 1024; - makecontext(static_cast(context), reinterpret_cast(contextProcedureStatic), reinterpret_cast(this)); - - ++contextCount; + NativeContext* context = &getReusableContext(); + if(contextGroup.firstContext != nullptr) { + context->groupPrev = contextGroup.lastContext; + assert(contextGroup.lastContext->groupNext == nullptr); + contextGroup.lastContext->groupNext = context; } else { - context = reusableContexts.top(); - reusableContexts.pop(); + context->groupPrev = nullptr; + contextGroup.firstContext = context; + contextGroup.firstWaiter = nullptr; } - resumingContexts.push(context); - spawningProcedures.emplace(std::move(procedure)); + context->interrupted = false; + context->group = &contextGroup; + context->groupNext = nullptr; + context->procedure = std::move(procedure); + contextGroup.lastContext = context; + pushContext(context); } void Dispatcher::yield() { @@ -213,18 +289,18 @@ void Dispatcher::yield() { throw std::runtime_error("kevent() fail errno=" + std::to_string(errno)); } - pthread_mutex_lock(reinterpret_cast(this->mutex)); + MutextGuard guard(*reinterpret_cast(this->mutex)); while (!remoteSpawningProcedures.empty()) { spawn(std::move(remoteSpawningProcedures.front())); remoteSpawningProcedures.pop(); } remoteSpawned = false; - pthread_mutex_unlock(reinterpret_cast(this->mutex)); continue; } - resumingContexts.push(static_cast(events[i].udata)->context); + static_cast(events[i].udata)->context->interruptProcedure = nullptr; + pushContext(static_cast(events[i].udata)->context); } } else { if (errno != EINTR) { @@ -233,8 +309,8 @@ void Dispatcher::yield() { } } - if (!resumingContexts.empty()) { - resumingContexts.push(getCurrentContext()); + if (firstResumingContext != nullptr) { + pushContext(currentContext); dispatch(); } } @@ -243,6 +319,37 @@ int Dispatcher::getKqueue() const { return kqueue; } +NativeContext& Dispatcher::getReusableContext() { + if(firstReusableContext == nullptr) { + uctx* newlyCreatedContext = new uctx; + uint8_t* stackPointer = new uint8_t[STACK_SIZE]; + static_cast(newlyCreatedContext)->uc_stack.ss_sp = stackPointer; + static_cast(newlyCreatedContext)->uc_stack.ss_size = STACK_SIZE; + + ContextMakingData makingData{ newlyCreatedContext, this}; + makecontext(static_cast(newlyCreatedContext), reinterpret_cast(contextProcedureStatic), reinterpret_cast(&makingData)); + + uctx* oldContext = static_cast(currentContext->uctx); + if (swapcontext(oldContext, newlyCreatedContext) == -1) { + throw std::runtime_error("Dispatcher::getReusableContext(), swapcontext() failed, errno=" + std::to_string(errno)); + } + + assert(firstReusableContext != nullptr); + assert(firstReusableContext->uctx == newlyCreatedContext); + firstReusableContext->stackPtr = stackPointer; + } + + NativeContext* context = firstReusableContext; + firstReusableContext = firstReusableContext->next; + return *context; +} + +void Dispatcher::pushReusableContext(NativeContext& context) { + context.next = firstReusableContext; + firstReusableContext = &context; + --runningContextCount; +} + int Dispatcher::getTimer() { int timer; if (timers.empty()) { @@ -259,20 +366,68 @@ void Dispatcher::pushTimer(int timer) { timers.push(timer); } -void Dispatcher::contextProcedure() { - void* context = currentContext; +void Dispatcher::contextProcedure(void* ucontext) { + assert(firstReusableContext == nullptr); + NativeContext context; + context.uctx = ucontext; + context.interrupted = false; + context.next = nullptr; + firstReusableContext = &context; + uctx* oldContext = static_cast(context.uctx); + if (swapcontext(oldContext, static_cast(currentContext->uctx)) == -1) { + throw std::runtime_error("Dispatcher::contextProcedure() swapcontext() failed, errno=" + std::to_string(errno)); + } + for (;;) { - assert(!spawningProcedures.empty()); - std::function procedure = std::move(spawningProcedures.front()); - spawningProcedures.pop(); - procedure(); - reusableContexts.push(context); + ++runningContextCount; + try { + context.procedure(); + } catch(std::exception&) { + } + + if (context.group != nullptr) { + if (context.groupPrev != nullptr) { + assert(context.groupPrev->groupNext == &context); + context.groupPrev->groupNext = context.groupNext; + if (context.groupNext != nullptr) { + assert(context.groupNext->groupPrev == &context); + context.groupNext->groupPrev = context.groupPrev; + } else { + assert(context.group->lastContext == &context); + context.group->lastContext = context.groupPrev; + } + } else { + assert(context.group->firstContext == &context); + context.group->firstContext = context.groupNext; + if (context.groupNext != nullptr) { + assert(context.groupNext->groupPrev == &context); + context.groupNext->groupPrev = nullptr; + } else { + assert(context.group->lastContext == &context); + if (context.group->firstWaiter != nullptr) { + if (firstResumingContext != nullptr) { + assert(lastResumingContext->next == nullptr); + lastResumingContext->next = context.group->firstWaiter; + } else { + firstResumingContext = context.group->firstWaiter; + } + + lastResumingContext = context.group->lastWaiter; + context.group->firstWaiter = nullptr; + } + } + } + + pushReusableContext(context); + } + dispatch(); } } void Dispatcher::contextProcedureStatic(intptr_t context) { - reinterpret_cast(context)->contextProcedure(); + ContextMakingData* makingContextData = reinterpret_cast(context); + makingContextData->dispatcher->contextProcedure(makingContextData->uctx); } } diff --git a/src/Platform/OSX/System/Dispatcher.h b/src/Platform/OSX/System/Dispatcher.h index 482dc3c2..48bb5636 100755 --- a/src/Platform/OSX/System/Dispatcher.h +++ b/src/Platform/OSX/System/Dispatcher.h @@ -18,12 +18,39 @@ #pragma once #include +#include #include #include #include namespace System { +struct NativeContextGroup; + +struct NativeContext { + void* uctx; + void* stackPtr; + bool interrupted; + NativeContext* next; + NativeContextGroup* group; + NativeContext* groupPrev; + NativeContext* groupNext; + std::function procedure; + std::function interruptProcedure; +}; + +struct NativeContextGroup { + NativeContext* firstContext; + NativeContext* lastContext; + NativeContext* firstWaiter; + NativeContext* lastWaiter; +}; + +struct OperationContext { + NativeContext* context; + bool interrupted; +}; + class Dispatcher { public: Dispatcher(); @@ -32,18 +59,17 @@ public: Dispatcher& operator=(const Dispatcher&) = delete; void clear(); void dispatch(); - void* getCurrentContext() const; - void pushContext(void* context); + NativeContext* getCurrentContext() const; + void interrupt(); + void interrupt(NativeContext* context); + bool interrupted(); + void pushContext(NativeContext* context); void remoteSpawn(std::function&& procedure); - void spawn(std::function&& procedure); void yield(); - struct OperationContext { - void *context; - bool interrupted; - }; - int getKqueue() const; + NativeContext& getReusableContext(); + void pushReusableContext(NativeContext&); int getTimer(); void pushTimer(int timer); @@ -54,20 +80,24 @@ public: #endif private: - std::stack allocatedStacks; - std::size_t contextCount; - void* currentContext; + void spawn(std::function&& procedure); + int kqueue; int lastCreatedTimer; - uint8_t mutex[SIZEOF_PTHREAD_MUTEX_T]; + alignas(std::max_align_t) uint8_t mutex[SIZEOF_PTHREAD_MUTEX_T]; std::atomic remoteSpawned; std::queue> remoteSpawningProcedures; - std::queue resumingContexts; - std::queue> spawningProcedures; - std::stack reusableContexts; std::stack timers; - void contextProcedure(); + NativeContext mainContext; + NativeContextGroup contextGroup; + NativeContext* currentContext; + NativeContext* firstResumingContext; + NativeContext* lastResumingContext; + NativeContext* firstReusableContext; + size_t runningContextCount; + + void contextProcedure(void* uctx); static void contextProcedureStatic(intptr_t context); }; diff --git a/src/Platform/OSX/System/Ipv4Resolver.cpp b/src/Platform/OSX/System/Ipv4Resolver.cpp index d78195e4..b9f1e435 100755 --- a/src/Platform/OSX/System/Ipv4Resolver.cpp +++ b/src/Platform/OSX/System/Ipv4Resolver.cpp @@ -22,6 +22,7 @@ #include +#include #include #include @@ -30,12 +31,11 @@ namespace System { Ipv4Resolver::Ipv4Resolver() : dispatcher(nullptr) { } -Ipv4Resolver::Ipv4Resolver(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false) { +Ipv4Resolver::Ipv4Resolver(Dispatcher& dispatcher) : dispatcher(&dispatcher) { } Ipv4Resolver::Ipv4Resolver(Ipv4Resolver&& other) : dispatcher(other.dispatcher) { if (dispatcher != nullptr) { - stopped = other.stopped; other.dispatcher = nullptr; } } @@ -46,28 +46,15 @@ Ipv4Resolver::~Ipv4Resolver() { Ipv4Resolver& Ipv4Resolver::operator=(Ipv4Resolver&& other) { dispatcher = other.dispatcher; if (dispatcher != nullptr) { - stopped = other.stopped; other.dispatcher = nullptr; } return *this; } -void Ipv4Resolver::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void Ipv4Resolver::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - stopped = true; -} - Ipv4Address Ipv4Resolver::resolve(const std::string& host) { assert(dispatcher != nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } diff --git a/src/Platform/OSX/System/Ipv4Resolver.h b/src/Platform/OSX/System/Ipv4Resolver.h index d59d50e0..02dd9793 100755 --- a/src/Platform/OSX/System/Ipv4Resolver.h +++ b/src/Platform/OSX/System/Ipv4Resolver.h @@ -33,13 +33,10 @@ public: ~Ipv4Resolver(); Ipv4Resolver& operator=(const Ipv4Resolver&) = delete; Ipv4Resolver& operator=(Ipv4Resolver&& other); - void start(); - void stop(); Ipv4Address resolve(const std::string& host); private: Dispatcher* dispatcher; - bool stopped; }; } diff --git a/src/Platform/OSX/System/TcpConnection.cpp b/src/Platform/OSX/System/TcpConnection.cpp index 62872f63..112c2d59 100755 --- a/src/Platform/OSX/System/TcpConnection.cpp +++ b/src/Platform/OSX/System/TcpConnection.cpp @@ -38,7 +38,6 @@ TcpConnection::TcpConnection(TcpConnection&& other) : dispatcher(other.dispatche assert(other.readContext == nullptr); assert(other.writeContext == nullptr); connection = other.connection; - stopped = other.stopped; readContext = nullptr; writeContext = nullptr; other.dispatcher = nullptr; @@ -68,7 +67,6 @@ TcpConnection& TcpConnection::operator=(TcpConnection&& other) { assert(other.readContext == nullptr); assert(other.writeContext == nullptr); connection = other.connection; - stopped = other.stopped; readContext = nullptr; writeContext = nullptr; other.dispatcher = nullptr; @@ -77,52 +75,10 @@ TcpConnection& TcpConnection::operator=(TcpConnection&& other) { return *this; } -void TcpConnection::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void TcpConnection::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (writeContext != nullptr) { - Dispatcher::OperationContext* context = static_cast(writeContext); - if (!context->interrupted) { - struct kevent event; - EV_SET(&event, connection, EVFILT_WRITE, EV_DELETE | EV_DISABLE, 0, 0, NULL); - - if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { - throw std::runtime_error("TcpListener::stop, kevent() failed, errno=" + std::to_string(errno)); - } - - context->interrupted = true; - dispatcher->pushContext(context->context); - } - } - - if (readContext != nullptr) { - Dispatcher::OperationContext* context = static_cast(readContext); - if (!context->interrupted) { - struct kevent event; - EV_SET(&event, connection, EVFILT_READ, EV_DELETE | EV_DISABLE, 0, 0, NULL); - - if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { - throw std::runtime_error("TcpListener::stop, kevent() failed, errno=" + std::to_string(errno)); - } - - context->interrupted = true; - dispatcher->pushContext(context->context); - } - } - - stopped = true; -} - size_t TcpConnection::read(uint8_t* data, size_t size) { assert(dispatcher != nullptr); assert(readContext == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -132,7 +88,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) { if (errno != EAGAIN && errno != EWOULDBLOCK) { message = "recv failed, errno=" + std::to_string(errno); } else { - Dispatcher::OperationContext context; + OperationContext context; context.context = dispatcher->getCurrentContext(); context.interrupted = false; struct kevent event; @@ -141,7 +97,25 @@ size_t TcpConnection::read(uint8_t* data, size_t size) { message = "kevent() failed, errno=" + std::to_string(errno); } else { readContext = &context; + dispatcher->getCurrentContext()->interruptProcedure = [&] { + assert(dispatcher != nullptr); + assert(readContext != nullptr); + OperationContext* context = static_cast(readContext); + if (!context->interrupted) { + struct kevent event; + EV_SET(&event, connection, EVFILT_READ, EV_DELETE | EV_DISABLE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("TcpListener::interruptionProcedure, kevent() failed, errno=" + std::to_string(errno)); + } + + context->interrupted = true; + dispatcher->pushContext(context->context); + } + }; + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; assert(dispatcher != nullptr); assert(context.context == dispatcher->getCurrentContext()); assert(readContext == &context); @@ -171,7 +145,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) { size_t TcpConnection::write(const uint8_t* data, size_t size) { assert(dispatcher != nullptr); assert(writeContext == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -189,7 +163,7 @@ size_t TcpConnection::write(const uint8_t* data, size_t size) { if (errno != EAGAIN && errno != EWOULDBLOCK) { message = "send failed, result=" + std::to_string(errno); } else { - Dispatcher::OperationContext context; + OperationContext context; context.context = dispatcher->getCurrentContext(); context.interrupted = false; struct kevent event; @@ -198,7 +172,25 @@ size_t TcpConnection::write(const uint8_t* data, size_t size) { message = "kevent() failed, errno=" + std::to_string(errno); } else { writeContext = &context; + dispatcher->getCurrentContext()->interruptProcedure = [&] { + assert(dispatcher != nullptr); + assert(writeContext != nullptr); + OperationContext* context = static_cast(writeContext); + if (!context->interrupted) { + struct kevent event; + EV_SET(&event, connection, EVFILT_WRITE, EV_DELETE | EV_DISABLE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("TcpListener::stop, kevent() failed, errno=" + std::to_string(errno)); + } + + context->interrupted = true; + dispatcher->pushContext(context->context); + } + }; + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; assert(dispatcher != nullptr); assert(context.context == dispatcher->getCurrentContext()); assert(writeContext == &context); @@ -225,7 +217,7 @@ size_t TcpConnection::write(const uint8_t* data, size_t size) { return transferred; } -std::pair TcpConnection::getPeerAddressAndPort() { +std::pair TcpConnection::getPeerAddressAndPort() const { sockaddr_in addr; socklen_t size = sizeof(addr); if (getpeername(connection, reinterpret_cast(&addr), &size) != 0) { @@ -236,7 +228,11 @@ std::pair TcpConnection::getPeerAddressAndPort() { return std::make_pair(Ipv4Address(htonl(addr.sin_addr.s_addr)), htons(addr.sin_port)); } -TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), stopped(false), readContext(nullptr), writeContext(nullptr) { +TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), readContext(nullptr), writeContext(nullptr) { + int val = 1; + if (setsockopt(connection, SOL_SOCKET, SO_NOSIGPIPE, (void*)&val, sizeof val) == -1) { + throw std::runtime_error("TcpConnection::TcpConnection, setsockopt failed, result=" + std::to_string(errno)); + } } } diff --git a/src/Platform/OSX/System/TcpConnection.h b/src/Platform/OSX/System/TcpConnection.h index c98650f7..63da8280 100755 --- a/src/Platform/OSX/System/TcpConnection.h +++ b/src/Platform/OSX/System/TcpConnection.h @@ -34,11 +34,9 @@ public: ~TcpConnection(); TcpConnection& operator=(const TcpConnection&) = delete; TcpConnection& operator=(TcpConnection&& other); - void start(); - void stop(); std::size_t read(uint8_t* data, std::size_t size); std::size_t write(const uint8_t* data, std::size_t size); - std::pair getPeerAddressAndPort(); + std::pair getPeerAddressAndPort() const; private: friend class TcpConnector; @@ -46,7 +44,6 @@ private: Dispatcher* dispatcher; int connection; - bool stopped; void* readContext; void* writeContext; diff --git a/src/Platform/OSX/System/TcpConnector.cpp b/src/Platform/OSX/System/TcpConnector.cpp index 794ec515..898b9536 100755 --- a/src/Platform/OSX/System/TcpConnector.cpp +++ b/src/Platform/OSX/System/TcpConnector.cpp @@ -35,7 +35,7 @@ namespace System { namespace { -struct ConnectorContext : public Dispatcher::OperationContext { +struct ConnectorContext : public OperationContext { int connection; }; @@ -44,13 +44,12 @@ struct ConnectorContext : public Dispatcher::OperationContext { TcpConnector::TcpConnector() : dispatcher(nullptr) { } -TcpConnector::TcpConnector(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr) { +TcpConnector::TcpConnector(Dispatcher& dispatcher) : dispatcher(&dispatcher), context(nullptr) { } TcpConnector::TcpConnector(TcpConnector&& other) : dispatcher(other.dispatcher) { if (other.dispatcher != nullptr) { assert(other.context == nullptr); - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -64,7 +63,6 @@ TcpConnector& TcpConnector::operator=(TcpConnector&& other) { dispatcher = other.dispatcher; if (other.dispatcher != nullptr) { assert(other.context == nullptr); - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -72,34 +70,10 @@ TcpConnector& TcpConnector::operator=(TcpConnector&& other) { return *this; } -void TcpConnector::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void TcpConnector::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (context != nullptr) { - ConnectorContext* connectorContext = static_cast(context); - if (!connectorContext->interrupted) { - if (close(connectorContext->connection) == -1) { - throw std::runtime_error("TcpListener::stop, close failed, errno=" + std::to_string(errno)); - } - - dispatcher->pushContext(connectorContext->context); - connectorContext->interrupted = true; - } - } - - stopped = true; -} - TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) { assert(dispatcher != nullptr); assert(context == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -137,7 +111,22 @@ TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) { message = "kevent() failed, errno=" + std::to_string(errno); } else { context = &connectorContext; + dispatcher->getCurrentContext()->interruptProcedure = [&] { + assert(dispatcher != nullptr); + assert(context != nullptr); + ConnectorContext* connectorContext = static_cast(context); + if (!connectorContext->interrupted) { + if (close(connectorContext->connection) == -1) { + throw std::runtime_error("TcpListener::stop, close failed, errno=" + std::to_string(errno)); + } + + dispatcher->pushContext(connectorContext->context); + connectorContext->interrupted = true; + } + }; + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; assert(dispatcher != nullptr); assert(connectorContext.context == dispatcher->getCurrentContext()); assert(context == &connectorContext); diff --git a/src/Platform/OSX/System/TcpConnector.h b/src/Platform/OSX/System/TcpConnector.h index 9eef67ef..8dc7b110 100755 --- a/src/Platform/OSX/System/TcpConnector.h +++ b/src/Platform/OSX/System/TcpConnector.h @@ -35,14 +35,11 @@ public: ~TcpConnector(); TcpConnector& operator=(const TcpConnector&) = delete; TcpConnector& operator=(TcpConnector&& other); - void start(); - void stop(); TcpConnection connect(const Ipv4Address& address, uint16_t port); private: void* context; Dispatcher* dispatcher; - bool stopped; }; } diff --git a/src/Platform/OSX/System/TcpListener.cpp b/src/Platform/OSX/System/TcpListener.cpp index 4c9f15bf..7c9ea2d3 100755 --- a/src/Platform/OSX/System/TcpListener.cpp +++ b/src/Platform/OSX/System/TcpListener.cpp @@ -61,12 +61,11 @@ TcpListener::TcpListener(Dispatcher& dispatcher, const Ipv4Address& addr, uint16 message = "listen failed, errno=" + std::to_string(errno); } else { struct kevent event; - EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_DISABLE, 0, SOMAXCONN, NULL); + EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_DISABLE | EV_CLEAR, 0, SOMAXCONN, NULL); if (kevent(dispatcher.getKqueue(), &event, 1, NULL, 0, NULL) == -1) { message = "kevent() failed, errno=" + std::to_string(errno); } else { - stopped = false; context = nullptr; return; } @@ -86,7 +85,6 @@ TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) { if (other.dispatcher != nullptr) { assert(other.context == nullptr); listener = other.listener; - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -112,7 +110,6 @@ TcpListener& TcpListener::operator=(TcpListener&& other) { if (other.dispatcher != nullptr) { assert(other.context == nullptr); listener = other.listener; - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -120,52 +117,43 @@ TcpListener& TcpListener::operator=(TcpListener&& other) { return *this; } -void TcpListener::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void TcpListener::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (context != nullptr) { - Dispatcher::OperationContext* listenerContext = static_cast(context); - if (!listenerContext->interrupted) { - - struct kevent event; - EV_SET(&event, listener, EVFILT_READ, EV_DELETE | EV_DISABLE, 0, 0, NULL); - - if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { - throw std::runtime_error("TcpListener::stop, kevent() failed, errno=" + std::to_string(errno)); - } - - listenerContext->interrupted = true; - dispatcher->pushContext(listenerContext->context); - } - } - - stopped = true; -} - TcpConnection TcpListener::accept() { assert(dispatcher != nullptr); assert(context == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } std::string message; - Dispatcher::OperationContext listenerContext; + OperationContext listenerContext; listenerContext.context = dispatcher->getCurrentContext(); listenerContext.interrupted = false; struct kevent event; - EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, SOMAXCONN, &listenerContext); + EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR , 0, SOMAXCONN, &listenerContext); if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { message = "kevent() failed, errno=" + std::to_string(errno); } else { context = &listenerContext; + dispatcher->getCurrentContext()->interruptProcedure = [&] { + assert(dispatcher != nullptr); + assert(context != nullptr); + OperationContext* listenerContext = static_cast(context); + if (!listenerContext->interrupted) { + + struct kevent event; + EV_SET(&event, listener, EVFILT_READ, EV_DELETE | EV_DISABLE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("TcpListener::stop, kevent() failed, errno=" + std::to_string(errno)); + } + + listenerContext->interrupted = true; + dispatcher->pushContext(listenerContext->context); + } + }; + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; assert(dispatcher != nullptr); assert(listenerContext.context == dispatcher->getCurrentContext()); assert(context == &listenerContext); diff --git a/src/Platform/OSX/System/TcpListener.h b/src/Platform/OSX/System/TcpListener.h index 43fc2b4a..e5baaf8c 100755 --- a/src/Platform/OSX/System/TcpListener.h +++ b/src/Platform/OSX/System/TcpListener.h @@ -35,14 +35,11 @@ public: ~TcpListener(); TcpListener& operator=(const TcpListener&) = delete; TcpListener& operator=(TcpListener&& other); - void start(); - void stop(); TcpConnection accept(); private: Dispatcher* dispatcher; int listener; - bool stopped; void* context; }; diff --git a/src/Platform/OSX/System/Timer.cpp b/src/Platform/OSX/System/Timer.cpp index a8a9e829..6f61c7b5 100755 --- a/src/Platform/OSX/System/Timer.cpp +++ b/src/Platform/OSX/System/Timer.cpp @@ -33,14 +33,13 @@ namespace System { Timer::Timer() : dispatcher(nullptr) { } -Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr), timer(-1) { +Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), context(nullptr), timer(-1) { } Timer::Timer(Timer&& other) : dispatcher(other.dispatcher) { if (other.dispatcher != nullptr) { assert(other.context == nullptr); timer = other.timer; - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -56,7 +55,6 @@ Timer& Timer::operator=(Timer&& other) { if (other.dispatcher != nullptr) { assert(other.context == nullptr); timer = other.timer; - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; other.timer = -1; @@ -65,21 +63,33 @@ Timer& Timer::operator=(Timer&& other) { return *this; } -void Timer::start() { +void Timer::sleep(std::chrono::nanoseconds duration) { assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} + assert(context == nullptr); + if (dispatcher->interrupted()) { + throw InterruptedException(); + } -void Timer::stop() { - assert(dispatcher != nullptr); - assert(!stopped); + OperationContext timerContext; + timerContext.context = dispatcher->getCurrentContext(); + timerContext.interrupted = false; + timer = dispatcher->getTimer(); - if (context != nullptr) { - Dispatcher::OperationContext* timerContext = static_cast(context); + struct kevent event; + EV_SET(&event, timer, EVFILT_TIMER, EV_ADD | EV_ENABLE | EV_ONESHOT, NOTE_NSECONDS, duration.count(), &timerContext); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("Timer::stop, kevent() failed, errno=" + std::to_string(errno)); + } + + context = &timerContext; + dispatcher->getCurrentContext()->interruptProcedure = [&] { + assert(dispatcher != nullptr); + assert(context != nullptr); + OperationContext* timerContext = static_cast(context); if (!timerContext->interrupted) { struct kevent event; - EV_SET(&event, timer, EVFILT_TIMER, EV_DISABLE, 0, 0, NULL); + EV_SET(&event, timer, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { throw std::runtime_error("Timer::stop, kevent() failed, errno=" + std::to_string(errno)); @@ -88,32 +98,10 @@ void Timer::stop() { dispatcher->pushContext(timerContext->context); timerContext->interrupted = true; } - } - - stopped = true; -} - -void Timer::sleep(std::chrono::nanoseconds duration) { - assert(dispatcher != nullptr); - assert(context == nullptr); - if (stopped) { - throw InterruptedException(); - } - - Dispatcher::OperationContext timerContext; - timerContext.context = dispatcher->getCurrentContext(); - timerContext.interrupted = false; - timer = dispatcher->getTimer(); - - struct kevent event; - EV_SET(&event, timer, EVFILT_TIMER, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, duration.count() / 1000000, &timerContext); - - if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { - throw std::runtime_error("Timer::stop, kevent() failed, errno=" + std::to_string(errno)); - } - - context = &timerContext; + }; + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; assert(dispatcher != nullptr); assert(timerContext.context == dispatcher->getCurrentContext()); assert(context == &timerContext); diff --git a/src/Platform/OSX/System/Timer.h b/src/Platform/OSX/System/Timer.h index 75840bed..ecb9dd81 100755 --- a/src/Platform/OSX/System/Timer.h +++ b/src/Platform/OSX/System/Timer.h @@ -32,14 +32,11 @@ public: ~Timer(); Timer& operator=(const Timer&) = delete; Timer& operator=(Timer&& other); - void start(); - void stop(); void sleep(std::chrono::nanoseconds duration); private: Dispatcher* dispatcher; int timer; - bool stopped; void* context; }; diff --git a/src/Platform/Windows/System/Dispatcher.cpp b/src/Platform/Windows/System/Dispatcher.cpp index cb23e6a8..2fd59441 100755 --- a/src/Platform/Windows/System/Dispatcher.cpp +++ b/src/Platform/Windows/System/Dispatcher.cpp @@ -31,9 +31,11 @@ namespace System { namespace { struct DispatcherContext : public OVERLAPPED { - void* context; + NativeContext* context; }; +const size_t STACK_SIZE = 16384; +const size_t RESERVE_STACK_SIZE = 2097152; } Dispatcher::Dispatcher() { @@ -44,35 +46,40 @@ Dispatcher::Dispatcher() { if (ConvertThreadToFiberEx(NULL, 0) == NULL) { message = "ConvertThreadToFiberEx failed, result=" + std::to_string(GetLastError()); } else { - threadHandle = OpenThread(THREAD_SET_CONTEXT, FALSE, GetCurrentThreadId()); - if (threadHandle == NULL) { - message = "OpenThread failed, result=" + std::to_string(GetLastError()); + completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); + if (completionPort == NULL) { + message = "CreateIoCompletionPort failed, result=" + std::to_string(GetLastError()); } else { - completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); - if (completionPort == NULL) { - message = "CreateIoCompletionPort failed, result=" + std::to_string(GetLastError()); + WSADATA wsaData; + int wsaResult = WSAStartup(0x0202, &wsaData); + if (wsaResult != 0) { + message = "WSAStartup failed, result=" + std::to_string(wsaResult); } else { - WSADATA wsaData; - int wsaResult = WSAStartup(0x0202, &wsaData); - if (wsaResult != 0) { - message = "WSAStartup failed, result=" + std::to_string(wsaResult); - } else { - contextCount = 0; - remoteNotificationSent = false; - reinterpret_cast(remoteSpawnOverlapped)->hEvent = NULL; - threadId = GetCurrentThreadId(); - return; - } + remoteNotificationSent = false; + reinterpret_cast(remoteSpawnOverlapped)->hEvent = NULL; + threadId = GetCurrentThreadId(); - BOOL result = CloseHandle(completionPort); - assert(result == TRUE); + mainContext.fiber = GetCurrentFiber(); + mainContext.interrupted = false; + mainContext.group = &contextGroup; + mainContext.groupPrev = nullptr; + mainContext.groupNext = nullptr; + contextGroup.firstContext = nullptr; + contextGroup.lastContext = nullptr; + contextGroup.firstWaiter = nullptr; + contextGroup.lastWaiter = nullptr; + currentContext = &mainContext; + firstResumingContext = nullptr; + firstReusableContext = nullptr; + runningContextCount = 0; + return; } - BOOL result = CloseHandle(threadHandle); - assert(result == TRUE); + BOOL result2 = CloseHandle(completionPort); + assert(result2 == TRUE); } - BOOL result = ConvertFiberToThread(); + BOOL result2 = ConvertFiberToThread(); assert(result == TRUE); } @@ -81,21 +88,27 @@ Dispatcher::Dispatcher() { } Dispatcher::~Dispatcher() { - assert(resumingContexts.empty()); - assert(reusableContexts.size() == contextCount); - assert(spawningProcedures.empty()); assert(GetCurrentThreadId() == threadId); - while (!reusableContexts.empty()) { - DeleteFiber(reusableContexts.top()); - reusableContexts.pop(); + for (NativeContext* context = contextGroup.firstContext; context != nullptr; context = context->groupNext) { + interrupt(context); + } + + yield(); + assert(timers.empty()); + assert(contextGroup.firstContext == nullptr); + assert(contextGroup.firstWaiter == nullptr); + assert(firstResumingContext == nullptr); + assert(runningContextCount == 0); + while (firstReusableContext != nullptr) { + void* fiber = firstReusableContext->fiber; + firstReusableContext = firstReusableContext->next; + DeleteFiber(fiber); } int wsaResult = WSACleanup(); assert(wsaResult == 0); BOOL result = CloseHandle(completionPort); assert(result == TRUE); - result = CloseHandle(threadHandle); - assert(result == TRUE); result = ConvertFiberToThread(); assert(result == TRUE); DeleteCriticalSection(reinterpret_cast(criticalSection)); @@ -103,20 +116,20 @@ Dispatcher::~Dispatcher() { void Dispatcher::clear() { assert(GetCurrentThreadId() == threadId); - while (!reusableContexts.empty()) { - DeleteFiber(reusableContexts.top()); - --contextCount; - reusableContexts.pop(); + while (firstReusableContext != nullptr) { + void* fiber = firstReusableContext->fiber; + firstReusableContext = firstReusableContext->next; + DeleteFiber(fiber); } } void Dispatcher::dispatch() { assert(GetCurrentThreadId() == threadId); - void* context; + NativeContext* context; for (;;) { - if (!resumingContexts.empty()) { - context = resumingContexts.front(); - resumingContexts.pop(); + if (firstResumingContext != nullptr) { + context = firstResumingContext; + firstResumingContext = context->next; break; } @@ -128,16 +141,16 @@ void Dispatcher::dispatch() { auto timerContextPair = timers.begin(); auto end = timers.end(); while (timerContextPair != end && timerContextPair->first <= currentTime) { - resumingContexts.push(timerContextPair->second); + pushContext(timerContextPair->second); timerContextPair = timers.erase(timerContextPair); } - if (!resumingContexts.empty()) { - context = resumingContexts.front(); - resumingContexts.pop(); + if (firstResumingContext != nullptr) { + context = firstResumingContext; + firstResumingContext = context->next; break; } - + DWORD timeout = timers.empty() ? INFINITE : static_cast(std::min(timers.begin()->first - currentTime, static_cast(INFINITE - 1))); OVERLAPPED_ENTRY entry; ULONG actual = 0; @@ -170,19 +183,55 @@ void Dispatcher::dispatch() { } } - if (context != GetCurrentFiber()) { - SwitchToFiber(context); + if (context != currentContext) { + currentContext = context; + SwitchToFiber(context->fiber); } } -void* Dispatcher::getCurrentContext() const { +NativeContext* Dispatcher::getCurrentContext() const { assert(GetCurrentThreadId() == threadId); - return GetCurrentFiber(); + return currentContext; } -void Dispatcher::pushContext(void* context) { +void Dispatcher::interrupt() { + interrupt(currentContext); +} + +void Dispatcher::interrupt(NativeContext* context) { assert(GetCurrentThreadId() == threadId); - resumingContexts.push(context); + assert(context != nullptr); + if (!context->interrupted) { + if (context->interruptProcedure != nullptr) { + context->interruptProcedure(); + context->interruptProcedure = nullptr; + } else { + context->interrupted = true; + } + } +} + +bool Dispatcher::interrupted() { + if (currentContext->interrupted) { + currentContext->interrupted = false; + return true; + } + + return false; +} + +void Dispatcher::pushContext(NativeContext* context) { + assert(GetCurrentThreadId() == threadId); + assert(context != nullptr); + context->next = nullptr; + if (firstResumingContext != nullptr) { + assert(lastResumingContext->next == nullptr); + lastResumingContext->next = context; + } else { + firstResumingContext = context; + } + + lastResumingContext = context; } void Dispatcher::remoteSpawn(std::function&& procedure) { @@ -201,21 +250,23 @@ void Dispatcher::remoteSpawn(std::function&& procedure) { void Dispatcher::spawn(std::function&& procedure) { assert(GetCurrentThreadId() == threadId); - void* context; - if (reusableContexts.empty()) { - context = CreateFiberEx(16384, 131072, 0, contextProcedureStatic, this); - if (context == NULL) { - throw std::runtime_error("Dispatcher::spawn, CreateFiberEx failed, result=" + std::to_string(GetLastError())); - } - - ++contextCount; + NativeContext* context = &getReusableContext(); + if (contextGroup.firstContext != nullptr) { + context->groupPrev = contextGroup.lastContext; + assert(contextGroup.lastContext->groupNext == nullptr); + contextGroup.lastContext->groupNext = context; } else { - context = reusableContexts.top(); - reusableContexts.pop(); + context->groupPrev = nullptr; + contextGroup.firstContext = context; + contextGroup.firstWaiter = nullptr; } - resumingContexts.push(context); - spawningProcedures.emplace(std::move(procedure)); + context->interrupted = false; + context->group = &contextGroup; + context->groupNext = nullptr; + context->procedure = std::move(procedure); + contextGroup.lastContext = context; + pushContext(context); } void Dispatcher::yield() { @@ -229,7 +280,8 @@ void Dispatcher::yield() { auto timerContextPair = timers.begin(); auto end = timers.end(); while (timerContextPair != end && timerContextPair->first <= currentTime) { - resumingContexts.push(timerContextPair->second); + timerContextPair->second->interruptProcedure = nullptr; + pushContext(timerContextPair->second); timerContextPair = timers.erase(timerContextPair); } @@ -252,26 +304,27 @@ void Dispatcher::yield() { continue; } - void* context = reinterpret_cast(entries[i].lpOverlapped)->context; - resumingContexts.push(context); + NativeContext* context = reinterpret_cast(entries[i].lpOverlapped)->context; + context->interruptProcedure = nullptr; + pushContext(context); } } else { DWORD lastError = GetLastError(); if (lastError == WAIT_TIMEOUT) { break; } else if (lastError != WAIT_IO_COMPLETION) { - throw std::runtime_error("Dispatcher::dispatch, GetQueuedCompletionStatusEx failed, result=" + std::to_string(lastError)); + throw std::runtime_error("Dispatcher::yield, GetQueuedCompletionStatusEx failed, result=" + std::to_string(lastError)); } } } - if (!resumingContexts.empty()) { - resumingContexts.push(GetCurrentFiber()); + if (firstResumingContext != nullptr) { + pushContext(currentContext); dispatch(); } } -void Dispatcher::addTimer(uint64_t time, void* context) { +void Dispatcher::addTimer(uint64_t time, NativeContext* context) { assert(GetCurrentThreadId() == threadId); timers.insert(std::make_pair(time, context)); } @@ -280,12 +333,36 @@ void* Dispatcher::getCompletionPort() const { return completionPort; } -void Dispatcher::interruptTimer(uint64_t time, void* context) { +NativeContext& Dispatcher::getReusableContext() { + if (firstReusableContext == nullptr) { + void* fiber = CreateFiberEx(STACK_SIZE, RESERVE_STACK_SIZE, 0, contextProcedureStatic, this); + if (fiber == NULL) { + throw std::runtime_error("Dispatcher::getReusableContext, CreateFiberEx failed, result=" + std::to_string(GetLastError())); + } + + SwitchToFiber(fiber); + assert(firstReusableContext != nullptr); + firstReusableContext->fiber = fiber; + } + + NativeContext* context = firstReusableContext; + firstReusableContext = context->next; + return *context; +} + +void Dispatcher::pushReusableContext(NativeContext& context) { + context.next = firstReusableContext; + firstReusableContext = &context; + --runningContextCount; +} + +void Dispatcher::interruptTimer(uint64_t time, NativeContext* context) { assert(GetCurrentThreadId() == threadId); auto range = timers.equal_range(time); - for (auto it = range.first; it != range.second; ++it) { + for (auto it = range.first; ; ++it) { + assert(it != range.second); if (it->second == context) { - resumingContexts.push(context); + pushContext(context); timers.erase(it); break; } @@ -294,12 +371,55 @@ void Dispatcher::interruptTimer(uint64_t time, void* context) { void Dispatcher::contextProcedure() { assert(GetCurrentThreadId() == threadId); + assert(firstReusableContext == nullptr); + NativeContext context; + context.interrupted = false; + context.next = nullptr; + firstReusableContext = &context; + SwitchToFiber(currentContext->fiber); for (;;) { - assert(!spawningProcedures.empty()); - std::function procedure = std::move(spawningProcedures.front()); - spawningProcedures.pop(); - procedure(); - reusableContexts.push(GetCurrentFiber()); + ++runningContextCount; + try { + context.procedure(); + } catch (std::exception&) { + } + + if (context.group != nullptr) { + if (context.groupPrev != nullptr) { + assert(context.groupPrev->groupNext == &context); + context.groupPrev->groupNext = context.groupNext; + if (context.groupNext != nullptr) { + assert(context.groupNext->groupPrev == &context); + context.groupNext->groupPrev = context.groupPrev; + } else { + assert(context.group->lastContext == &context); + context.group->lastContext = context.groupPrev; + } + } else { + assert(context.group->firstContext == &context); + context.group->firstContext = context.groupNext; + if (context.groupNext != nullptr) { + assert(context.groupNext->groupPrev == &context); + context.groupNext->groupPrev = nullptr; + } else { + assert(context.group->lastContext == &context); + if (context.group->firstWaiter != nullptr) { + if (firstResumingContext != nullptr) { + assert(lastResumingContext->next == nullptr); + lastResumingContext->next = context.group->firstWaiter; + } else { + firstResumingContext = context.group->firstWaiter; + } + + lastResumingContext = context.group->lastWaiter; + context.group->firstWaiter = nullptr; + } + } + } + + pushReusableContext(context); + } + dispatch(); } } diff --git a/src/Platform/Windows/System/Dispatcher.h b/src/Platform/Windows/System/Dispatcher.h index 1d04809b..0d96f3e0 100755 --- a/src/Platform/Windows/System/Dispatcher.h +++ b/src/Platform/Windows/System/Dispatcher.h @@ -21,10 +21,29 @@ #include #include #include -#include namespace System { +struct NativeContextGroup; + +struct NativeContext { + void* fiber; + bool interrupted; + NativeContext* next; + NativeContextGroup* group; + NativeContext* groupPrev; + NativeContext* groupNext; + std::function procedure; + std::function interruptProcedure; +}; + +struct NativeContextGroup { + NativeContext* firstContext; + NativeContext* lastContext; + NativeContext* firstWaiter; + NativeContext* lastWaiter; +}; + class Dispatcher { public: Dispatcher(); @@ -33,30 +52,38 @@ public: Dispatcher& operator=(const Dispatcher&) = delete; void clear(); void dispatch(); - void* getCurrentContext() const; - void pushContext(void* context); + NativeContext* getCurrentContext() const; + void interrupt(); + void interrupt(NativeContext* context); + bool interrupted(); + void pushContext(NativeContext* context); void remoteSpawn(std::function&& procedure); - void spawn(std::function&& procedure); void yield(); // Platform-specific - void addTimer(uint64_t time, void* context); + void addTimer(uint64_t time, NativeContext* context); void* getCompletionPort() const; - void interruptTimer(uint64_t time, void* context); + NativeContext& getReusableContext(); + void pushReusableContext(NativeContext&); + void interruptTimer(uint64_t time, NativeContext* context); private: + void spawn(std::function&& procedure); void* completionPort; - std::size_t contextCount; uint8_t criticalSection[2 * sizeof(long) + 4 * sizeof(void*)]; - std::queue resumingContexts; bool remoteNotificationSent; std::queue> remoteSpawningProcedures; uint8_t remoteSpawnOverlapped[4 * sizeof(void*)]; - std::stack reusableContexts; - std::queue> spawningProcedures; - void* threadHandle; uint32_t threadId; - std::multimap timers; + std::multimap timers; + + NativeContext mainContext; + NativeContextGroup contextGroup; + NativeContext* currentContext; + NativeContext* firstResumingContext; + NativeContext* lastResumingContext; + NativeContext* firstReusableContext; + size_t runningContextCount; void contextProcedure(); static void __stdcall contextProcedureStatic(void* context); diff --git a/src/Platform/Windows/System/Ipv4Resolver.cpp b/src/Platform/Windows/System/Ipv4Resolver.cpp index 5d48d627..64f03e7a 100755 --- a/src/Platform/Windows/System/Ipv4Resolver.cpp +++ b/src/Platform/Windows/System/Ipv4Resolver.cpp @@ -22,6 +22,7 @@ #define WIN32_LEAN_AND_MEAN #endif #include +#include #include #include @@ -30,12 +31,11 @@ namespace System { Ipv4Resolver::Ipv4Resolver() : dispatcher(nullptr) { } -Ipv4Resolver::Ipv4Resolver(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false) { +Ipv4Resolver::Ipv4Resolver(Dispatcher& dispatcher) : dispatcher(&dispatcher) { } Ipv4Resolver::Ipv4Resolver(Ipv4Resolver&& other) : dispatcher(other.dispatcher) { if (dispatcher != nullptr) { - stopped = other.stopped; other.dispatcher = nullptr; } } @@ -46,28 +46,15 @@ Ipv4Resolver::~Ipv4Resolver() { Ipv4Resolver& Ipv4Resolver::operator=(Ipv4Resolver&& other) { dispatcher = other.dispatcher; if (dispatcher != nullptr) { - stopped = other.stopped; other.dispatcher = nullptr; } return *this; } -void Ipv4Resolver::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void Ipv4Resolver::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - stopped = true; -} - Ipv4Address Ipv4Resolver::resolve(const std::string& host) { assert(dispatcher != nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -78,15 +65,15 @@ Ipv4Address Ipv4Resolver::resolve(const std::string& host) { throw std::runtime_error("Ipv4Resolver::resolve, getaddrinfo failed, result=" + std::to_string(result)); } - std::size_t count = 0; + size_t count = 0; for (addrinfo* addressInfo = addressInfos; addressInfo != nullptr; addressInfo = addressInfo->ai_next) { ++count; } std::mt19937 generator{ std::random_device()() }; - std::size_t index = std::uniform_int_distribution(0, count - 1)(generator); + size_t index = std::uniform_int_distribution(0, count - 1)(generator); addrinfo* addressInfo = addressInfos; - for (std::size_t i = 0; i < index; ++i) { + for (size_t i = 0; i < index; ++i) { addressInfo = addressInfo->ai_next; } diff --git a/src/Platform/Windows/System/Ipv4Resolver.h b/src/Platform/Windows/System/Ipv4Resolver.h index d59d50e0..8f4a3fa6 100755 --- a/src/Platform/Windows/System/Ipv4Resolver.h +++ b/src/Platform/Windows/System/Ipv4Resolver.h @@ -33,13 +33,10 @@ public: ~Ipv4Resolver(); Ipv4Resolver& operator=(const Ipv4Resolver&) = delete; Ipv4Resolver& operator=(Ipv4Resolver&& other); - void start(); - void stop(); - Ipv4Address resolve(const std::string& host); + Ipv4Address resolve(const std::string& host); private: Dispatcher* dispatcher; - bool stopped; }; } diff --git a/src/Platform/Windows/System/TcpConnection.cpp b/src/Platform/Windows/System/TcpConnection.cpp index 927036d3..4a8ee621 100755 --- a/src/Platform/Windows/System/TcpConnection.cpp +++ b/src/Platform/Windows/System/TcpConnection.cpp @@ -26,14 +26,12 @@ #include #include "Dispatcher.h" -#pragma comment(lib, "Ws2_32.lib") - namespace System { namespace { struct TcpConnectionContext : public OVERLAPPED { - void* context; + NativeContext* context; bool interrupted; }; @@ -47,7 +45,6 @@ TcpConnection::TcpConnection(TcpConnection&& other) : dispatcher(other.dispatche assert(other.readContext == nullptr); assert(other.writeContext == nullptr); connection = other.connection; - stopped = other.stopped; readContext = nullptr; writeContext = nullptr; other.dispatcher = nullptr; @@ -77,7 +74,6 @@ TcpConnection& TcpConnection::operator=(TcpConnection&& other) { assert(other.readContext == nullptr); assert(other.writeContext == nullptr); connection = other.connection; - stopped = other.stopped; readContext = nullptr; writeContext = nullptr; other.dispatcher = nullptr; @@ -86,50 +82,10 @@ TcpConnection& TcpConnection::operator=(TcpConnection&& other) { return *this; } -void TcpConnection::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void TcpConnection::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (readContext != nullptr) { - TcpConnectionContext* context = static_cast(readContext); - if (!context->interrupted) { - if (CancelIoEx(reinterpret_cast(connection), context) != TRUE) { - DWORD lastError = GetLastError(); - if (lastError != ERROR_NOT_FOUND) { - throw std::runtime_error("TcpConnection::stop, CancelIoEx failed, result=" + std::to_string(GetLastError())); - } - } - - context->interrupted = true; - } - } - - if (writeContext != nullptr) { - TcpConnectionContext* context = static_cast(writeContext); - if (!context->interrupted) { - if (CancelIoEx(reinterpret_cast(connection), context) != TRUE) { - DWORD lastError = GetLastError(); - if (lastError != ERROR_NOT_FOUND) { - throw std::runtime_error("TcpConnection::stop, CancelIoEx failed, result=" + std::to_string(GetLastError())); - } - } - - context->interrupted = true; - } - } - - stopped = true; -} - size_t TcpConnection::read(uint8_t* data, size_t size) { assert(dispatcher != nullptr); assert(readContext == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -145,11 +101,30 @@ size_t TcpConnection::read(uint8_t* data, size_t size) { } assert(flags == 0); - context.context = GetCurrentFiber(); + context.context = dispatcher->getCurrentContext(); context.interrupted = false; readContext = &context; + dispatcher->getCurrentContext()->interruptProcedure = [&]() { + assert(dispatcher != nullptr); + assert(readContext != nullptr); + TcpConnectionContext* context = static_cast(readContext); + if (!context->interrupted) { + if (CancelIoEx(reinterpret_cast(connection), context) != TRUE) { + DWORD lastError = GetLastError(); + if (lastError != ERROR_NOT_FOUND) { + throw std::runtime_error("TcpConnection::stop, CancelIoEx failed, result=" + std::to_string(GetLastError())); + } + + context->context->interrupted = true; + } + + context->interrupted = true; + } + }; + dispatcher->dispatch(); - assert(context.context == GetCurrentFiber()); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; + assert(context.context == dispatcher->getCurrentContext()); assert(dispatcher != nullptr); assert(readContext == &context); readContext = nullptr; @@ -157,7 +132,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) { if (WSAGetOverlappedResult(connection, &context, &transferred, FALSE, &flags) != TRUE) { int lastError = WSAGetLastError(); if (lastError != ERROR_OPERATION_ABORTED) { - throw std::runtime_error("TcpConnection::read, WSARecv failed, result=" + std::to_string(lastError)); + throw std::runtime_error("TcpConnection::read, WSAGetOverlappedResult failed, result=" + std::to_string(lastError)); } assert(context.interrupted); @@ -169,10 +144,10 @@ size_t TcpConnection::read(uint8_t* data, size_t size) { return transferred; } -std::size_t TcpConnection::write(const uint8_t* data, size_t size) { +size_t TcpConnection::write(const uint8_t* data, size_t size) { assert(dispatcher != nullptr); assert(writeContext == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -194,11 +169,30 @@ std::size_t TcpConnection::write(const uint8_t* data, size_t size) { } } - context.context = GetCurrentFiber(); + context.context = dispatcher->getCurrentContext(); context.interrupted = false; writeContext = &context; + dispatcher->getCurrentContext()->interruptProcedure = [&]() { + assert(dispatcher != nullptr); + assert(writeContext != nullptr); + TcpConnectionContext* context = static_cast(writeContext); + if (!context->interrupted) { + if (CancelIoEx(reinterpret_cast(connection), context) != TRUE) { + DWORD lastError = GetLastError(); + if (lastError != ERROR_NOT_FOUND) { + throw std::runtime_error("TcpConnection::stop, CancelIoEx failed, result=" + std::to_string(GetLastError())); + } + + context->context->interrupted = true; + } + + context->interrupted = true; + } + }; + dispatcher->dispatch(); - assert(context.context == GetCurrentFiber()); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; + assert(context.context == dispatcher->getCurrentContext()); assert(dispatcher != nullptr); assert(writeContext == &context); writeContext = nullptr; @@ -207,7 +201,7 @@ std::size_t TcpConnection::write(const uint8_t* data, size_t size) { if (WSAGetOverlappedResult(connection, &context, &transferred, FALSE, &flags) != TRUE) { int lastError = WSAGetLastError(); if (lastError != ERROR_OPERATION_ABORTED) { - throw std::runtime_error("TcpConnection::write, WSASend failed, result=" + std::to_string(lastError)); + throw std::runtime_error("TcpConnection::write, WSAGetOverlappedResult failed, result=" + std::to_string(lastError)); } assert(context.interrupted); @@ -219,7 +213,7 @@ std::size_t TcpConnection::write(const uint8_t* data, size_t size) { return transferred; } -std::pair TcpConnection::getPeerAddressAndPort() { +std::pair TcpConnection::getPeerAddressAndPort() const { sockaddr_in address; int size = sizeof(address); if (getpeername(connection, reinterpret_cast(&address), &size) != 0) { @@ -230,7 +224,7 @@ std::pair TcpConnection::getPeerAddressAndPort() { return std::make_pair(Ipv4Address(htonl(address.sin_addr.S_un.S_addr)), htons(address.sin_port)); } -TcpConnection::TcpConnection(Dispatcher& dispatcher, std::size_t connection) : dispatcher(&dispatcher), connection(connection), stopped(false), readContext(nullptr), writeContext(nullptr) { +TcpConnection::TcpConnection(Dispatcher& dispatcher, size_t connection) : dispatcher(&dispatcher), connection(connection), readContext(nullptr), writeContext(nullptr) { } } diff --git a/src/Platform/Windows/System/TcpConnection.h b/src/Platform/Windows/System/TcpConnection.h index cf79c3b9..a6b0d4bd 100755 --- a/src/Platform/Windows/System/TcpConnection.h +++ b/src/Platform/Windows/System/TcpConnection.h @@ -33,23 +33,20 @@ public: ~TcpConnection(); TcpConnection& operator=(const TcpConnection&) = delete; TcpConnection& operator=(TcpConnection&& other); - void start(); - void stop(); - std::size_t read(uint8_t* data, std::size_t size); - std::size_t write(const uint8_t* data, std::size_t size); - std::pair getPeerAddressAndPort(); + size_t read(uint8_t* data, size_t size); + size_t write(const uint8_t* data, size_t size); + std::pair getPeerAddressAndPort() const; private: friend class TcpConnector; friend class TcpListener; Dispatcher* dispatcher; - std::size_t connection; - bool stopped; + size_t connection; void* readContext; void* writeContext; - TcpConnection(Dispatcher& dispatcher, std::size_t connection); + TcpConnection(Dispatcher& dispatcher, size_t connection); }; } diff --git a/src/Platform/Windows/System/TcpConnector.cpp b/src/Platform/Windows/System/TcpConnector.cpp index dc4e8104..5a19ac44 100755 --- a/src/Platform/Windows/System/TcpConnector.cpp +++ b/src/Platform/Windows/System/TcpConnector.cpp @@ -32,8 +32,8 @@ namespace System { namespace { struct TcpConnectorContext : public OVERLAPPED { - void* context; - std::size_t connection; + NativeContext* context; + size_t connection; bool interrupted; }; @@ -44,13 +44,12 @@ LPFN_CONNECTEX connectEx = nullptr; TcpConnector::TcpConnector() : dispatcher(nullptr) { } -TcpConnector::TcpConnector(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr) { +TcpConnector::TcpConnector(Dispatcher& dispatcher) : dispatcher(&dispatcher), context(nullptr) { } TcpConnector::TcpConnector(TcpConnector&& other) : dispatcher(other.dispatcher) { if (dispatcher != nullptr) { assert(other.context == nullptr); - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -65,7 +64,6 @@ TcpConnector& TcpConnector::operator=(TcpConnector&& other) { dispatcher = other.dispatcher; if (dispatcher != nullptr) { assert(other.context == nullptr); - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -73,36 +71,10 @@ TcpConnector& TcpConnector::operator=(TcpConnector&& other) { return *this; } -void TcpConnector::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void TcpConnector::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (context != nullptr) { - TcpConnectorContext* context2 = static_cast(context); - if (!context2->interrupted) { - if (CancelIoEx(reinterpret_cast(context2->connection), context2) != TRUE) { - DWORD lastError = GetLastError(); - if (lastError != ERROR_NOT_FOUND) { - throw std::runtime_error("TcpConnector::stop, CancelIoEx failed, result=" + std::to_string(GetLastError())); - } - } - - context2->interrupted = true; - } - } - - stopped = true; -} - TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) { assert(dispatcher != nullptr); assert(context == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -140,12 +112,31 @@ TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) { if (lastError != WSA_IO_PENDING) { message = "ConnectEx failed, result=" + std::to_string(lastError); } else { - context2.context = GetCurrentFiber(); + context2.context = dispatcher->getCurrentContext(); context2.connection = connection; context2.interrupted = false; context = &context2; + dispatcher->getCurrentContext()->interruptProcedure = [&]() { + assert(dispatcher != nullptr); + assert(context != nullptr); + TcpConnectorContext* context2 = static_cast(context); + if (!context2->interrupted) { + if (CancelIoEx(reinterpret_cast(context2->connection), context2) != TRUE) { + DWORD lastError = GetLastError(); + if (lastError != ERROR_NOT_FOUND) { + throw std::runtime_error("TcpConnector::stop, CancelIoEx failed, result=" + std::to_string(GetLastError())); + } + + context2->context->interrupted = true; + } + + context2->interrupted = true; + } + }; + dispatcher->dispatch(); - assert(context2.context == GetCurrentFiber()); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; + assert(context2.context == dispatcher->getCurrentContext()); assert(context2.connection == connection); assert(dispatcher != nullptr); assert(context == &context2); diff --git a/src/Platform/Windows/System/TcpConnector.h b/src/Platform/Windows/System/TcpConnector.h index 562ddfed..18762ac5 100755 --- a/src/Platform/Windows/System/TcpConnector.h +++ b/src/Platform/Windows/System/TcpConnector.h @@ -35,13 +35,10 @@ public: ~TcpConnector(); TcpConnector& operator=(const TcpConnector&) = delete; TcpConnector& operator=(TcpConnector&& other); - void start(); - void stop(); TcpConnection connect(const Ipv4Address& address, uint16_t port); private: Dispatcher* dispatcher; - bool stopped; void* context; }; diff --git a/src/Platform/Windows/System/TcpListener.cpp b/src/Platform/Windows/System/TcpListener.cpp index d64b3106..3c546a59 100755 --- a/src/Platform/Windows/System/TcpListener.cpp +++ b/src/Platform/Windows/System/TcpListener.cpp @@ -32,7 +32,7 @@ namespace System { namespace { struct TcpListenerContext : public OVERLAPPED { - void* context; + NativeContext* context; bool interrupted; }; @@ -67,7 +67,6 @@ TcpListener::TcpListener(Dispatcher& dispatcher, const Ipv4Address& address, uin if (CreateIoCompletionPort(reinterpret_cast(listener), dispatcher.getCompletionPort(), 0, 0) != dispatcher.getCompletionPort()) { message = "CreateIoCompletionPort failed, result=" + std::to_string(GetLastError()); } else { - stopped = false; context = nullptr; return; } @@ -85,7 +84,6 @@ TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) { if (dispatcher != nullptr) { assert(other.context == nullptr); listener = other.listener; - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -111,7 +109,6 @@ TcpListener& TcpListener::operator=(TcpListener&& other) { if (dispatcher != nullptr) { assert(other.context == nullptr); listener = other.listener; - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -119,36 +116,10 @@ TcpListener& TcpListener::operator=(TcpListener&& other) { return *this; } -void TcpListener::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void TcpListener::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (context != nullptr) { - TcpListenerContext* context2 = static_cast(context); - if (!context2->interrupted) { - if (CancelIoEx(reinterpret_cast(listener), context2) != TRUE) { - DWORD lastError = GetLastError(); - if (lastError != ERROR_NOT_FOUND) { - throw std::runtime_error("TcpListener::stop, CancelIoEx failed, result=" + std::to_string(GetLastError())); - } - } - - context2->interrupted = true; - } - } - - stopped = true; -} - TcpConnection TcpListener::accept() { assert(dispatcher != nullptr); assert(context == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -168,11 +139,30 @@ TcpConnection TcpListener::accept() { if (lastError != WSA_IO_PENDING) { message = "AcceptEx failed, result=" + std::to_string(lastError); } else { - context2.context = GetCurrentFiber(); + context2.context = dispatcher->getCurrentContext(); context2.interrupted = false; context = &context2; + dispatcher->getCurrentContext()->interruptProcedure = [&]() { + assert(dispatcher != nullptr); + assert(context != nullptr); + TcpListenerContext* context2 = static_cast(context); + if (!context2->interrupted) { + if (CancelIoEx(reinterpret_cast(listener), context2) != TRUE) { + DWORD lastError = GetLastError(); + if (lastError != ERROR_NOT_FOUND) { + throw std::runtime_error("TcpListener::stop, CancelIoEx failed, result=" + std::to_string(GetLastError())); + } + + context2->context->interrupted = true; + } + + context2->interrupted = true; + } + }; + dispatcher->dispatch(); - assert(context2.context == GetCurrentFiber()); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; + assert(context2.context == dispatcher->getCurrentContext()); assert(dispatcher != nullptr); assert(context == &context2); context = nullptr; diff --git a/src/Platform/Windows/System/TcpListener.h b/src/Platform/Windows/System/TcpListener.h index 3ec2f65b..18886de3 100755 --- a/src/Platform/Windows/System/TcpListener.h +++ b/src/Platform/Windows/System/TcpListener.h @@ -35,14 +35,11 @@ public: ~TcpListener(); TcpListener& operator=(const TcpListener&) = delete; TcpListener& operator=(TcpListener&& other); - void start(); - void stop(); TcpConnection accept(); private: Dispatcher* dispatcher; - std::size_t listener; - bool stopped; + size_t listener; void* context; }; diff --git a/src/Platform/Windows/System/Timer.cpp b/src/Platform/Windows/System/Timer.cpp index bf3a195b..62bc7a5c 100755 --- a/src/Platform/Windows/System/Timer.cpp +++ b/src/Platform/Windows/System/Timer.cpp @@ -31,7 +31,7 @@ namespace { struct TimerContext { uint64_t time; - void* context; + NativeContext* context; bool interrupted; }; @@ -40,13 +40,12 @@ struct TimerContext { Timer::Timer() : dispatcher(nullptr) { } -Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr) { +Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), context(nullptr) { } Timer::Timer(Timer&& other) : dispatcher(other.dispatcher) { if (dispatcher != nullptr) { assert(other.context == nullptr); - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -61,7 +60,6 @@ Timer& Timer::operator=(Timer&& other) { dispatcher = other.dispatcher; if (dispatcher != nullptr) { assert(other.context == nullptr); - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -69,30 +67,10 @@ Timer& Timer::operator=(Timer&& other) { return *this; } -void Timer::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void Timer::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (context != nullptr) { - TimerContext* timerContext = static_cast(context); - if (!timerContext->interrupted) { - dispatcher->interruptTimer(timerContext->time, timerContext->context); - timerContext->interrupted = true; - } - } - - stopped = true; -} - void Timer::sleep(std::chrono::nanoseconds duration) { assert(dispatcher != nullptr); assert(context == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -102,12 +80,22 @@ void Timer::sleep(std::chrono::nanoseconds duration) { QueryPerformanceFrequency(&frequency); uint64_t currentTime = ticks.QuadPart / (frequency.QuadPart / 1000); uint64_t time = currentTime + duration.count() / 1000000; - void* fiber = GetCurrentFiber(); - TimerContext timerContext{ time, fiber, false }; + TimerContext timerContext{ time, dispatcher->getCurrentContext(), false }; context = &timerContext; - dispatcher->addTimer(time, fiber); + dispatcher->addTimer(time, dispatcher->getCurrentContext()); + dispatcher->getCurrentContext()->interruptProcedure = [&]() { + assert(dispatcher != nullptr); + assert(context != nullptr); + TimerContext* timerContext = static_cast(context); + if (!timerContext->interrupted) { + dispatcher->interruptTimer(timerContext->time, timerContext->context); + timerContext->interrupted = true; + } + }; + dispatcher->dispatch(); - assert(timerContext.context == GetCurrentFiber()); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; + assert(timerContext.context == dispatcher->getCurrentContext()); assert(dispatcher != nullptr); assert(context == &timerContext); context = nullptr; diff --git a/src/Platform/Windows/System/Timer.h b/src/Platform/Windows/System/Timer.h index cf2d19b6..66cf1841 100755 --- a/src/Platform/Windows/System/Timer.h +++ b/src/Platform/Windows/System/Timer.h @@ -32,13 +32,10 @@ public: ~Timer(); Timer& operator=(const Timer&) = delete; Timer& operator=(Timer&& other); - void start(); - void stop(); void sleep(std::chrono::nanoseconds duration); private: Dispatcher* dispatcher; - bool stopped; void* context; }; diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/Rpc/CoreRpcServerCommandsDefinitions.h old mode 100644 new mode 100755 similarity index 77% rename from src/rpc/core_rpc_server_commands_defs.h rename to src/Rpc/CoreRpcServerCommandsDefinitions.h index 04e070b7..1dfa6710 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/Rpc/CoreRpcServerCommandsDefinitions.h @@ -17,12 +17,12 @@ #pragma once -#include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/difficulty.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" +#include "CryptoNoteCore/Difficulty.h" #include "crypto/hash.h" -#include "serialization/SerializationOverloads.h" +#include "Serialization/SerializationOverloads.h" namespace CryptoNote { //----------------------------------------------- @@ -58,7 +58,7 @@ struct COMMAND_RPC_GET_HEIGHT { struct COMMAND_RPC_GET_BLOCKS_FAST { struct request { - std::list block_ids; //*first 10 blocks id goes sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ + std::vector block_ids; //*first 10 blocks id goes sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ void serialize(ISerializer &s) { serializeAsBinary(block_ids, "block_ids", s); @@ -66,7 +66,7 @@ struct COMMAND_RPC_GET_BLOCKS_FAST { }; struct response { - std::list blocks; + std::vector blocks; uint64_t start_height; uint64_t current_height; std::string status; @@ -82,7 +82,7 @@ struct COMMAND_RPC_GET_BLOCKS_FAST { //----------------------------------------------- struct COMMAND_RPC_GET_TRANSACTIONS { struct request { - std::list txs_hashes; + std::vector txs_hashes; void serialize(ISerializer &s) { KV_MEMBER(txs_hashes) @@ -90,8 +90,8 @@ struct COMMAND_RPC_GET_TRANSACTIONS { }; struct response { - std::list txs_as_hex; //transactions blobs as hex - std::list missed_tx; //not found transactions + std::vector txs_as_hex; //transactions blobs as hex + std::vector missed_tx; //not found transactions std::string status; void serialize(ISerializer &s) { @@ -104,8 +104,8 @@ struct COMMAND_RPC_GET_TRANSACTIONS { //----------------------------------------------- struct COMMAND_RPC_GET_POOL_CHANGES { struct request { - crypto::hash tailBlockId; - std::vector knownTxsIds; + Crypto::Hash tailBlockId; + std::vector knownTxsIds; void serialize(ISerializer &s) { KV_MEMBER(tailBlockId) @@ -115,8 +115,8 @@ struct COMMAND_RPC_GET_POOL_CHANGES { struct response { bool isTailBlockActual; - std::vector addedTxs; // Added transactions blobs - std::vector deletedTxsIds; // IDs of not found transactions + std::vector addedTxs; // Added transactions blobs + std::vector deletedTxsIds; // IDs of not found transactions std::string status; void serialize(ISerializer &s) { @@ -127,11 +127,38 @@ struct COMMAND_RPC_GET_POOL_CHANGES { } }; }; + +struct COMMAND_RPC_GET_POOL_CHANGES_LITE { + struct request { + Crypto::Hash tailBlockId; + std::vector knownTxsIds; + + void serialize(ISerializer &s) { + KV_MEMBER(tailBlockId) + serializeAsBinary(knownTxsIds, "knownTxsIds", s); + } + }; + + struct response { + bool isTailBlockActual; + std::vector addedTxs; // Added transactions blobs + std::vector deletedTxsIds; // IDs of not found transactions + std::string status; + + void serialize(ISerializer &s) { + KV_MEMBER(isTailBlockActual) + KV_MEMBER(addedTxs) + serializeAsBinary(deletedTxsIds, "deletedTxsIds", s); + KV_MEMBER(status) + } + }; +}; + //----------------------------------------------- struct COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES { struct request { - crypto::hash txid; + Crypto::Hash txid; void serialize(ISerializer &s) { KV_MEMBER(txid) @@ -162,13 +189,13 @@ struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request { #pragma pack(push, 1) struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_out_entry { uint64_t global_amount_index; - crypto::public_key out_key; + Crypto::PublicKey out_key; }; #pragma pack(pop) struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount { uint64_t amount; - std::list outs; + std::vector outs; void serialize(ISerializer &s) { KV_MEMBER(amount) @@ -280,7 +307,7 @@ struct COMMAND_RPC_STOP_DAEMON { // struct COMMAND_RPC_GETBLOCKCOUNT { - typedef std::list request; + typedef std::vector request; struct response { uint64_t count; @@ -313,7 +340,7 @@ struct COMMAND_RPC_GETBLOCKTEMPLATE { uint64_t difficulty; uint32_t height; uint64_t reserved_offset; - blobdata blocktemplate_blob; + std::string blocktemplate_blob; std::string status; void serialize(ISerializer &s) { @@ -343,7 +370,7 @@ struct COMMAND_RPC_SUBMITBLOCK { typedef STATUS_STRUCT response; }; -struct block_header_responce { +struct block_header_response { uint8_t major_version; uint8_t minor_version; uint64_t timestamp; @@ -373,7 +400,7 @@ struct block_header_responce { struct BLOCK_HEADER_RESPONSE { std::string status; - block_header_responce block_header; + block_header_response block_header; void serialize(ISerializer &s) { KV_MEMBER(block_header) @@ -413,7 +440,7 @@ struct COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT { struct COMMAND_RPC_QUERY_BLOCKS { struct request { - std::list block_ids; //*first 10 blocks id goes sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ + std::vector block_ids; //*first 10 blocks id goes sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ uint64_t timestamp; void serialize(ISerializer &s) { @@ -427,7 +454,7 @@ struct COMMAND_RPC_QUERY_BLOCKS { uint64_t start_height; uint64_t current_height; uint64_t full_offset; - std::list items; + std::vector items; void serialize(ISerializer &s) { KV_MEMBER(status) @@ -439,4 +466,32 @@ struct COMMAND_RPC_QUERY_BLOCKS { }; }; +struct COMMAND_RPC_QUERY_BLOCKS_LITE { + struct request { + std::vector blockIds; + uint64_t timestamp; + + void serialize(ISerializer &s) { + serializeAsBinary(blockIds, "block_ids", s); + KV_MEMBER(timestamp) + } + }; + + struct response { + std::string status; + uint64_t startHeight; + uint64_t currentHeight; + uint64_t fullOffset; + std::vector items; + + void serialize(ISerializer &s) { + KV_MEMBER(status) + KV_MEMBER(startHeight) + KV_MEMBER(currentHeight) + KV_MEMBER(fullOffset) + KV_MEMBER(items) + } + }; +}; + } diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/Rpc/CoreRpcServerErrorCodes.h old mode 100644 new mode 100755 similarity index 100% rename from src/rpc/core_rpc_server_error_codes.h rename to src/Rpc/CoreRpcServerErrorCodes.h diff --git a/src/rpc/HttpClient.cpp b/src/Rpc/HttpClient.cpp similarity index 84% rename from src/rpc/HttpClient.cpp rename to src/Rpc/HttpClient.cpp index 36b6f347..83ddf90e 100644 --- a/src/rpc/HttpClient.cpp +++ b/src/Rpc/HttpClient.cpp @@ -28,6 +28,12 @@ HttpClient::HttpClient(System::Dispatcher& dispatcher, const std::string& addres m_dispatcher(dispatcher), m_address(address), m_port(port) { } +HttpClient::~HttpClient() { + if (m_connected) { + disconnect(); + } +} + void HttpClient::request(const HttpRequest &req, HttpResponse &res) { if (!m_connected) { connect(); @@ -54,7 +60,18 @@ void HttpClient::connect() { void HttpClient::disconnect() { m_streamBuf.reset(); - m_connection = System::TcpConnection(); + try { + m_connection.write(nullptr, 0); //Socket shutdown. + } catch (std::exception&) { + //Ignoring possible exception. + } + + try { + m_connection = System::TcpConnection(); + } catch (std::exception&) { + //Ignoring possible exception. + } + m_connected = false; } diff --git a/src/rpc/HttpClient.h b/src/Rpc/HttpClient.h old mode 100644 new mode 100755 similarity index 97% rename from src/rpc/HttpClient.h rename to src/Rpc/HttpClient.h index 0e351d7f..7c1ee1ea --- a/src/rpc/HttpClient.h +++ b/src/Rpc/HttpClient.h @@ -24,7 +24,7 @@ #include #include -#include "serialization/SerializationTools.h" +#include "Serialization/SerializationTools.h" namespace CryptoNote { @@ -32,6 +32,7 @@ class HttpClient { public: HttpClient(System::Dispatcher& dispatcher, const std::string& address, uint16_t port); + ~HttpClient(); void request(const HttpRequest& req, HttpResponse& res); private: diff --git a/src/rpc/HttpServer.cpp b/src/Rpc/HttpServer.cpp old mode 100644 new mode 100755 similarity index 83% rename from src/rpc/HttpServer.cpp rename to src/Rpc/HttpServer.cpp index 9386fea1..a0b23043 --- a/src/rpc/HttpServer.cpp +++ b/src/Rpc/HttpServer.cpp @@ -28,30 +28,22 @@ using namespace Logging; namespace CryptoNote { HttpServer::HttpServer(System::Dispatcher& dispatcher, Logging::ILogger& log) - : m_dispatcher(dispatcher), logger(log, "HttpServer"), m_shutdownCompleteEvent(dispatcher) { + : m_dispatcher(dispatcher), workingContextGroup(dispatcher), logger(log, "HttpServer") { } void HttpServer::start(const std::string& address, uint16_t port) { m_listener = System::TcpListener(m_dispatcher, System::Ipv4Address(address), port); - ++m_spawnCount; - m_dispatcher.spawn(std::bind(&HttpServer::acceptLoop, this)); + workingContextGroup.spawn(std::bind(&HttpServer::acceptLoop, this)); } void HttpServer::stop() { - m_listener.stop(); - for (auto connPtr : m_connections) { - connPtr->stop(); - } - - if (m_spawnCount) { - m_shutdownCompleteEvent.wait(); - } + workingContextGroup.interrupt(); + workingContextGroup.wait(); } void HttpServer::acceptLoop() { try { - System::TcpConnection connection; bool accepted = false; @@ -74,8 +66,7 @@ void HttpServer::acceptLoop() { logger(DEBUGGING) << "Incoming connection from " << addr.first.toDottedDecimal() << ":" << addr.second; - ++m_spawnCount; - m_dispatcher.spawn(std::bind(&HttpServer::acceptLoop, this)); + workingContextGroup.spawn(std::bind(&HttpServer::acceptLoop, this)); System::TcpStreambuf streambuf(connection); std::iostream stream(&streambuf); @@ -90,6 +81,10 @@ void HttpServer::acceptLoop() { stream << resp; stream.flush(); + + if (stream.peek() == std::iostream::traits_type::eof()) { + break; + } } logger(DEBUGGING) << "Closing connection from " << addr.first.toDottedDecimal() << ":" << addr.second << " total=" << m_connections.size(); @@ -98,10 +93,6 @@ void HttpServer::acceptLoop() { } catch (std::exception& e) { logger(WARNING) << "Connection error: " << e.what(); } - - if (--m_spawnCount == 0) { - m_shutdownCompleteEvent.set(); - } } } diff --git a/src/rpc/HttpServer.h b/src/Rpc/HttpServer.h old mode 100644 new mode 100755 similarity index 95% rename from src/rpc/HttpServer.h rename to src/Rpc/HttpServer.h index 6139e090..ca5535b6 --- a/src/rpc/HttpServer.h +++ b/src/Rpc/HttpServer.h @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -51,10 +52,9 @@ private: void acceptLoop(); void connectionHandler(System::TcpConnection&& conn); + System::ContextGroup workingContextGroup; Logging::LoggerRef logger; System::TcpListener m_listener; - System::Event m_shutdownCompleteEvent; - size_t m_spawnCount = 0; std::unordered_set m_connections; }; diff --git a/src/rpc/JsonRpc.cpp b/src/Rpc/JsonRpc.cpp old mode 100644 new mode 100755 similarity index 98% rename from src/rpc/JsonRpc.cpp rename to src/Rpc/JsonRpc.cpp index f4567508..57a214e6 --- a/src/rpc/JsonRpc.cpp +++ b/src/Rpc/JsonRpc.cpp @@ -16,7 +16,7 @@ // along with Bytecoin. If not, see . #include "JsonRpc.h" -#include "rpc/HttpClient.h" +#include "Rpc/HttpClient.h" namespace CryptoNote { diff --git a/src/rpc/JsonRpc.h b/src/Rpc/JsonRpc.h old mode 100644 new mode 100755 similarity index 98% rename from src/rpc/JsonRpc.h rename to src/Rpc/JsonRpc.h index ee1ca60d..dc484041 --- a/src/rpc/JsonRpc.h +++ b/src/Rpc/JsonRpc.h @@ -21,8 +21,8 @@ #include #include -#include "serialization/ISerializer.h" -#include "serialization/SerializationTools.h" +#include "Serialization/ISerializer.h" +#include "Serialization/SerializationTools.h" #include namespace CryptoNote { diff --git a/src/rpc/RpcServer.cpp b/src/Rpc/RpcServer.cpp old mode 100644 new mode 100755 similarity index 78% rename from src/rpc/RpcServer.cpp rename to src/Rpc/RpcServer.cpp index 594b17fa..e372279b --- a/src/rpc/RpcServer.cpp +++ b/src/Rpc/RpcServer.cpp @@ -21,16 +21,22 @@ #include // CryptoNote -#include "cryptonote_core/cryptonote_core.h" -#include "cryptonote_core/miner.h" -#include "p2p/net_node.h" +#include "Common/StringTools.h" +#include "CryptoNoteCore/CryptoNoteTools.h" +#include "CryptoNoteCore/Core.h" +#include "CryptoNoteCore/IBlock.h" +#include "CryptoNoteCore/Miner.h" +#include "CryptoNoteCore/TransactionExtra.h" +#include "P2p/NetNode.h" -#include "core_rpc_server_error_codes.h" +#include "CoreRpcServerErrorCodes.h" #include "JsonRpc.h" #undef ERROR using namespace Logging; +using namespace Crypto; +using namespace Common; namespace CryptoNote { @@ -77,9 +83,11 @@ std::unordered_map RpcServer::s_handler // binary handlers { "/getblocks.bin", binMethod(&RpcServer::on_get_blocks) }, { "/queryblocks.bin", binMethod(&RpcServer::on_query_blocks) }, + { "/queryblockslite.bin", binMethod(&RpcServer::on_query_blocks_lite) }, { "/get_o_indexes.bin", binMethod(&RpcServer::on_get_indexes) }, { "/getrandom_outs.bin", binMethod(&RpcServer::on_get_random_outs) }, { "/get_pool_changes.bin", binMethod(&RpcServer::onGetPoolChanges) }, + { "/get_pool_changes_lite.bin", binMethod(&RpcServer::onGetPoolChangesLite) }, // json handlers { "/getinfo", jsonMethod(&RpcServer::on_get_info) }, @@ -94,7 +102,7 @@ std::unordered_map RpcServer::s_handler { "/json_rpc", std::bind(&RpcServer::processJsonRpcRequest, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) } }; -RpcServer::RpcServer(System::Dispatcher& dispatcher, Logging::ILogger& log, core& c, node_server& p2p) : +RpcServer::RpcServer(System::Dispatcher& dispatcher, Logging::ILogger& log, core& c, NodeServer& p2p) : HttpServer(dispatcher, log), logger(log, "RpcServer"), m_core(c), m_p2p(p2p) { } @@ -174,18 +182,35 @@ bool RpcServer::checkCoreReady() { // bool RpcServer::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res) { - - std::list>> bs; - if (!m_core.find_blockchain_supplement(req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) { + // TODO code duplication see InProcessNode::doGetNewBlocks() + if (req.block_ids.empty()) { res.status = "Failed"; return false; } - for (auto& b : bs) { + if (req.block_ids.back() != m_core.getBlockIdByHeight(0)) { + res.status = "Failed"; + return false; + } + + uint32_t totalBlockCount; + uint32_t startBlockIndex; + std::vector supplement = m_core.findBlockchainSupplement(req.block_ids, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT, totalBlockCount, startBlockIndex); + + res.current_height = totalBlockCount; + res.start_height = startBlockIndex; + + for (const auto& blockId : supplement) { + assert(m_core.have_block(blockId)); + auto completeBlock = m_core.getBlock(blockId); + assert(completeBlock != nullptr); + res.blocks.resize(res.blocks.size() + 1); - res.blocks.back().block = block_to_blob(b.first); - for (auto& t : b.second) { - res.blocks.back().txs.push_back(tx_to_blob(t)); + res.blocks.back().block = asString(toBinaryArray(completeBlock->getBlock())); + + res.blocks.back().txs.reserve(completeBlock->getTransactionCount()); + for (size_t i = 0; i < completeBlock->getTransactionCount(); ++i) { + res.blocks.back().txs.push_back(asString(toBinaryArray(completeBlock->getTransaction(i)))); } } @@ -196,11 +221,36 @@ bool RpcServer::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, C bool RpcServer::on_query_blocks(const COMMAND_RPC_QUERY_BLOCKS::request& req, COMMAND_RPC_QUERY_BLOCKS::response& res) { CHECK_CORE_READY(); - if (!m_core.queryBlocks(req.block_ids, req.timestamp, res.start_height, res.current_height, res.full_offset, res.items)) { + uint32_t startHeight; + uint32_t currentHeight; + uint32_t fullOffset; + + if (!m_core.queryBlocks(req.block_ids, req.timestamp, startHeight, currentHeight, fullOffset, res.items)) { res.status = "Failed to perform query"; return false; } + res.start_height = startHeight; + res.current_height = currentHeight; + res.full_offset = fullOffset; + res.status = CORE_RPC_STATUS_OK; + return true; +} + +bool RpcServer::on_query_blocks_lite(const COMMAND_RPC_QUERY_BLOCKS_LITE::request& req, COMMAND_RPC_QUERY_BLOCKS_LITE::response& res) { + CHECK_CORE_READY(); + + uint32_t startHeight; + uint32_t currentHeight; + uint32_t fullOffset; + if (!m_core.queryBlocksLite(req.blockIds, req.timestamp, startHeight, currentHeight, fullOffset, res.items)) { + res.status = "Failed to perform query"; + return false; + } + + res.startHeight = startHeight; + res.currentHeight = currentHeight; + res.fullOffset = fullOffset; res.status = CORE_RPC_STATUS_OK; return true; } @@ -208,11 +258,13 @@ bool RpcServer::on_query_blocks(const COMMAND_RPC_QUERY_BLOCKS::request& req, CO bool RpcServer::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res) { CHECK_CORE_READY(); - if (!m_core.get_tx_outputs_gindexs(req.txid, res.o_indexes)) { + std::vector outputIndexes; + if (!m_core.get_tx_outputs_gindexs(req.txid, outputIndexes)) { res.status = "Failed"; return true; } + res.o_indexes.assign(outputIndexes.begin(), outputIndexes.end()); res.status = CORE_RPC_STATUS_OK; logger(TRACE) << "COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES: [" << res.o_indexes.size() << "]"; return true; @@ -256,8 +308,8 @@ bool RpcServer::onGetPoolChanges(const COMMAND_RPC_GET_POOL_CHANGES::request& re rsp.isTailBlockActual = m_core.getPoolChanges(req.tailBlockId, req.knownTxsIds, addedTransactions, rsp.deletedTxsIds); if (rsp.isTailBlockActual) { for (auto& tx : addedTransactions) { - blobdata txBlob; - if (!CryptoNote::tx_to_blob(tx, txBlob)) { + BinaryArray txBlob; + if (!toBinaryArray(tx, txBlob)) { rsp.status = "Internal error"; break;; } @@ -270,21 +322,30 @@ bool RpcServer::onGetPoolChanges(const COMMAND_RPC_GET_POOL_CHANGES::request& re } +bool RpcServer::onGetPoolChangesLite(const COMMAND_RPC_GET_POOL_CHANGES_LITE::request& req, COMMAND_RPC_GET_POOL_CHANGES_LITE::response& rsp) { + CHECK_CORE_READY(); + + rsp.status = CORE_RPC_STATUS_OK; + rsp.isTailBlockActual = m_core.getPoolChangesLite(req.tailBlockId, req.knownTxsIds, rsp.addedTxs, rsp.deletedTxsIds); + + return true; +} + // // JSON handlers // bool RpcServer::on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res) { res.height = m_core.get_current_blockchain_height(); - res.difficulty = m_core.get_blockchain_storage().get_difficulty_for_next_block(); - res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase + res.difficulty = m_core.getNextBlockDifficulty(); + res.tx_count = m_core.get_blockchain_total_transactions() - res.height; //without coinbase res.tx_pool_size = m_core.get_pool_transactions_count(); - res.alt_blocks_count = m_core.get_blockchain_storage().get_alternative_blocks_count(); + res.alt_blocks_count = m_core.get_alternative_blocks_count(); uint64_t total_conn = m_p2p.get_connections_count(); res.outgoing_connections_count = m_p2p.get_outgoing_connections_count(); res.incoming_connections_count = total_conn - res.outgoing_connections_count; - res.white_peerlist_size = m_p2p.get_peerlist_manager().get_white_peers_count(); - res.grey_peerlist_size = m_p2p.get_peerlist_manager().get_gray_peers_count(); + res.white_peerlist_size = m_p2p.getPeerlistManager().get_white_peers_count(); + res.grey_peerlist_size = m_p2p.getPeerlistManager().get_gray_peers_count(); res.status = CORE_RPC_STATUS_OK; return true; } @@ -298,27 +359,26 @@ bool RpcServer::on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAN bool RpcServer::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res) { CHECK_CORE_READY(); - std::vector vh; + std::vector vh; for (const auto& tx_hex_str : req.txs_hashes) { - blobdata b; - if (!hexToBlob(tx_hex_str, b)) + BinaryArray b; + if (!fromHex(tx_hex_str, b)) { res.status = "Failed to parse hex representation of transaction hash"; return true; } - if (b.size() != sizeof(crypto::hash)) + if (b.size() != sizeof(Hash)) { res.status = "Failed, size of data mismatch"; } - vh.push_back(*reinterpret_cast(b.data())); + vh.push_back(*reinterpret_cast(b.data())); } - std::list missed_txs; + std::list missed_txs; std::list txs; m_core.getTransactions(vh, txs, missed_txs); for (auto& tx : txs) { - blobdata blob = t_serializable_object_to_blob(tx); - res.txs_as_hex.push_back(blobToHex(blob)); + res.txs_as_hex.push_back(toHex(toBinaryArray(tx))); } for (const auto& miss_tx : missed_txs) { @@ -332,8 +392,8 @@ bool RpcServer::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& bool RpcServer::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res) { CHECK_CORE_READY(); - std::string tx_blob; - if (!hexToBlob(req.tx_as_hex, tx_blob)) + BinaryArray tx_blob; + if (!fromHex(req.tx_as_hex, tx_blob)) { logger(INFO) << "[on_send_raw_tx]: Failed to parse tx from hexbuff: " << req.tx_as_hex; res.status = "Failed"; @@ -364,7 +424,7 @@ bool RpcServer::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMM NOTIFY_NEW_TRANSACTIONS::request r; - r.txs.push_back(tx_blob); + r.txs.push_back(asString(tx_blob)); m_core.get_protocol()->relay_transactions(r); //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes res.status = CORE_RPC_STATUS_OK; @@ -379,10 +439,7 @@ bool RpcServer::on_start_mining(const COMMAND_RPC_START_MINING::request& req, CO return true; } - boost::thread::attributes attrs; - attrs.set_stack_size(THREAD_STACK_SIZE); - - if (!m_core.get_miner().start(adr, static_cast(req.threads_count), attrs)) { + if (!m_core.get_miner().start(adr, static_cast(req.threads_count))) { res.status = "Failed, mining not started"; return true; } @@ -404,7 +461,7 @@ bool RpcServer::on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMM bool RpcServer::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res) { CHECK_CORE_READY(); if (m_core.currency().isTestnet()) { - m_p2p.send_stop_signal(); + m_p2p.sendStopSignal(); res.status = CORE_RPC_STATUS_OK; } else { res.status = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; @@ -427,15 +484,16 @@ bool RpcServer::on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, CO throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_PARAM, "Wrong parameters, expected height" }; } - uint64_t h = req[0]; - if (m_core.get_current_blockchain_height() <= h) { + uint32_t h = static_cast(req[0]); + Crypto::Hash blockId = m_core.getBlockIdByHeight(h); + if (blockId == NULL_HASH) { throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT, std::string("To big height: ") + std::to_string(h) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height()) }; } - res = Common::podToHex(m_core.getBlockIdByHeight(h)); + res = Common::podToHex(blockId); return true; } @@ -468,16 +526,16 @@ bool RpcServer::on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& } Block b = boost::value_initialized(); - CryptoNote::blobdata blob_reserve; + CryptoNote::BinaryArray blob_reserve; blob_reserve.resize(req.reserve_size, 0); if (!m_core.get_block_template(b, acc, res.difficulty, res.height, blob_reserve)) { logger(ERROR) << "Failed to create block template"; throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: failed to create block template" }; } - blobdata block_blob = t_serializable_object_to_blob(b); - crypto::public_key tx_pub_key = CryptoNote::get_tx_pub_key_from_extra(b.minerTx); - if (tx_pub_key == null_pkey) { + BinaryArray block_blob = toBinaryArray(b); + PublicKey tx_pub_key = CryptoNote::getTransactionPublicKeyFromExtra(b.baseTransaction.extra); + if (tx_pub_key == NULL_PUBLIC_KEY) { logger(ERROR) << "Failed to find tx pub key in coinbase extra"; throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: failed to find tx pub key in coinbase extra" }; } @@ -497,16 +555,15 @@ bool RpcServer::on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& res.reserved_offset = 0; } - res.blocktemplate_blob = blobToHex(block_blob); + res.blocktemplate_blob = toHex(block_blob); res.status = CORE_RPC_STATUS_OK; return true; } bool RpcServer::on_get_currency_id(const COMMAND_RPC_GET_CURRENCY_ID::request& /*req*/, COMMAND_RPC_GET_CURRENCY_ID::response& res) { - crypto::hash currencyId = m_core.currency().genesisBlockHash(); - blobdata blob = t_serializable_object_to_blob(currencyId); - res.currency_id_blob = blobToHex(blob); + Hash currencyId = m_core.currency().genesisBlockHash(); + res.currency_id_blob = Common::podToHex(currencyId); return true; } @@ -515,20 +572,14 @@ bool RpcServer::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMM throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_PARAM, "Wrong param" }; } - blobdata blockblob; - if (!hexToBlob(req[0], blockblob)) { + BinaryArray blockblob; + if (!fromHex(req[0], blockblob)) { throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB, "Wrong block blob" }; } block_verification_context bvc = boost::value_initialized(); - System::Event event(m_dispatcher); - auto resultFuture = std::async(std::launch::async, [this, &event, &bvc, &blockblob]{ - m_core.handle_incoming_block_blob(blockblob, bvc, true, true); - m_dispatcher.remoteSpawn([&event]() { event.set(); }); - }); - event.wait(); - resultFuture.get(); + m_core.handle_incoming_block_blob(blockblob, bvc, true, true); if (!bvc.m_added_to_main_chain) { throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED, "Block not accepted" }; @@ -542,47 +593,45 @@ bool RpcServer::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMM namespace { uint64_t get_block_reward(const Block& blk) { uint64_t reward = 0; - for (const TransactionOutput& out : blk.minerTx.vout) { + for (const TransactionOutput& out : blk.baseTransaction.outputs) { reward += out.amount; } return reward; } } -void RpcServer::fill_block_header_responce(const Block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_responce& responce) { +void RpcServer::fill_block_header_response(const Block& blk, bool orphan_status, uint64_t height, const Hash& hash, block_header_response& responce) { responce.major_version = blk.majorVersion; responce.minor_version = blk.minorVersion; responce.timestamp = blk.timestamp; - responce.prev_hash = Common::podToHex(blk.prevId); + responce.prev_hash = Common::podToHex(blk.previousBlockHash); responce.nonce = blk.nonce; responce.orphan_status = orphan_status; responce.height = height; responce.depth = m_core.get_current_blockchain_height() - height - 1; responce.hash = Common::podToHex(hash); - responce.difficulty = m_core.get_blockchain_storage().block_difficulty(height); + m_core.getBlockDifficulty(static_cast(height), responce.difficulty); responce.reward = get_block_reward(blk); } bool RpcServer::on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res) { - uint64_t last_block_height; - crypto::hash last_block_hash; + uint32_t last_block_height; + Hash last_block_hash; - if (!m_core.get_blockchain_top(last_block_height, last_block_hash)) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: can't get last block hash." }; - } + m_core.get_blockchain_top(last_block_height, last_block_hash); Block last_block; if (!m_core.getBlockByHash(last_block_hash, last_block)) { throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: can't get last block hash." }; } - fill_block_header_responce(last_block, false, last_block_height, last_block_hash, res.block_header); + fill_block_header_response(last_block, false, last_block_height, last_block_hash, res.block_header); res.status = CORE_RPC_STATUS_OK; return true; } bool RpcServer::on_get_block_header_by_hash(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response& res) { - crypto::hash block_hash; + Hash block_hash; if (!parse_hash256(req.hash, block_hash)) { throw JsonRpc::JsonRpcError{ @@ -597,14 +646,14 @@ bool RpcServer::on_get_block_header_by_hash(const COMMAND_RPC_GET_BLOCK_HEADER_B "Internal error: can't get block by hash. Hash = " + req.hash + '.' }; } - if (blk.minerTx.vin.front().type() != typeid(TransactionInputGenerate)) { + if (blk.baseTransaction.inputs.front().type() != typeid(BaseInput)) { throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: coinbase transaction in the block has the wrong type" }; } - uint64_t block_height = boost::get(blk.minerTx.vin.front()).height; - fill_block_header_responce(blk, false, block_height, block_hash, res.block_header); + uint64_t block_height = boost::get(blk.baseTransaction.inputs.front()).blockIndex; + fill_block_header_response(blk, false, block_height, block_hash, res.block_header); res.status = CORE_RPC_STATUS_OK; return true; } @@ -615,14 +664,14 @@ bool RpcServer::on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER std::string("To big height: ") + std::to_string(req.height) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height()) }; } - crypto::hash block_hash = m_core.getBlockIdByHeight(req.height); + Hash block_hash = m_core.getBlockIdByHeight(static_cast(req.height)); Block blk; if (!m_core.getBlockByHash(block_hash, blk)) { throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: can't get block by height. Height = " + std::to_string(req.height) + '.' }; } - fill_block_header_responce(blk, false, req.height, block_hash, res.block_header); + fill_block_header_response(blk, false, req.height, block_hash, res.block_header); res.status = CORE_RPC_STATUS_OK; return true; } diff --git a/src/rpc/RpcServer.h b/src/Rpc/RpcServer.h old mode 100644 new mode 100755 similarity index 88% rename from src/rpc/RpcServer.h rename to src/Rpc/RpcServer.h index 6c6ecc2a..d4567ff6 --- a/src/rpc/RpcServer.h +++ b/src/Rpc/RpcServer.h @@ -21,16 +21,16 @@ #include #include -#include "core_rpc_server_commands_defs.h" +#include "CoreRpcServerCommandsDefinitions.h" namespace CryptoNote { class core; -class node_server; +class NodeServer; class RpcServer : public HttpServer { public: - RpcServer(System::Dispatcher& dispatcher, Logging::ILogger& log, core& c, node_server& p2p); + RpcServer(System::Dispatcher& dispatcher, Logging::ILogger& log, core& c, NodeServer& p2p); typedef std::function HandlerFunction; @@ -46,9 +46,11 @@ private: // binary handlers bool on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res); bool on_query_blocks(const COMMAND_RPC_QUERY_BLOCKS::request& req, COMMAND_RPC_QUERY_BLOCKS::response& res); + bool on_query_blocks_lite(const COMMAND_RPC_QUERY_BLOCKS_LITE::request& req, COMMAND_RPC_QUERY_BLOCKS_LITE::response& res); bool on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res); bool on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res); bool onGetPoolChanges(const COMMAND_RPC_GET_POOL_CHANGES::request& req, COMMAND_RPC_GET_POOL_CHANGES::response& rsp); + bool onGetPoolChangesLite(const COMMAND_RPC_GET_POOL_CHANGES_LITE::request& req, COMMAND_RPC_GET_POOL_CHANGES_LITE::response& rsp); // json handlers bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res); @@ -69,11 +71,11 @@ private: bool on_get_block_header_by_hash(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response& res); bool on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response& res); - void fill_block_header_responce(const Block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_responce& responce); + void fill_block_header_response(const Block& blk, bool orphan_status, uint64_t height, const Crypto::Hash& hash, block_header_response& responce); Logging::LoggerRef logger; core& m_core; - node_server& m_p2p; + NodeServer& m_p2p; }; } diff --git a/src/rpc/RpcServerConfig.cpp b/src/Rpc/RpcServerConfig.cpp old mode 100644 new mode 100755 similarity index 96% rename from src/rpc/RpcServerConfig.cpp rename to src/Rpc/RpcServerConfig.cpp index 770d2ca5..b7276aaa --- a/src/rpc/RpcServerConfig.cpp +++ b/src/Rpc/RpcServerConfig.cpp @@ -16,8 +16,8 @@ // along with Bytecoin. If not, see . #include "RpcServerConfig.h" -#include "Common/command_line.h" -#include "cryptonote_config.h" +#include "Common/CommandLine.h" +#include "CryptoNoteConfig.h" namespace CryptoNote { diff --git a/src/rpc/RpcServerConfig.h b/src/Rpc/RpcServerConfig.h similarity index 100% rename from src/rpc/RpcServerConfig.h rename to src/Rpc/RpcServerConfig.h diff --git a/src/serialization/BinaryInputStreamSerializer.cpp b/src/Serialization/BinaryInputStreamSerializer.cpp similarity index 72% rename from src/serialization/BinaryInputStreamSerializer.cpp rename to src/Serialization/BinaryInputStreamSerializer.cpp index bcb80df8..10b70cc3 100644 --- a/src/serialization/BinaryInputStreamSerializer.cpp +++ b/src/Serialization/BinaryInputStreamSerializer.cpp @@ -16,54 +16,26 @@ // along with Bytecoin. If not, see . #include "BinaryInputStreamSerializer.h" -#include "SerializationOverloads.h" #include #include #include +#include +#include "SerializationOverloads.h" + +using namespace Common; + +namespace CryptoNote { namespace { -template::digits> -typename std::enable_if::value && std::is_unsigned::value, size_t>::type -readVarint(std::istream& s, T &i) { - size_t read = 0; - i = 0; - for (int shift = 0;; shift += 7) { - if (s.eof()) { - return read; - } - uint8_t byte = s.get(); - if (!s) { - throw std::runtime_error("Stream read error"); - } - ++read; - if (shift + 7 >= bits && byte >= 1 << (bits - shift)) { - throw std::runtime_error("Varint overflow"); - } - if (byte == 0 && shift != 0) { - throw std::runtime_error("Non-canonical varint representation"); - } - i |= static_cast(byte & 0x7f) << shift; - if ((byte & 0x80) == 0) { - break; - } - } - return read; -} - template -void readVarintAs(std::istream& s, T &i) { - StorageType v; - readVarint(s, v); - i = static_cast(v); +void readVarintAs(IInputStream& s, T &i) { + i = static_cast(readVarint(s)); } - } -namespace CryptoNote { - ISerializer::SerializerType BinaryInputStreamSerializer::type() const { return ISerializer::INPUT; } @@ -75,7 +47,7 @@ bool BinaryInputStreamSerializer::beginObject(Common::StringView name) { void BinaryInputStreamSerializer::endObject() { } -bool BinaryInputStreamSerializer::beginArray(std::size_t& size, Common::StringView name) { +bool BinaryInputStreamSerializer::beginArray(size_t& size, Common::StringView name) { readVarintAs(stream, size); return true; } @@ -88,6 +60,16 @@ bool BinaryInputStreamSerializer::operator()(uint8_t& value, Common::StringView return true; } +bool BinaryInputStreamSerializer::operator()(uint16_t& value, Common::StringView name) { + readVarint(stream, value); + return true; +} + +bool BinaryInputStreamSerializer::operator()(int16_t& value, Common::StringView name) { + readVarintAs(stream, value); + return true; +} + bool BinaryInputStreamSerializer::operator()(uint32_t& value, Common::StringView name) { readVarint(stream, value); return true; @@ -109,7 +91,7 @@ bool BinaryInputStreamSerializer::operator()(uint64_t& value, Common::StringView } bool BinaryInputStreamSerializer::operator()(bool& value, Common::StringView name) { - value = stream.get() != 0; + value = read(stream) != 0; return true; } @@ -130,7 +112,7 @@ bool BinaryInputStreamSerializer::operator()(std::string& value, Common::StringV return true; } -bool BinaryInputStreamSerializer::binary(void* value, std::size_t size, Common::StringView name) { +bool BinaryInputStreamSerializer::binary(void* value, size_t size, Common::StringView name) { checkedRead(static_cast(value), size); return true; } @@ -146,9 +128,7 @@ bool BinaryInputStreamSerializer::operator()(double& value, Common::StringView n } void BinaryInputStreamSerializer::checkedRead(char* buf, size_t size) { - if (stream.read(buf, size).gcount() != size) { - throw std::runtime_error("Stream read error"); - } + read(stream, buf, size); } } diff --git a/src/serialization/BinaryInputStreamSerializer.h b/src/Serialization/BinaryInputStreamSerializer.h similarity index 81% rename from src/serialization/BinaryInputStreamSerializer.h rename to src/Serialization/BinaryInputStreamSerializer.h index 002b8024..2bf2327c 100644 --- a/src/serialization/BinaryInputStreamSerializer.h +++ b/src/Serialization/BinaryInputStreamSerializer.h @@ -17,16 +17,15 @@ #pragma once +#include #include "ISerializer.h" #include "SerializationOverloads.h" -#include - namespace CryptoNote { class BinaryInputStreamSerializer : public ISerializer { public: - BinaryInputStreamSerializer(std::istream& strm) : stream(strm) {} + BinaryInputStreamSerializer(Common::IInputStream& strm) : stream(strm) {} virtual ~BinaryInputStreamSerializer() {} virtual ISerializer::SerializerType type() const; @@ -34,10 +33,12 @@ public: virtual bool beginObject(Common::StringView name) override; virtual void endObject() override; - virtual bool beginArray(std::size_t& size, Common::StringView name) override; + virtual bool beginArray(size_t& size, Common::StringView name) override; virtual void endArray() override; virtual bool operator()(uint8_t& value, Common::StringView name) override; + virtual bool operator()(int16_t& value, Common::StringView name) override; + virtual bool operator()(uint16_t& value, Common::StringView name) override; virtual bool operator()(int32_t& value, Common::StringView name) override; virtual bool operator()(uint32_t& value, Common::StringView name) override; virtual bool operator()(int64_t& value, Common::StringView name) override; @@ -45,7 +46,7 @@ public: virtual bool operator()(double& value, Common::StringView name) override; virtual bool operator()(bool& value, Common::StringView name) override; virtual bool operator()(std::string& value, Common::StringView name) override; - virtual bool binary(void* value, std::size_t size, Common::StringView name) override; + virtual bool binary(void* value, size_t size, Common::StringView name) override; virtual bool binary(std::string& value, Common::StringView name) override; template @@ -56,7 +57,7 @@ public: private: void checkedRead(char* buf, size_t size); - std::istream& stream; + Common::IInputStream& stream; }; } diff --git a/src/serialization/BinaryOutputStreamSerializer.cpp b/src/Serialization/BinaryOutputStreamSerializer.cpp similarity index 82% rename from src/serialization/BinaryOutputStreamSerializer.cpp rename to src/Serialization/BinaryOutputStreamSerializer.cpp index 9e9c3e6a..6cdb1e0c 100644 --- a/src/serialization/BinaryOutputStreamSerializer.cpp +++ b/src/Serialization/BinaryOutputStreamSerializer.cpp @@ -19,24 +19,9 @@ #include #include +#include "Common/StreamTools.h" -namespace { - -template -typename std::enable_if::value && std::is_unsigned::value, void>::type -writeVarint(std::ostream& s, T i) { - while (i >= 0x80) { - s.put((static_cast(i)& 0x7f) | 0x80); - i >>= 7; - } - s.put(static_cast(i)); - - if (!s) { - throw std::runtime_error("Stream write error"); - } -} - -} +using namespace Common; namespace CryptoNote { @@ -51,7 +36,7 @@ bool BinaryOutputStreamSerializer::beginObject(Common::StringView name) { void BinaryOutputStreamSerializer::endObject() { } -bool BinaryOutputStreamSerializer::beginArray(std::size_t& size, Common::StringView name) { +bool BinaryOutputStreamSerializer::beginArray(size_t& size, Common::StringView name) { writeVarint(stream, size); return true; } @@ -64,6 +49,16 @@ bool BinaryOutputStreamSerializer::operator()(uint8_t& value, Common::StringView return true; } +bool BinaryOutputStreamSerializer::operator()(uint16_t& value, Common::StringView name) { + writeVarint(stream, value); + return true; +} + +bool BinaryOutputStreamSerializer::operator()(int16_t& value, Common::StringView name) { + writeVarint(stream, static_cast(value)); + return true; +} + bool BinaryOutputStreamSerializer::operator()(uint32_t& value, Common::StringView name) { writeVarint(stream, value); return true; @@ -96,7 +91,7 @@ bool BinaryOutputStreamSerializer::operator()(std::string& value, Common::String return true; } -bool BinaryOutputStreamSerializer::binary(void* value, std::size_t size, Common::StringView name) { +bool BinaryOutputStreamSerializer::binary(void* value, size_t size, Common::StringView name) { checkedWrite(static_cast(value), size); return true; } @@ -113,10 +108,7 @@ bool BinaryOutputStreamSerializer::operator()(double& value, Common::StringView } void BinaryOutputStreamSerializer::checkedWrite(const char* buf, size_t size) { - stream.write(buf, size); - if (!stream) { - throw std::runtime_error("Stream write error"); - } + write(stream, buf, size); } } diff --git a/src/serialization/BinaryOutputStreamSerializer.h b/src/Serialization/BinaryOutputStreamSerializer.h similarity index 81% rename from src/serialization/BinaryOutputStreamSerializer.h rename to src/Serialization/BinaryOutputStreamSerializer.h index e1dc3500..5d585c77 100644 --- a/src/serialization/BinaryOutputStreamSerializer.h +++ b/src/Serialization/BinaryOutputStreamSerializer.h @@ -17,16 +17,15 @@ #pragma once +#include "Common/IOutputStream.h" #include "ISerializer.h" #include "SerializationOverloads.h" -#include - namespace CryptoNote { class BinaryOutputStreamSerializer : public ISerializer { public: - BinaryOutputStreamSerializer(std::ostream& strm) : stream(strm) {} + BinaryOutputStreamSerializer(Common::IOutputStream& strm) : stream(strm) {} virtual ~BinaryOutputStreamSerializer() {} virtual ISerializer::SerializerType type() const; @@ -34,10 +33,12 @@ public: virtual bool beginObject(Common::StringView name) override; virtual void endObject() override; - virtual bool beginArray(std::size_t& size, Common::StringView name) override; + virtual bool beginArray(size_t& size, Common::StringView name) override; virtual void endArray() override; virtual bool operator()(uint8_t& value, Common::StringView name) override; + virtual bool operator()(int16_t& value, Common::StringView name) override; + virtual bool operator()(uint16_t& value, Common::StringView name) override; virtual bool operator()(int32_t& value, Common::StringView name) override; virtual bool operator()(uint32_t& value, Common::StringView name) override; virtual bool operator()(int64_t& value, Common::StringView name) override; @@ -45,7 +46,7 @@ public: virtual bool operator()(double& value, Common::StringView name) override; virtual bool operator()(bool& value, Common::StringView name) override; virtual bool operator()(std::string& value, Common::StringView name) override; - virtual bool binary(void* value, std::size_t size, Common::StringView name) override; + virtual bool binary(void* value, size_t size, Common::StringView name) override; virtual bool binary(std::string& value, Common::StringView name) override; template @@ -55,7 +56,7 @@ public: private: void checkedWrite(const char* buf, size_t size); - std::ostream& stream; + Common::IOutputStream& stream; }; } diff --git a/src/Serialization/BinarySerializationTools.h b/src/Serialization/BinarySerializationTools.h new file mode 100644 index 00000000..e3273ccb --- /dev/null +++ b/src/Serialization/BinarySerializationTools.h @@ -0,0 +1,89 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include "BinaryInputStreamSerializer.h" +#include "BinaryOutputStreamSerializer.h" +#include "Common/MemoryInputStream.h" +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "Common/VectorOutputStream.h" + +#include + +namespace CryptoNote { + +template +BinaryArray storeToBinary(const T& obj) { + BinaryArray result; + Common::VectorOutputStream stream(result); + BinaryOutputStreamSerializer ba(stream); + serialize(const_cast(obj), ba); + return result; +} + +template +void loadFromBinary(T& obj, const BinaryArray& blob) { + Common::MemoryInputStream stream(blob.data(), blob.size()); + BinaryInputStreamSerializer ba(stream); + serialize(obj, ba); +} + +template +bool storeToBinaryFile(const T& obj, const std::string& filename) { + try { + std::ofstream dataFile; + dataFile.open(filename, std::ios_base::binary | std::ios_base::out | std::ios::trunc); + if (dataFile.fail()) { + return false; + } + + Common::StdOutputStream stream(dataFile); + BinaryOutputStreamSerializer out(stream); + CryptoNote::serialize(const_cast(obj), out); + + if (dataFile.fail()) { + return false; + } + + dataFile.flush(); + } catch (std::exception&) { + return false; + } + + return true; +} + +template +bool loadFromBinaryFile(T& obj, const std::string& filename) { + try { + std::ifstream dataFile; + dataFile.open(filename, std::ios_base::binary | std::ios_base::in); + if (dataFile.fail()) { + return false; + } + + Common::StdInputStream stream(dataFile); + BinaryInputStreamSerializer in(stream); + serialize(obj, in); + return !dataFile.fail(); + } catch (std::exception&) { + return false; + } +} + +} diff --git a/src/serialization/ISerializer.h b/src/Serialization/ISerializer.h similarity index 83% rename from src/serialization/ISerializer.h rename to src/Serialization/ISerializer.h index f7c5c5f0..d199e489 100644 --- a/src/serialization/ISerializer.h +++ b/src/Serialization/ISerializer.h @@ -38,10 +38,12 @@ public: virtual bool beginObject(Common::StringView name) = 0; virtual void endObject() = 0; - virtual bool beginArray(std::size_t& size, Common::StringView name) = 0; + virtual bool beginArray(size_t& size, Common::StringView name) = 0; virtual void endArray() = 0; virtual bool operator()(uint8_t& value, Common::StringView name) = 0; + virtual bool operator()(int16_t& value, Common::StringView name) = 0; + virtual bool operator()(uint16_t& value, Common::StringView name) = 0; virtual bool operator()(int32_t& value, Common::StringView name) = 0; virtual bool operator()(uint32_t& value, Common::StringView name) = 0; virtual bool operator()(int64_t& value, Common::StringView name) = 0; @@ -51,7 +53,7 @@ public: virtual bool operator()(std::string& value, Common::StringView name) = 0; // read/write binary block - virtual bool binary(void* value, std::size_t size, Common::StringView name) = 0; + virtual bool binary(void* value, size_t size, Common::StringView name) = 0; virtual bool binary(std::string& value, Common::StringView name) = 0; template @@ -79,6 +81,13 @@ void serialize(T& value, ISerializer& serializer) { value.serialize(serializer); } +#ifdef __clang__ +template<> inline +bool ISerializer::operator()(size_t& value, Common::StringView name) { + return operator()(*reinterpret_cast(&value), name); +} +#endif + #define KV_MEMBER(member) s(member, #member); } diff --git a/src/serialization/IStream.h b/src/Serialization/IStream.h similarity index 88% rename from src/serialization/IStream.h rename to src/Serialization/IStream.h index c979ac46..1cfe1949 100644 --- a/src/serialization/IStream.h +++ b/src/Serialization/IStream.h @@ -24,12 +24,12 @@ namespace CryptoNote { class IInputStream { public: - virtual std::size_t read(char* data, std::size_t size) = 0; + virtual size_t read(char* data, size_t size) = 0; }; class IOutputStream { public: - virtual void write(const char* data, std::size_t size) = 0; + virtual void write(const char* data, size_t size) = 0; }; } diff --git a/src/serialization/JsonInputStreamSerializer.cpp b/src/Serialization/JsonInputStreamSerializer.cpp similarity index 77% rename from src/serialization/JsonInputStreamSerializer.cpp rename to src/Serialization/JsonInputStreamSerializer.cpp index 8cb6efe1..ed6192e0 100644 --- a/src/serialization/JsonInputStreamSerializer.cpp +++ b/src/Serialization/JsonInputStreamSerializer.cpp @@ -15,15 +15,24 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "serialization/JsonInputStreamSerializer.h" +#include "Serialization/JsonInputStreamSerializer.h" #include #include namespace CryptoNote { -JsonInputStreamSerializer::JsonInputStreamSerializer(std::istream& stream) : JsonInputValueSerializer(root) { - stream >> root; +namespace { + +Common::JsonValue getJsonValueFromStreamHelper(std::istream& stream) { + Common::JsonValue value; + stream >> value; + return value; +} + +} + +JsonInputStreamSerializer::JsonInputStreamSerializer(std::istream& stream) : JsonInputValueSerializer(getJsonValueFromStreamHelper(stream)) { } JsonInputStreamSerializer::~JsonInputStreamSerializer() { diff --git a/src/serialization/JsonInputStreamSerializer.h b/src/Serialization/JsonInputStreamSerializer.h similarity index 96% rename from src/serialization/JsonInputStreamSerializer.h rename to src/Serialization/JsonInputStreamSerializer.h index ce8ea945..fdaf04fc 100644 --- a/src/serialization/JsonInputStreamSerializer.h +++ b/src/Serialization/JsonInputStreamSerializer.h @@ -30,9 +30,6 @@ class JsonInputStreamSerializer : public JsonInputValueSerializer { public: JsonInputStreamSerializer(std::istream& stream); virtual ~JsonInputStreamSerializer(); - -private: - Common::JsonValue root; }; } diff --git a/src/serialization/JsonInputValueSerializer.cpp b/src/Serialization/JsonInputValueSerializer.cpp similarity index 79% rename from src/serialization/JsonInputValueSerializer.cpp rename to src/Serialization/JsonInputValueSerializer.cpp index 35b282dc..e8f28913 100644 --- a/src/serialization/JsonInputValueSerializer.cpp +++ b/src/Serialization/JsonInputValueSerializer.cpp @@ -16,15 +16,29 @@ // along with Bytecoin. If not, see . #include "JsonInputValueSerializer.h" + #include #include -#include + +#include "Common/StringTools.h" using Common::JsonValue; using namespace CryptoNote; -JsonInputValueSerializer::JsonInputValueSerializer(const Common::JsonValue& value) : root(value) { - chain.push_back(&root); +JsonInputValueSerializer::JsonInputValueSerializer(const Common::JsonValue& value) { + if (!value.isObject()) { + throw std::runtime_error("Serializer doesn't support this type of serialization: Object expected."); + } + + chain.push_back(&value); +} + +JsonInputValueSerializer::JsonInputValueSerializer(Common::JsonValue&& value) : value(std::move(value)) { + if (!this->value.isObject()) { + throw std::runtime_error("Serializer doesn't support this type of serialization: Object expected."); + } + + chain.push_back(&this->value); } JsonInputValueSerializer::~JsonInputValueSerializer() { @@ -50,14 +64,6 @@ bool JsonInputValueSerializer::beginObject(Common::StringView name) { } return false; - - //if (parent->isArray()) { - // const JsonValue& v = (*parent)[idxs.back()++]; - // chain.push_back(&v); - //} else { - // const JsonValue& v = (*parent)(name); - // chain.push_back(&v); - //} } void JsonInputValueSerializer::endObject() { @@ -65,7 +71,7 @@ void JsonInputValueSerializer::endObject() { chain.pop_back(); } -bool JsonInputValueSerializer::beginArray(std::size_t& size, Common::StringView name) { +bool JsonInputValueSerializer::beginArray(size_t& size, Common::StringView name) { const JsonValue* parent = chain.back(); std::string strName(name); @@ -89,6 +95,14 @@ void JsonInputValueSerializer::endArray() { idxs.pop_back(); } +bool JsonInputValueSerializer::operator()(uint16_t& value, Common::StringView name) { + return getNumber(name, value); +} + +bool JsonInputValueSerializer::operator()(int16_t& value, Common::StringView name) { + return getNumber(name, value); +} + bool JsonInputValueSerializer::operator()(uint32_t& value, Common::StringView name) { return getNumber(name, value); } @@ -115,7 +129,7 @@ bool JsonInputValueSerializer::operator()(uint8_t& value, Common::StringView nam bool JsonInputValueSerializer::operator()(std::string& value, Common::StringView name) { auto ptr = getValue(name); - if (!ptr) { + if (ptr == nullptr) { return false; } value = ptr->getString(); @@ -124,14 +138,14 @@ bool JsonInputValueSerializer::operator()(std::string& value, Common::StringView bool JsonInputValueSerializer::operator()(bool& value, Common::StringView name) { auto ptr = getValue(name); - if (!ptr) { + if (ptr == nullptr) { return false; } value = ptr->getBool(); return true; } -bool JsonInputValueSerializer::binary(void* value, std::size_t size, Common::StringView name) { +bool JsonInputValueSerializer::binary(void* value, size_t size, Common::StringView name) { auto ptr = getValue(name); if (ptr == nullptr) { return false; diff --git a/src/serialization/JsonInputValueSerializer.h b/src/Serialization/JsonInputValueSerializer.h similarity index 84% rename from src/serialization/JsonInputValueSerializer.h rename to src/Serialization/JsonInputValueSerializer.h index e211b700..232e4f81 100644 --- a/src/serialization/JsonInputValueSerializer.h +++ b/src/Serialization/JsonInputValueSerializer.h @@ -17,7 +17,7 @@ #pragma once -#include "../Common/JsonValue.h" +#include "Common/JsonValue.h" #include "ISerializer.h" namespace CryptoNote { @@ -26,6 +26,7 @@ namespace CryptoNote { class JsonInputValueSerializer : public ISerializer { public: JsonInputValueSerializer(const Common::JsonValue& value); + JsonInputValueSerializer(Common::JsonValue&& value); virtual ~JsonInputValueSerializer(); SerializerType type() const; @@ -33,10 +34,12 @@ public: virtual bool beginObject(Common::StringView name) override; virtual void endObject() override; - virtual bool beginArray(std::size_t& size, Common::StringView name) override; + virtual bool beginArray(size_t& size, Common::StringView name) override; virtual void endArray() override; virtual bool operator()(uint8_t& value, Common::StringView name) override; + virtual bool operator()(int16_t& value, Common::StringView name) override; + virtual bool operator()(uint16_t& value, Common::StringView name) override; virtual bool operator()(int32_t& value, Common::StringView name) override; virtual bool operator()(uint32_t& value, Common::StringView name) override; virtual bool operator()(int64_t& value, Common::StringView name) override; @@ -44,7 +47,7 @@ public: virtual bool operator()(double& value, Common::StringView name) override; virtual bool operator()(bool& value, Common::StringView name) override; virtual bool operator()(std::string& value, Common::StringView name) override; - virtual bool binary(void* value, std::size_t size, Common::StringView name) override; + virtual bool binary(void* value, size_t size, Common::StringView name) override; virtual bool binary(std::string& value, Common::StringView name) override; template @@ -53,7 +56,7 @@ public: } private: - const Common::JsonValue& root; + Common::JsonValue value; std::vector chain; std::vector idxs; diff --git a/src/serialization/JsonOutputStreamSerializer.cpp b/src/Serialization/JsonOutputStreamSerializer.cpp similarity index 88% rename from src/serialization/JsonOutputStreamSerializer.cpp rename to src/Serialization/JsonOutputStreamSerializer.cpp index 3224d479..ed33e46c 100644 --- a/src/serialization/JsonOutputStreamSerializer.cpp +++ b/src/Serialization/JsonOutputStreamSerializer.cpp @@ -72,7 +72,7 @@ void JsonOutputStreamSerializer::endObject() { chain.pop_back(); } -bool JsonOutputStreamSerializer::beginArray(std::size_t& size, Common::StringView name) { +bool JsonOutputStreamSerializer::beginArray(size_t& size, Common::StringView name) { JsonValue val(JsonValue::ARRAY); JsonValue& res = chain.back()->insert(std::string(name), val); chain.push_back(&res); @@ -89,6 +89,16 @@ bool JsonOutputStreamSerializer::operator()(uint64_t& value, Common::StringView return operator()(v, name); } +bool JsonOutputStreamSerializer::operator()(uint16_t& value, Common::StringView name) { + uint64_t v = static_cast(value); + return operator()(v, name); +} + +bool JsonOutputStreamSerializer::operator()(int16_t& value, Common::StringView name) { + int64_t v = static_cast(value); + return operator()(v, name); +} + bool JsonOutputStreamSerializer::operator()(uint32_t& value, Common::StringView name) { uint64_t v = static_cast(value); return operator()(v, name); @@ -124,7 +134,7 @@ bool JsonOutputStreamSerializer::operator()(bool& value, Common::StringView name return true; } -bool JsonOutputStreamSerializer::binary(void* value, std::size_t size, Common::StringView name) { +bool JsonOutputStreamSerializer::binary(void* value, size_t size, Common::StringView name) { std::string hex = Common::toHex(value, size); return (*this)(hex, name); } diff --git a/src/serialization/JsonOutputStreamSerializer.h b/src/Serialization/JsonOutputStreamSerializer.h similarity index 87% rename from src/serialization/JsonOutputStreamSerializer.h rename to src/Serialization/JsonOutputStreamSerializer.h index bea003d9..c86e8d67 100644 --- a/src/serialization/JsonOutputStreamSerializer.h +++ b/src/Serialization/JsonOutputStreamSerializer.h @@ -33,10 +33,12 @@ public: virtual bool beginObject(Common::StringView name) override; virtual void endObject() override; - virtual bool beginArray(std::size_t& size, Common::StringView name) override; + virtual bool beginArray(size_t& size, Common::StringView name) override; virtual void endArray() override; virtual bool operator()(uint8_t& value, Common::StringView name) override; + virtual bool operator()(int16_t& value, Common::StringView name) override; + virtual bool operator()(uint16_t& value, Common::StringView name) override; virtual bool operator()(int32_t& value, Common::StringView name) override; virtual bool operator()(uint32_t& value, Common::StringView name) override; virtual bool operator()(int64_t& value, Common::StringView name) override; @@ -44,7 +46,7 @@ public: virtual bool operator()(double& value, Common::StringView name) override; virtual bool operator()(bool& value, Common::StringView name) override; virtual bool operator()(std::string& value, Common::StringView name) override; - virtual bool binary(void* value, std::size_t size, Common::StringView name) override; + virtual bool binary(void* value, size_t size, Common::StringView name) override; virtual bool binary(std::string& value, Common::StringView name) override; template diff --git a/src/serialization/KVBinaryCommon.h b/src/Serialization/KVBinaryCommon.h similarity index 100% rename from src/serialization/KVBinaryCommon.h rename to src/Serialization/KVBinaryCommon.h diff --git a/src/serialization/KVBinaryInputStreamSerializer.cpp b/src/Serialization/KVBinaryInputStreamSerializer.cpp similarity index 69% rename from src/serialization/KVBinaryInputStreamSerializer.cpp rename to src/Serialization/KVBinaryInputStreamSerializer.cpp index f6e27208..fb8f7689 100644 --- a/src/serialization/KVBinaryInputStreamSerializer.cpp +++ b/src/Serialization/KVBinaryInputStreamSerializer.cpp @@ -16,96 +16,98 @@ // along with Bytecoin. If not, see . #include "KVBinaryInputStreamSerializer.h" + #include #include #include #include +#include #include "KVBinaryCommon.h" -using Common::JsonValue; +using namespace Common; using namespace CryptoNote; namespace { -void strictRead(std::istream& s, void* ptr, size_t size) { - if (s.read(reinterpret_cast(ptr), size).gcount() != size) { - throw std::runtime_error("read error"); - } -} - template -T readPod(std::istream& s) { +T readPod(Common::IInputStream& s) { T v; - strictRead(s, &v, sizeof(T)); + read(s, &v, sizeof(T)); return v; } template -JsonValue readPodJson(std::istream& s) { +JsonValue readPodJson(Common::IInputStream& s) { JsonValue jv; jv = static_cast(readPod(s)); return jv; } template -JsonValue readIntegerJson(std::istream& s) { +JsonValue readIntegerJson(Common::IInputStream& s) { return readPodJson(s); } -size_t readVarint(std::istream& s) { - size_t v = 0; - uint8_t size_mask = uint8_t(s.peek()) & PORTABLE_RAW_SIZE_MARK_MASK; +size_t readVarint(Common::IInputStream& s) { + uint8_t b = read(s); + uint8_t size_mask = b & PORTABLE_RAW_SIZE_MARK_MASK; + size_t bytesLeft = 0; - switch (size_mask) { + switch (size_mask){ case PORTABLE_RAW_SIZE_MARK_BYTE: - v = readPod(s); + bytesLeft = 0; break; case PORTABLE_RAW_SIZE_MARK_WORD: - v = readPod(s); + bytesLeft = 1; break; case PORTABLE_RAW_SIZE_MARK_DWORD: - v = readPod(s); + bytesLeft = 3; break; case PORTABLE_RAW_SIZE_MARK_INT64: - v = readPod(s); + bytesLeft = 7; break; - default: - throw std::runtime_error("unknown varint size_mask"); } - v >>= 2; - return v; + size_t value = b; + + for (size_t i = 1; i <= bytesLeft; ++i) { + size_t n = read(s); + value |= n << (i * 8); + } + + value >>= 2; + return value; } -std::string readString(std::istream& s) { +std::string readString(Common::IInputStream& s) { auto size = readVarint(s); std::string str; str.resize(size); if (size) { - strictRead(s, &str[0], size); + read(s, &str[0], size); } return str; } -JsonValue readStringJson(std::istream& s) { +JsonValue readStringJson(Common::IInputStream& s) { return JsonValue(readString(s)); } -void readName(std::istream& s, std::string& name) { +void readName(Common::IInputStream& s, std::string& name) { uint8_t len = readPod(s); if (len) { name.resize(len); - strictRead(s, &name[0], len); + read(s, &name[0], len); } } -JsonValue loadValue(std::istream& stream, uint8_t type); -JsonValue loadSection(std::istream& stream); -JsonValue loadEntry(std::istream& stream); -JsonValue loadArray(std::istream& stream, uint8_t itemType); +JsonValue loadValue(Common::IInputStream& stream, uint8_t type); +JsonValue loadSection(Common::IInputStream& stream); +JsonValue loadEntry(Common::IInputStream& stream); +JsonValue loadArray(Common::IInputStream& stream, uint8_t itemType); -JsonValue loadSection(std::istream& stream) { +JsonValue loadSection(Common::IInputStream& stream) { JsonValue sec(JsonValue::OBJECT); size_t count = readVarint(stream); std::string name; @@ -118,7 +120,7 @@ JsonValue loadSection(std::istream& stream) { return sec; } -JsonValue loadValue(std::istream& stream, uint8_t type) { +JsonValue loadValue(Common::IInputStream& stream, uint8_t type) { switch (type) { case BIN_KV_SERIALIZE_TYPE_INT64: return readIntegerJson(stream); case BIN_KV_SERIALIZE_TYPE_INT32: return readIntegerJson(stream); @@ -129,7 +131,7 @@ JsonValue loadValue(std::istream& stream, uint8_t type) { case BIN_KV_SERIALIZE_TYPE_UINT16: return readIntegerJson(stream); case BIN_KV_SERIALIZE_TYPE_UINT8: return readIntegerJson(stream); case BIN_KV_SERIALIZE_TYPE_DOUBLE: return readPodJson(stream); - case BIN_KV_SERIALIZE_TYPE_BOOL: return JsonValue(stream.get() != 0); + case BIN_KV_SERIALIZE_TYPE_BOOL: return JsonValue(read(stream) != 0); case BIN_KV_SERIALIZE_TYPE_STRING: return readStringJson(stream); case BIN_KV_SERIALIZE_TYPE_OBJECT: return loadSection(stream); case BIN_KV_SERIALIZE_TYPE_ARRAY: return loadArray(stream, type); @@ -139,7 +141,7 @@ JsonValue loadValue(std::istream& stream, uint8_t type) { } } -JsonValue loadEntry(std::istream& stream) { +JsonValue loadEntry(Common::IInputStream& stream) { uint8_t type = readPod(stream); if (type & BIN_KV_SERIALIZE_FLAG_ARRAY) { @@ -150,7 +152,7 @@ JsonValue loadEntry(std::istream& stream) { return loadValue(stream, type); } -JsonValue loadArray(std::istream& stream, uint8_t itemType) { +JsonValue loadArray(Common::IInputStream& stream, uint8_t itemType) { JsonValue arr(JsonValue::ARRAY); size_t count = readVarint(stream); @@ -162,7 +164,7 @@ JsonValue loadArray(std::istream& stream, uint8_t itemType) { } -JsonValue parseBinary(std::istream& stream) { +JsonValue parseBinary(Common::IInputStream& stream) { auto hdr = readPod(stream); if ( @@ -180,10 +182,10 @@ JsonValue parseBinary(std::istream& stream) { } -KVBinaryInputStreamSerializer::KVBinaryInputStreamSerializer(std::istream& strm) : value(parseBinary(strm)), JsonInputValueSerializer(value) { +KVBinaryInputStreamSerializer::KVBinaryInputStreamSerializer(Common::IInputStream& strm) : JsonInputValueSerializer(parseBinary(strm)) { } -bool KVBinaryInputStreamSerializer::binary(void* value, std::size_t size, Common::StringView name) { +bool KVBinaryInputStreamSerializer::binary(void* value, size_t size, Common::StringView name) { std::string str; if (!(*this)(str, name)) { diff --git a/src/serialization/KVBinaryInputStreamSerializer.h b/src/Serialization/KVBinaryInputStreamSerializer.h similarity index 84% rename from src/serialization/KVBinaryInputStreamSerializer.h rename to src/Serialization/KVBinaryInputStreamSerializer.h index 6fe29628..05d6dcc4 100644 --- a/src/serialization/KVBinaryInputStreamSerializer.h +++ b/src/Serialization/KVBinaryInputStreamSerializer.h @@ -17,7 +17,7 @@ #pragma once -#include +#include #include "ISerializer.h" #include "JsonInputValueSerializer.h" @@ -25,14 +25,10 @@ namespace CryptoNote { class KVBinaryInputStreamSerializer : public JsonInputValueSerializer { public: - KVBinaryInputStreamSerializer(std::istream& strm); + KVBinaryInputStreamSerializer(Common::IInputStream& strm); - virtual bool binary(void* value, std::size_t size, Common::StringView name) override; + virtual bool binary(void* value, size_t size, Common::StringView name) override; virtual bool binary(std::string& value, Common::StringView name) override; - -private: - - Common::JsonValue value; }; } diff --git a/src/serialization/KVBinaryOutputStreamSerializer.cpp b/src/Serialization/KVBinaryOutputStreamSerializer.cpp similarity index 84% rename from src/serialization/KVBinaryOutputStreamSerializer.cpp rename to src/Serialization/KVBinaryOutputStreamSerializer.cpp index 37c77f69..a33c34b1 100644 --- a/src/serialization/KVBinaryOutputStreamSerializer.cpp +++ b/src/Serialization/KVBinaryOutputStreamSerializer.cpp @@ -20,33 +20,23 @@ #include #include +#include +using namespace Common; using namespace CryptoNote; namespace { -class StdOutputStream : public IOutputStream { -public: - StdOutputStream(std::ostream& s) : stream(s) {} - - virtual void write(const char* data, std::size_t size) override { - stream.write(data, size); - } - -private: - std::ostream& stream; -}; - template void writePod(IOutputStream& s, const T& value) { - s.write((const char*)&value, sizeof(T)); + write(s, &value, sizeof(T)); } template size_t packVarint(IOutputStream& s, uint8_t type_or, size_t pv) { T v = static_cast(pv << 2); v |= type_or; - s.write((const char*)&v, sizeof(T)); + write(s, &v, sizeof(T)); return sizeof(T); } @@ -56,8 +46,8 @@ void writeElementName(IOutputStream& s, Common::StringView name) { } uint8_t len = static_cast(name.getSize()); - s.write((const char*)&len, sizeof(len)); - s.write(name.getData(), len); + write(s, &len, sizeof(len)); + write(s, name.getData(), len); } size_t writeArraySize(IOutputStream& s, size_t val) { @@ -79,17 +69,11 @@ size_t writeArraySize(IOutputStream& s, size_t val) { namespace CryptoNote { -using namespace CryptoNote; - - - - KVBinaryOutputStreamSerializer::KVBinaryOutputStreamSerializer() { beginObject(std::string()); } -void KVBinaryOutputStreamSerializer::write(std::ostream& target) { - +void KVBinaryOutputStreamSerializer::dump(IOutputStream& target) { assert(m_objectsStack.size() == 1); assert(m_stack.size() == 1); @@ -98,10 +82,9 @@ void KVBinaryOutputStreamSerializer::write(std::ostream& target) { hdr.m_signature_b = PORTABLE_STORAGE_SIGNATUREB; hdr.m_ver = PORTABLE_STORAGE_FORMAT_VER; - target.write(reinterpret_cast(&hdr), sizeof(hdr)); - StdOutputStream stdstream(target); - writeArraySize(stdstream, m_stack.front().count); - target.write(stream().data(), stream().size()); + Common::write(target, &hdr, sizeof(hdr)); + writeArraySize(target, m_stack.front().count); + write(target, stream().data(), stream().size()); } ISerializer::SerializerType KVBinaryOutputStreamSerializer::type() const { @@ -131,10 +114,10 @@ void KVBinaryOutputStreamSerializer::endObject() { writeElementPrefix(BIN_KV_SERIALIZE_TYPE_OBJECT, level.name); writeArraySize(out, level.count); - out.write(objStream.data(), objStream.size()); + write(out, objStream.data(), objStream.size()); } -bool KVBinaryOutputStreamSerializer::beginArray(std::size_t& size, Common::StringView name) { +bool KVBinaryOutputStreamSerializer::beginArray(size_t& size, Common::StringView name) { m_stack.push_back(Level(name, size)); return true; } @@ -154,6 +137,18 @@ bool KVBinaryOutputStreamSerializer::operator()(uint8_t& value, Common::StringVi return true; } +bool KVBinaryOutputStreamSerializer::operator()(uint16_t& value, Common::StringView name) { + writeElementPrefix(BIN_KV_SERIALIZE_TYPE_UINT16, name); + writePod(stream(), value); + return true; +} + +bool KVBinaryOutputStreamSerializer::operator()(int16_t& value, Common::StringView name) { + writeElementPrefix(BIN_KV_SERIALIZE_TYPE_INT16, name); + writePod(stream(), value); + return true; +} + bool KVBinaryOutputStreamSerializer::operator()(uint32_t& value, Common::StringView name) { writeElementPrefix(BIN_KV_SERIALIZE_TYPE_UINT32, name); writePod(stream(), value); @@ -195,16 +190,16 @@ bool KVBinaryOutputStreamSerializer::operator()(std::string& value, Common::Stri auto& out = stream(); writeArraySize(out, value.size()); - out.write(value.data(), value.size()); + write(out, value.data(), value.size()); return true; } -bool KVBinaryOutputStreamSerializer::binary(void* value, std::size_t size, Common::StringView name) { +bool KVBinaryOutputStreamSerializer::binary(void* value, size_t size, Common::StringView name) { if (size > 0) { writeElementPrefix(BIN_KV_SERIALIZE_TYPE_STRING, name); auto& out = stream(); writeArraySize(out, size); - out.write(static_cast(value), size); + write(out, value, size); } return true; } @@ -223,7 +218,7 @@ void KVBinaryOutputStreamSerializer::writeElementPrefix(uint8_t type, Common::St if (!name.isEmpty()) { auto& s = stream(); writeElementName(s, name); - s.write((const char*)&type, 1); + write(s, &type, 1); } ++level.count; } @@ -240,7 +235,7 @@ void KVBinaryOutputStreamSerializer::checkArrayPreamble(uint8_t type) { auto& s = stream(); writeElementName(s, level.name); char c = BIN_KV_SERIALIZE_FLAG_ARRAY | type; - s.write(&c, 1); + write(s, &c, 1); writeArraySize(s, level.count); level.state = State::Array; } diff --git a/src/serialization/KVBinaryOutputStreamSerializer.h b/src/Serialization/KVBinaryOutputStreamSerializer.h similarity index 87% rename from src/serialization/KVBinaryOutputStreamSerializer.h rename to src/Serialization/KVBinaryOutputStreamSerializer.h index 70fb43d9..87ab6f6d 100644 --- a/src/serialization/KVBinaryOutputStreamSerializer.h +++ b/src/Serialization/KVBinaryOutputStreamSerializer.h @@ -17,14 +17,11 @@ #pragma once +#include +#include #include "ISerializer.h" #include "MemoryStream.h" -#include -#include -#include -#include - namespace CryptoNote { class KVBinaryOutputStreamSerializer : public ISerializer { @@ -33,17 +30,19 @@ public: KVBinaryOutputStreamSerializer(); virtual ~KVBinaryOutputStreamSerializer() {} - void write(std::ostream& target); + void dump(Common::IOutputStream& target); virtual ISerializer::SerializerType type() const; virtual bool beginObject(Common::StringView name) override; virtual void endObject() override; - virtual bool beginArray(std::size_t& size, Common::StringView name) override; + virtual bool beginArray(size_t& size, Common::StringView name) override; virtual void endArray() override; virtual bool operator()(uint8_t& value, Common::StringView name) override; + virtual bool operator()(int16_t& value, Common::StringView name) override; + virtual bool operator()(uint16_t& value, Common::StringView name) override; virtual bool operator()(int32_t& value, Common::StringView name) override; virtual bool operator()(uint32_t& value, Common::StringView name) override; virtual bool operator()(int64_t& value, Common::StringView name) override; @@ -51,7 +50,7 @@ public: virtual bool operator()(double& value, Common::StringView name) override; virtual bool operator()(bool& value, Common::StringView name) override; virtual bool operator()(std::string& value, Common::StringView name) override; - virtual bool binary(void* value, std::size_t size, Common::StringView name) override; + virtual bool binary(void* value, size_t size, Common::StringView name) override; virtual bool binary(std::string& value, Common::StringView name) override; template diff --git a/src/serialization/MemoryStream.cpp b/src/Serialization/MemoryStream.cpp similarity index 100% rename from src/serialization/MemoryStream.cpp rename to src/Serialization/MemoryStream.cpp diff --git a/src/serialization/MemoryStream.h b/src/Serialization/MemoryStream.h similarity index 68% rename from src/serialization/MemoryStream.h rename to src/Serialization/MemoryStream.h index 1b995221..d4f9b7fe 100644 --- a/src/serialization/MemoryStream.h +++ b/src/Serialization/MemoryStream.h @@ -17,25 +17,23 @@ #pragma once - -#include "IStream.h" -#include #include +#include #include // memcpy +#include +#include namespace CryptoNote { -class MemoryStream: - public IInputStream, - public IOutputStream { +class MemoryStream: public Common::IOutputStream { public: - MemoryStream() : - m_readPos(0), m_writePos(0) {} + MemoryStream() : m_writePos(0) { + } - virtual void write(const char* data, std::size_t size) override { + virtual size_t writeSome(const void* data, size_t size) override { if (size == 0) { - return; + return 0; } if (m_writePos + size > m_buffer.size()) { @@ -44,38 +42,25 @@ public: memcpy(&m_buffer[m_writePos], data, size); m_writePos += size; - } - - virtual std::size_t read(char* data, std::size_t size) override { - size_t readSize = std::min(size, m_buffer.size() - m_readPos); - - if (readSize > 0) { - memcpy(data, &m_buffer[m_readPos], readSize); - m_readPos += readSize; - } - - return readSize; + return size; } size_t size() { return m_buffer.size(); } - const char* data() { + const uint8_t* data() { return m_buffer.data(); } void clear() { - m_readPos = 0; m_writePos = 0; m_buffer.resize(0); } private: - - size_t m_readPos; size_t m_writePos; - std::vector m_buffer; + std::vector m_buffer; }; } diff --git a/src/Serialization/SerializationOverloads.h b/src/Serialization/SerializationOverloads.h new file mode 100644 index 00000000..4b115d0f --- /dev/null +++ b/src/Serialization/SerializationOverloads.h @@ -0,0 +1,252 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "ISerializer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace CryptoNote { + +template +typename std::enable_if::value>::type +serializeAsBinary(std::vector& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + std::string blob; + if (serializer.type() == ISerializer::INPUT) { + serializer.binary(blob, name); + value.resize(blob.size() / sizeof(T)); + if (blob.size()) { + memcpy(&value[0], blob.data(), blob.size()); + } + } else { + if (!value.empty()) { + blob.assign(reinterpret_cast(&value[0]), value.size() * sizeof(T)); + } + serializer.binary(blob, name); + } +} + +template +typename std::enable_if::value>::type +serializeAsBinary(std::list& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + std::string blob; + if (serializer.type() == ISerializer::INPUT) { + serializer.binary(blob, name); + + size_t count = blob.size() / sizeof(T); + const T* ptr = reinterpret_cast(blob.data()); + + while (count--) { + value.push_back(*ptr++); + } + } else { + if (!value.empty()) { + blob.resize(value.size() * sizeof(T)); + T* ptr = reinterpret_cast(&blob[0]); + + for (const auto& item : value) { + *ptr++ = item; + } + } + serializer.binary(blob, name); + } +} + +template +bool serializeContainer(Cont& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + size_t size = value.size(); + if (!serializer.beginArray(size, name)) { + value.clear(); + return false; + } + + value.resize(size); + + for (auto& item : value) { + serializer(const_cast(item), ""); + } + + serializer.endArray(); + return true; +} + +template +bool serializeEnumClass(E& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + static_assert(std::is_enum::value, "E must be an enum class"); + + typedef typename std::underlying_type::type EType; + + if (serializer.type() == CryptoNote::ISerializer::INPUT) { + EType numericValue; + serializer(numericValue, name); + value = static_cast(numericValue); + } else { + auto numericValue = static_cast(value); + serializer(numericValue, name); + } + + return true; +} + +template +bool serialize(std::vector& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializeContainer(value, name, serializer); +} + +template +bool serialize(std::list& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializeContainer(value, name, serializer); +} + +template +bool serializeMap(MapT& value, Common::StringView name, CryptoNote::ISerializer& serializer, ReserveOp reserve) { + size_t size = value.size(); + + if (!serializer.beginArray(size, name)) { + value.clear(); + return false; + } + + if (serializer.type() == CryptoNote::ISerializer::INPUT) { + reserve(size); + + for (size_t i = 0; i < size; ++i) { + typename MapT::key_type key; + typename MapT::mapped_type v; + + serializer.beginObject(""); + serializer(key, "key"); + serializer(v, "value"); + serializer.endObject(); + + value.insert(std::make_pair(std::move(key), std::move(v))); + } + } else { + for (auto& kv : value) { + serializer.beginObject(""); + serializer(const_cast(kv.first), "key"); + serializer(kv.second, "value"); + serializer.endObject(); + } + } + + serializer.endArray(); + return true; +} + +template +bool serializeSet(SetT& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + size_t size = value.size(); + + if (!serializer.beginArray(size, name)) { + value.clear(); + return false; + } + + if (serializer.type() == CryptoNote::ISerializer::INPUT) { + for (size_t i = 0; i < size; ++i) { + typename SetT::value_type key; + serializer(key, ""); + value.insert(std::move(key)); + } + } else { + for (auto& key : value) { + serializer(const_cast(key), ""); + } + } + + serializer.endArray(); + return true; +} + +template +bool serialize(std::unordered_set& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializeSet(value, name, serializer); +} + +template +bool serialize(std::set& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializeSet(value, name, serializer); +} + +template +bool serialize(std::unordered_map& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializeMap(value, name, serializer, [&value](size_t size) { value.reserve(size); }); +} + +template +bool serialize(std::unordered_multimap& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializeMap(value, name, serializer, [&value](size_t size) { value.reserve(size); }); +} + +template +bool serialize(std::map& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializeMap(value, name, serializer, [](size_t size) {}); +} + +template +bool serialize(std::multimap& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializeMap(value, name, serializer, [](size_t size) {}); +} + +template +bool serialize(std::array& value, Common::StringView name, CryptoNote::ISerializer& s) { + return s.binary(value.data(), value.size(), name); +} + +template +void serialize(std::pair& value, ISerializer& s) { + s(value.first, "first"); + s(value.second, "second"); +} + +template +void writeSequence(Iterator begin, Iterator end, Common::StringView name, ISerializer& s) { + size_t size = std::distance(begin, end); + s.beginArray(size, name); + for (Iterator i = begin; i != end; ++i) { + s(const_cast(*i), ""); + } + s.endArray(); +} + +template +void readSequence(Iterator outputIterator, Common::StringView name, ISerializer& s) { + size_t size = 0; + s.beginArray(size, name); + + while (size--) { + Element e; + s(e, ""); + *outputIterator++ = std::move(e); + } + + s.endArray(); +} + +} diff --git a/src/serialization/SerializationTools.h b/src/Serialization/SerializationTools.h similarity index 91% rename from src/serialization/SerializationTools.h rename to src/Serialization/SerializationTools.h index dc314c70..827ad69a 100644 --- a/src/serialization/SerializationTools.h +++ b/src/Serialization/SerializationTools.h @@ -17,11 +17,14 @@ #pragma once -#include "KVBinaryInputStreamSerializer.h" -#include "KVBinaryOutputStreamSerializer.h" - +#include +#include +#include +#include #include "JsonInputStreamSerializer.h" #include "JsonOutputStreamSerializer.h" +#include "KVBinaryInputStreamSerializer.h" +#include "KVBinaryOutputStreamSerializer.h" namespace Common { @@ -37,7 +40,6 @@ inline std::string getValueAs(const JsonValue& js) { return js.getS template <> inline uint64_t getValueAs(const JsonValue& js) { return static_cast(js.getInteger()); } - } namespace CryptoNote { @@ -67,7 +69,6 @@ Common::JsonValue storeToJsonValue(const std::list& v) { return storeContaine template <> inline Common::JsonValue storeToJsonValue(const std::string& v) { return Common::JsonValue(v); } - template void loadFromJsonValue(T& v, const Common::JsonValue& js) { JsonInputValueSerializer s(js); @@ -88,7 +89,6 @@ void loadFromJsonValue(std::list& v, const Common::JsonValue& js) { } } - template std::string storeToJson(const T& v) { return storeToJsonValue(v).toString(); @@ -113,22 +113,22 @@ std::string storeToBinaryKeyValue(const T& v) { KVBinaryOutputStreamSerializer s; serialize(const_cast(v), s); - std::ostringstream stream; - s.write(stream); - return stream.str(); + std::string result; + Common::StringOutputStream stream(result); + s.dump(stream); + return result; } template bool loadFromBinaryKeyValue(T& v, const std::string& buf) { try { - std::istringstream stream(buf); + Common::MemoryInputStream stream(buf.data(), buf.size()); KVBinaryInputStreamSerializer s(stream); serialize(v, s); - return stream.good(); + return true; } catch (std::exception&) { return false; } } - } diff --git a/src/simplewallet/password_container.cpp b/src/SimpleWallet/PasswordContainer.cpp similarity index 90% rename from src/simplewallet/password_container.cpp rename to src/SimpleWallet/PasswordContainer.cpp index 9a574c76..1643bf7c 100644 --- a/src/simplewallet/password_container.cpp +++ b/src/SimpleWallet/PasswordContainer.cpp @@ -15,7 +15,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "password_container.h" +#include "PasswordContainer.h" #include #include @@ -29,36 +29,36 @@ #include #endif -namespace tools +namespace Tools { namespace { bool is_cin_tty(); } - password_container::password_container() + PasswordContainer::PasswordContainer() : m_empty(true) { } - password_container::password_container(std::string&& password) + PasswordContainer::PasswordContainer(std::string&& password) : m_empty(false) , m_password(std::move(password)) { } - password_container::password_container(password_container&& rhs) + PasswordContainer::PasswordContainer(PasswordContainer&& rhs) : m_empty(std::move(rhs.m_empty)) , m_password(std::move(rhs.m_password)) { } - password_container::~password_container() + PasswordContainer::~PasswordContainer() { clear(); } - void password_container::clear() + void PasswordContainer::clear() { if (0 < m_password.capacity()) { @@ -68,7 +68,7 @@ namespace tools m_empty = true; } - bool password_container::read_password() + bool PasswordContainer::read_password() { clear(); @@ -95,7 +95,7 @@ namespace tools return r; } - bool password_container::read_from_file() + bool PasswordContainer::read_from_file() { m_password.reserve(max_password_size); for (size_t i = 0; i < max_password_size; ++i) @@ -128,7 +128,7 @@ namespace tools } } - bool password_container::read_from_tty() + bool PasswordContainer::read_from_tty() { const char BACKSPACE = 8; @@ -204,7 +204,7 @@ namespace tools } } - bool password_container::read_from_tty() + bool PasswordContainer::read_from_tty() { const char BACKSPACE = 127; diff --git a/src/simplewallet/password_container.h b/src/SimpleWallet/PasswordContainer.h old mode 100644 new mode 100755 similarity index 86% rename from src/simplewallet/password_container.h rename to src/SimpleWallet/PasswordContainer.h index 7be4ccf4..0eeee40d --- a/src/simplewallet/password_container.h +++ b/src/SimpleWallet/PasswordContainer.h @@ -19,17 +19,17 @@ #include -namespace tools +namespace Tools { - class password_container + class PasswordContainer { public: static const size_t max_password_size = 1024; - password_container(); - password_container(std::string&& password); - password_container(password_container&& rhs); - ~password_container(); + PasswordContainer(); + PasswordContainer(std::string&& password); + PasswordContainer(PasswordContainer&& rhs); + ~PasswordContainer(); void clear(); bool empty() const { return m_empty; } diff --git a/src/simplewallet/simplewallet.cpp b/src/SimpleWallet/SimpleWallet.cpp similarity index 91% rename from src/simplewallet/simplewallet.cpp rename to src/SimpleWallet/SimpleWallet.cpp index 5dcbdc04..99ee6bb2 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/SimpleWallet/SimpleWallet.cpp @@ -15,7 +15,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "simplewallet.h" +#include "SimpleWallet.h" #include #include @@ -29,20 +29,20 @@ #include #include -#include "Common/command_line.h" +#include "Common/CommandLine.h" #include "Common/SignalHandler.h" #include "Common/PathTools.h" -#include "Common/util.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_protocol/cryptonote_protocol_handler.h" -#include "node_rpc_proxy/NodeRpcProxy.h" -#include "rpc/core_rpc_server_commands_defs.h" -#include "rpc/HttpClient.h" +#include "Common/Util.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolHandler.h" +#include "NodeRpcProxy/NodeRpcProxy.h" +#include "Rpc/CoreRpcServerCommandsDefinitions.h" +#include "Rpc/HttpClient.h" -#include "wallet/wallet_rpc_server.h" -#include "wallet/Wallet.h" -#include "wallet/LegacyKeysImporter.h" -#include "wallet/WalletHelper.h" +#include "Wallet/WalletRpcServer.h" +#include "WalletLegacy/WalletLegacy.h" +#include "Wallet/LegacyKeysImporter.h" +#include "WalletLegacy/WalletHelper.h" #include "version.h" @@ -75,13 +75,10 @@ const command_line::arg_descriptor< std::vector > arg_command = { " bool parseUrlAddress(const std::string& url, std::string& address, uint16_t& port) { - auto pos = url.find("://"); size_t addrStart = 0; - if (pos == std::string::npos) { - pos = 0; - } else { + if (pos != std::string::npos) { addrStart = pos + 3; } @@ -145,7 +142,7 @@ private: struct TransferCommand { const CryptoNote::Currency& m_currency; size_t fake_outs_count; - std::vector dsts; + std::vector dsts; std::vector extra; uint64_t fee; @@ -192,11 +189,11 @@ struct TransferCommand { } } } else { - Transfer destination; - CryptoNote::tx_destination_entry de; + WalletLegacyTransfer destination; + CryptoNote::TransactionDestinationEntry de; if (!m_currency.parseAccountAddressString(arg, de.addr)) { - crypto::hash paymentId; + Crypto::Hash paymentId; if (CryptoNote::parsePaymentId(arg, paymentId)) { logger(ERROR, BRIGHT_RED) << "Invalid payment ID usage. Please, use -p . See help for details."; } else { @@ -252,7 +249,7 @@ JsonValue buildLoggerConfiguration(Level level, const std::string& logfile) { return loggerConfiguration; } -std::error_code initAndLoadWallet(IWallet& wallet, std::istream& walletFile, const std::string& password) { +std::error_code initAndLoadWallet(IWalletLegacy& wallet, std::istream& walletFile, const std::string& password) { WalletHelper::InitWalletResultObserver initObserver; std::future f_initError = initObserver.initResult.get_future(); @@ -263,7 +260,7 @@ std::error_code initAndLoadWallet(IWallet& wallet, std::istream& walletFile, con return initError; } -std::string tryToOpenWalletOrLoadKeysOrThrow(LoggerRef& logger, std::unique_ptr& wallet, const std::string& walletFile, const std::string& password) { +std::string tryToOpenWalletOrLoadKeysOrThrow(LoggerRef& logger, std::unique_ptr& wallet, const std::string& walletFile, const std::string& password) { std::string keys_file, walletFileName; WalletHelper::prepareFileNames(walletFile, keys_file, walletFileName); @@ -271,9 +268,10 @@ std::string tryToOpenWalletOrLoadKeysOrThrow(LoggerRef& logger, std::unique_ptr< bool keysExists = boost::filesystem::exists(keys_file, ignore); bool walletExists = boost::filesystem::exists(walletFileName, ignore); if (!walletExists && !keysExists && boost::filesystem::exists(walletFile, ignore)) { - auto replaceEc = tools::replace_file(walletFile, walletFileName); - if (replaceEc) { - throw std::runtime_error("failed to rename file '" + walletFile + "' to '" + walletFileName + "'"); + boost::system::error_code renameEc; + boost::filesystem::rename(walletFile, walletFileName, renameEc); + if (renameEc) { + throw std::runtime_error("failed to rename file '" + walletFile + "' to '" + walletFileName + "': " + renameEc.message()); } walletExists = true; @@ -328,12 +326,10 @@ std::string tryToOpenWalletOrLoadKeysOrThrow(LoggerRef& logger, std::unique_ptr< std::future f_initError = initObserver.initResult.get_future(); WalletHelper::IWalletRemoveObserverGuard removeGuard(*wallet, initObserver); - wallet->initAndLoad(ss, password); auto initError = f_initError.get(); removeGuard.removeObserver(); - if (initError) { throw std::runtime_error("failed to load wallet: " + initError.message()); } @@ -380,14 +376,14 @@ bool simple_wallet::exit(const std::vector &args) { return true; } -simple_wallet::simple_wallet(System::Dispatcher& dispatcher, const CryptoNote::Currency& currency, Logging::LoggerManager& log) - : m_daemon_port(0) - , m_currency(currency) - , logManager(log) - , m_dispatcher(dispatcher) - , logger(log, "simplewallet") - , m_refresh_progress_reporter(*this) - , m_initResultPromise(nullptr) +simple_wallet::simple_wallet(System::Dispatcher& dispatcher, const CryptoNote::Currency& currency, Logging::LoggerManager& log) : + m_dispatcher(dispatcher), + m_daemon_port(0), + m_currency(currency), + logManager(log), + logger(log, "simplewallet"), + m_refresh_progress_reporter(*this), + m_initResultPromise(nullptr) { m_consoleHandler.setHandler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), "start_mining [] - Start mining in daemon"); m_consoleHandler.setHandler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, _1), "Stop mining in daemon"); @@ -401,7 +397,7 @@ simple_wallet::simple_wallet(System::Dispatcher& dispatcher, const CryptoNote::C "transfer [ ... ] [-p payment_id] [-f fee]" " - Transfer ,... to ,... , respectively. " " is the number of transactions yours is indistinguishable from (from 0 to maximum available)"); - m_consoleHandler.setHandler("set_log", boost::bind(&simple_wallet::set_log, this, _1), "set_log - Change current log detalization level, is a number 0-4"); + m_consoleHandler.setHandler("set_log", boost::bind(&simple_wallet::set_log, this, _1), "set_log - Change current log level, is a number 0-4"); m_consoleHandler.setHandler("address", boost::bind(&simple_wallet::print_address, this, _1), "Show current wallet public address"); m_consoleHandler.setHandler("save", boost::bind(&simple_wallet::save, this, _1), "Save wallet synchronized data"); m_consoleHandler.setHandler("reset", boost::bind(&simple_wallet::reset, this, _1), "Discard cache data and start synchronizing from the start"); @@ -505,7 +501,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) m_daemon_address = std::string("http://") + m_daemon_host + ":" + std::to_string(m_daemon_port); } - tools::password_container pwd_container; + Tools::PasswordContainer pwd_container; if (command_line::has_arg(vm, arg_password)) { pwd_container.password(command_line::get_arg(vm, arg_password)); } else if (!pwd_container.read_password()) { @@ -532,7 +528,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) } else { - m_wallet.reset(new Wallet(m_currency, *m_node)); + m_wallet.reset(new WalletLegacy(m_currency, *m_node)); try { m_wallet_file = tryToOpenWalletOrLoadKeysOrThrow(logger, m_wallet, m_wallet_file_arg, pwd_container.password()); @@ -576,7 +572,7 @@ bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string { m_wallet_file = wallet_file; - m_wallet.reset(new Wallet(m_currency, *m_node.get())); + m_wallet.reset(new WalletLegacy(m_currency, *m_node.get())); m_node->addObserver(this); m_wallet->addObserver(this); try @@ -598,7 +594,7 @@ bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string throw; } - WalletAccountKeys keys; + AccountKeys keys; m_wallet->getAccountKeys(keys); logger(INFO, BRIGHT_WHITE) << @@ -624,8 +620,7 @@ bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string //---------------------------------------------------------------------------------------------------- bool simple_wallet::close_wallet() { - try - { + try { CryptoNote::WalletHelper::storeWallet(*m_wallet, m_wallet_file); } catch (const std::exception& e) @@ -739,27 +734,38 @@ void simple_wallet::initCompleted(std::error_code result) { } } //---------------------------------------------------------------------------------------------------- -void simple_wallet::localBlockchainUpdated(uint64_t height) +void simple_wallet::localBlockchainUpdated(uint32_t height) { m_refresh_progress_reporter.update(height, false); } //---------------------------------------------------------------------------------------------------- void simple_wallet::externalTransactionCreated(CryptoNote::TransactionId transactionId) { - TransactionInfo txInfo; + WalletLegacyTransaction txInfo; m_wallet->getTransaction(transactionId, txInfo); + + std::stringstream logPrefix; + if (txInfo.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { + logPrefix << "Unconfirmed"; + } else { + logPrefix << "Height " << txInfo.blockHeight << ','; + } if (txInfo.totalAmount >= 0) { logger(INFO, GREEN) << - "Height " << txInfo.blockHeight << ", transaction " << Common::podToHex(txInfo.hash) << + logPrefix.str() << " transaction " << Common::podToHex(txInfo.hash) << ", received " << m_currency.formatAmount(txInfo.totalAmount); } else { logger(INFO, MAGENTA) << - "Height " << txInfo.blockHeight << ", transaction " << Common::podToHex(txInfo.hash) << + logPrefix.str() << " transaction " << Common::podToHex(txInfo.hash) << ", spent " << m_currency.formatAmount(static_cast(-txInfo.totalAmount)); } - m_refresh_progress_reporter.update(txInfo.blockHeight, true); + if (txInfo.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { + m_refresh_progress_reporter.update(m_node->getLastLocalBlockHeight(), true); + } else { + m_refresh_progress_reporter.update(txInfo.blockHeight, true); + } } //---------------------------------------------------------------------------------------------------- bool simple_wallet::show_balance(const std::vector& args/* = std::vector()*/) @@ -775,7 +781,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector& args bool hasTransfers = false; size_t transactionsCount = m_wallet->getTransactionCount(); for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { - TransactionInfo txInfo; + WalletLegacyTransaction txInfo; m_wallet->getTransaction(trantransactionNumber, txInfo); if (txInfo.totalAmount < 0) continue; hasTransfers = true; @@ -791,9 +797,9 @@ bool simple_wallet::show_incoming_transfers(const std::vector& args bool simple_wallet::listTransfers(const std::vector& args) { size_t transactionsCount = m_wallet->getTransactionCount(); for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { - TransactionInfo txInfo; + WalletLegacyTransaction txInfo; m_wallet->getTransaction(trantransactionNumber, txInfo); - if (txInfo.state != TransactionState::Active || txInfo.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { + if (txInfo.state != WalletLegacyTransactionState::Active || txInfo.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { continue; } @@ -802,14 +808,14 @@ bool simple_wallet::listTransfers(const std::vector& args) { extraVec.reserve(txInfo.extra.size()); std::for_each(txInfo.extra.begin(), txInfo.extra.end(), [&extraVec](const char el) { extraVec.push_back(el); }); - crypto::hash paymentId; - paymentIdStr = (getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId != null_hash ? Common::podToHex(paymentId) : ""); + Crypto::Hash paymentId; + paymentIdStr = (getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId != NULL_HASH ? Common::podToHex(paymentId) : ""); std::string address = "-"; if (txInfo.totalAmount < 0) { if (txInfo.transferCount > 0) { - Transfer tr; + WalletLegacyTransfer tr; m_wallet->getTransfer(txInfo.firstTransferId, tr); address = tr.address; } @@ -846,19 +852,19 @@ bool simple_wallet::show_payments(const std::vector &args) bool payments_found = false; for (const std::string& arg: args) { - crypto::hash expectedPaymentId; + Crypto::Hash expectedPaymentId; if (CryptoNote::parsePaymentId(arg, expectedPaymentId)) { size_t transactionsCount = m_wallet->getTransactionCount(); for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { - TransactionInfo txInfo; + WalletLegacyTransaction txInfo; m_wallet->getTransaction(trantransactionNumber, txInfo); if (txInfo.totalAmount < 0) continue; std::vector extraVec; extraVec.reserve(txInfo.extra.size()); std::for_each(txInfo.extra.begin(), txInfo.extra.end(), [&extraVec](const char el) { extraVec.push_back(el); }); - crypto::hash paymentId; + Crypto::Hash paymentId; if (CryptoNote::getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId == expectedPaymentId) { payments_found = true; success_msg_writer(true) << @@ -909,7 +915,7 @@ bool simple_wallet::transfer(const std::vector &args) WalletHelper::IWalletRemoveObserverGuard removeGuard(*m_wallet, sent); CryptoNote::TransactionId tx = m_wallet->sendTransaction(cmd.dsts, cmd.fee, extraString, cmd.fake_outs_count, 0); - if (tx == INVALID_TRANSACTION_ID) { + if (tx == WALLET_LEGACY_INVALID_TRANSACTION_ID) { fail_msg_writer() << "Can't send money"; return true; } @@ -922,7 +928,7 @@ bool simple_wallet::transfer(const std::vector &args) return true; } - CryptoNote::TransactionInfo txInfo; + CryptoNote::WalletLegacyTransaction txInfo; m_wallet->getTransaction(tx, txInfo); success_msg_writer(true) << "Money successfully sent, transaction " << Common::podToHex(txInfo.hash); @@ -989,7 +995,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_params, arg_command); command_line::add_arg(desc_params, arg_log_level); command_line::add_arg(desc_params, arg_testnet); - tools::wallet_rpc_server::init_options(desc_params); + Tools::wallet_rpc_server::init_options(desc_params); po::positional_options_description positional_options; positional_options.add(arg_command.name, -1); @@ -1042,7 +1048,7 @@ int main(int argc, char* argv[]) CryptoNote::Currency currency = CryptoNote::CurrencyBuilder(logManager). testnet(command_line::get_arg(vm, arg_testnet)).currency(); - if (command_line::has_arg(vm, tools::wallet_rpc_server::arg_rpc_bind_port)) { + if (command_line::has_arg(vm, Tools::wallet_rpc_server::arg_rpc_bind_port)) { //runs wallet with rpc interface if (!command_line::has_arg(vm, arg_wallet_file)) { @@ -1064,20 +1070,17 @@ int main(int argc, char* argv[]) std::string wallet_password = command_line::get_arg(vm, arg_password); std::string daemon_address = command_line::get_arg(vm, arg_daemon_address); std::string daemon_host = command_line::get_arg(vm, arg_daemon_host); - int daemon_port = command_line::get_arg(vm, arg_daemon_port); + uint16_t daemon_port = command_line::get_arg(vm, arg_daemon_port); if (daemon_host.empty()) daemon_host = "localhost"; if (!daemon_port) daemon_port = RPC_DEFAULT_PORT; if (!daemon_address.empty()) { - uint16_t port = 0; - if (!parseUrlAddress(daemon_address, daemon_host, port)) { + if (!parseUrlAddress(daemon_address, daemon_host, daemon_port)) { logger(ERROR, BRIGHT_RED) << "failed to parse daemon address: " << daemon_address; return 1; } - - daemon_port = port; } std::unique_ptr node(new NodeRpcProxy(daemon_host, daemon_port)); @@ -1091,7 +1094,7 @@ int main(int argc, char* argv[]) return 1; } - std::unique_ptr wallet(new Wallet(currency, *node.get())); + std::unique_ptr wallet(new WalletLegacy(currency, *node.get())); std::string walletFileName; try { @@ -1106,14 +1109,14 @@ int main(int argc, char* argv[]) return 1; } - tools::wallet_rpc_server wrpc(dispatcher, logManager, *wallet, *node, currency, walletFileName); + Tools::wallet_rpc_server wrpc(dispatcher, logManager, *wallet, *node, currency, walletFileName); if (!wrpc.init(vm)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize wallet rpc server"; return 1; } - tools::SignalHandler::install([&wrpc, &wallet] { + Tools::SignalHandler::install([&wrpc, &wallet] { wrpc.send_stop_signal(); }); @@ -1122,8 +1125,8 @@ int main(int argc, char* argv[]) logger(INFO) << "Stopped wallet rpc server"; try { + logger(INFO) << "Storing wallet..."; CryptoNote::WalletHelper::storeWallet(*wallet, walletFileName); - logger(INFO, BRIGHT_GREEN) << "Stored ok"; } catch (const std::exception& e) @@ -1144,7 +1147,7 @@ int main(int argc, char* argv[]) if (!command.empty()) wal.process_command(command); - tools::SignalHandler::install([&wal] { + Tools::SignalHandler::install([&wal] { wal.stop(); }); diff --git a/src/simplewallet/simplewallet.h b/src/SimpleWallet/SimpleWallet.h old mode 100644 new mode 100755 similarity index 94% rename from src/simplewallet/simplewallet.h rename to src/SimpleWallet/SimpleWallet.h index a2d6ba9d..75ff383d --- a/src/simplewallet/simplewallet.h +++ b/src/SimpleWallet/SimpleWallet.h @@ -22,14 +22,14 @@ #include -#include "IWallet.h" +#include "IWalletLegacy.h" #include "INode.h" -#include "password_container.h" +#include "PasswordContainer.h" #include "Common/ConsoleHandler.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/Currency.h" -#include "wallet/WalletHelper.h" +#include "CryptoNoteCore/CryptoNoteBasicImpl.h" +#include "CryptoNoteCore/Currency.h" +#include "WalletLegacy/WalletHelper.h" #include #include @@ -42,7 +42,7 @@ namespace CryptoNote /************************************************************************/ /* */ /************************************************************************/ - class simple_wallet : public CryptoNote::INodeObserver, public CryptoNote::IWalletObserver + class simple_wallet : public CryptoNote::INodeObserver, public CryptoNote::IWalletLegacyObserver { public: simple_wallet(System::Dispatcher& dispatcher, const CryptoNote::Currency& currency, Logging::LoggerManager& log); @@ -100,7 +100,7 @@ namespace CryptoNote //---------------------------------------------------------- //----------------- INodeObserver -------------------------- - virtual void localBlockchainUpdated(uint64_t height) override; + virtual void localBlockchainUpdated(uint32_t height) override; //---------------------------------------------------------- friend class refresh_progress_reporter_t; @@ -166,6 +166,8 @@ namespace CryptoNote std::string m_wallet_file; + std::unique_ptr> m_initResultPromise; + Common::ConsoleHandler m_consoleHandler; const CryptoNote::Currency& m_currency; Logging::LoggerManager& logManager; @@ -173,8 +175,7 @@ namespace CryptoNote Logging::LoggerRef logger; std::unique_ptr m_node; - std::unique_ptr m_wallet; + std::unique_ptr m_wallet; refresh_progress_reporter_t m_refresh_progress_reporter; - std::unique_ptr> m_initResultPromise; }; } diff --git a/src/System/Context.h b/src/System/Context.h new file mode 100755 index 00000000..65c8d410 --- /dev/null +++ b/src/System/Context.h @@ -0,0 +1,151 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +namespace System { + +template +class Context { +public: + Context(Dispatcher& dispatcher, std::function&& target) : + dispatcher(dispatcher), target(std::move(target)), ready(dispatcher), bindingContext(dispatcher.getReusableContext()) { + bindingContext.interrupted = false; + bindingContext.groupNext = nullptr; + bindingContext.groupPrev = nullptr; + bindingContext.group = nullptr; + bindingContext.procedure = [this] { + try { + new(resultStorage) ResultType(this->target()); + } catch (...) { + exceptionPointer = std::current_exception(); + } + + ready.set(); + }; + + dispatcher.pushContext(&bindingContext); + } + + Context(const Context&) = delete; + Context& operator=(const Context&) = delete; + + ~Context() { + interrupt(); + wait(); + dispatcher.pushReusableContext(bindingContext); + } + + ResultType& get() { + wait(); + if (exceptionPointer != nullptr) { + std::rethrow_exception(exceptionPointer); + } + + return *reinterpret_cast(resultStorage); + } + + void interrupt() { + dispatcher.interrupt(&bindingContext); + } + + void wait() { + for (;;) { + try { + ready.wait(); + break; + } catch (InterruptedException&) { + interrupt(); + } + } + } + +private: + uint8_t resultStorage[sizeof(ResultType)]; + Dispatcher& dispatcher; + std::function target; + Event ready; + NativeContext& bindingContext; + std::exception_ptr exceptionPointer; +}; + +template<> +class Context { +public: + Context(Dispatcher& dispatcher, std::function&& target) : + dispatcher(dispatcher), target(std::move(target)), ready(dispatcher), bindingContext(dispatcher.getReusableContext()) { + bindingContext.interrupted = false; + bindingContext.groupNext = nullptr; + bindingContext.groupPrev = nullptr; + bindingContext.group = nullptr; + bindingContext.procedure = [this] { + try { + this->target(); + } catch (...) { + exceptionPointer = std::current_exception(); + } + + ready.set(); + }; + + dispatcher.pushContext(&bindingContext); + } + + Context(const Context&) = delete; + Context& operator=(const Context&) = delete; + + ~Context() { + interrupt(); + wait(); + dispatcher.pushReusableContext(bindingContext); + } + + void get() { + wait(); + if (exceptionPointer != nullptr) { + std::rethrow_exception(exceptionPointer); + } + } + + void interrupt() { + dispatcher.interrupt(&bindingContext); + } + + void wait() { + for (;;) { + try { + ready.wait(); + break; + } catch (InterruptedException&) { + interrupt(); + } + } + } + +private: + Dispatcher& dispatcher; + std::function target; + Event ready; + NativeContext& bindingContext; + std::exception_ptr exceptionPointer; +}; + +} diff --git a/src/System/ContextGroup.cpp b/src/System/ContextGroup.cpp new file mode 100755 index 00000000..a0fd5b7f --- /dev/null +++ b/src/System/ContextGroup.cpp @@ -0,0 +1,99 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "ContextGroup.h" +#include + +namespace System { + +ContextGroup::ContextGroup(Dispatcher& dispatcher) : dispatcher(&dispatcher) { + contextGroup.firstContext = nullptr; +} + +ContextGroup::ContextGroup(ContextGroup&& other) : dispatcher(other.dispatcher) { + if (dispatcher != nullptr) { + assert(other.contextGroup.firstContext == nullptr); + contextGroup.firstContext = nullptr; + other.dispatcher = nullptr; + } +} + +ContextGroup::~ContextGroup() { + if (dispatcher != nullptr) { + interrupt(); + wait(); + } +} + +ContextGroup& ContextGroup::operator=(ContextGroup&& other) { + assert(dispatcher == nullptr || contextGroup.firstContext == nullptr); + dispatcher = other.dispatcher; + if (dispatcher != nullptr) { + assert(other.contextGroup.firstContext == nullptr); + contextGroup.firstContext = nullptr; + other.dispatcher = nullptr; + } + + return *this; +} + +void ContextGroup::interrupt() { + assert(dispatcher != nullptr); + for (NativeContext* context = contextGroup.firstContext; context != nullptr; context = context->groupNext) { + dispatcher->interrupt(context); + } +} + +void ContextGroup::spawn(std::function&& procedure) { + assert(dispatcher != nullptr); + NativeContext& context = dispatcher->getReusableContext(); + if (contextGroup.firstContext != nullptr) { + context.groupPrev = contextGroup.lastContext; + assert(contextGroup.lastContext->groupNext == nullptr); + contextGroup.lastContext->groupNext = &context; + } else { + context.groupPrev = nullptr; + contextGroup.firstContext = &context; + contextGroup.firstWaiter = nullptr; + } + + context.interrupted = false; + context.group = &contextGroup; + context.groupNext = nullptr; + context.procedure = std::move(procedure); + contextGroup.lastContext = &context; + dispatcher->pushContext(&context); +} + +void ContextGroup::wait() { + if (contextGroup.firstContext != nullptr) { + NativeContext* context = dispatcher->getCurrentContext(); + context->next = nullptr; + if (contextGroup.firstWaiter != nullptr) { + assert(contextGroup.lastWaiter->next == nullptr); + contextGroup.lastWaiter->next = context; + } else { + contextGroup.firstWaiter = context; + } + + contextGroup.lastWaiter = context; + dispatcher->dispatch(); + assert(context == dispatcher->getCurrentContext()); + } +} + +} diff --git a/src/System/Latch.h b/src/System/ContextGroup.h similarity index 67% rename from src/System/Latch.h rename to src/System/ContextGroup.h index ebe37558..40ad6e42 100755 --- a/src/System/Latch.h +++ b/src/System/ContextGroup.h @@ -17,31 +17,25 @@ #pragma once -#include +#include namespace System { -class Dispatcher; - -class Latch { +class ContextGroup { public: - Latch(); - explicit Latch(Dispatcher& dispatcher); - Latch(const Latch&) = delete; - Latch(Latch&& other); - ~Latch(); - Latch& operator=(const Latch&) = delete; - Latch& operator=(Latch&& other); - std::size_t get() const; - void decrease(std::size_t value = 1); - void increase(std::size_t value = 1); + explicit ContextGroup(Dispatcher& dispatcher); + ContextGroup(const ContextGroup&) = delete; + ContextGroup(ContextGroup&& other); + ~ContextGroup(); + ContextGroup& operator=(const ContextGroup&) = delete; + ContextGroup& operator=(ContextGroup&& other); + void interrupt(); + void spawn(std::function&& procedure); void wait(); private: Dispatcher* dispatcher; - std::size_t value; - void* first; - void* last; + NativeContextGroup contextGroup; }; } diff --git a/tests/System/LatchTests.cpp b/src/System/ContextGroupTimeout.cpp similarity index 64% rename from tests/System/LatchTests.cpp rename to src/System/ContextGroupTimeout.cpp index ce91670d..ac2fe472 100755 --- a/tests/System/LatchTests.cpp +++ b/src/System/ContextGroupTimeout.cpp @@ -15,19 +15,19 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include -#include -#include +#include "ContextGroupTimeout.h" +#include using namespace System; -TEST(LatchTest, latch1) { - Dispatcher dispatcher; - Latch latch(dispatcher); - ASSERT_EQ(0, latch.get()); - latch.increase(10); - ASSERT_EQ(10, latch.get()); - latch.decrease(10); - ASSERT_EQ(0, latch.get()); - latch.wait(); +ContextGroupTimeout::ContextGroupTimeout(Dispatcher& dispatcher, ContextGroup& contextGroup, std::chrono::nanoseconds timeout) : + workingContextGroup(dispatcher), + timeoutTimer(dispatcher) { + workingContextGroup.spawn([&, timeout] { + try { + timeoutTimer.sleep(timeout); + contextGroup.interrupt(); + } catch (InterruptedException&) { + } + }); } diff --git a/src/System/ContextGroupTimeout.h b/src/System/ContextGroupTimeout.h new file mode 100755 index 00000000..e36645b4 --- /dev/null +++ b/src/System/ContextGroupTimeout.h @@ -0,0 +1,34 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once +#include +#include +#include + +namespace System { + +class ContextGroupTimeout { +public: + ContextGroupTimeout(Dispatcher&, ContextGroup&, std::chrono::nanoseconds); + +private: + Timer timeoutTimer; + ContextGroup workingContextGroup; +}; + +} diff --git a/src/System/Event.cpp b/src/System/Event.cpp index 809593e6..6d24b095 100755 --- a/src/System/Event.cpp +++ b/src/System/Event.cpp @@ -18,14 +18,17 @@ #include "Event.h" #include #include +#include namespace System { namespace { struct EventWaiter { + bool interrupted; + EventWaiter* prev; EventWaiter* next; - void* context; + NativeContext* context; }; } @@ -86,6 +89,7 @@ void Event::set() { if (!state) { state = true; for (EventWaiter* waiter = static_cast(first); waiter != nullptr; waiter = waiter->next) { + waiter->context->interruptProcedure = nullptr; dispatcher->pushContext(waiter->context); } } @@ -93,10 +97,37 @@ void Event::set() { void Event::wait() { assert(dispatcher != nullptr); + if (dispatcher->interrupted()) { + throw InterruptedException(); + } + if (!state) { - EventWaiter waiter = {nullptr, dispatcher->getCurrentContext()}; + EventWaiter waiter = { false, nullptr, nullptr, dispatcher->getCurrentContext() }; + waiter.context->interruptProcedure = [&] { + if (waiter.next != nullptr) { + assert(waiter.next->prev == &waiter); + waiter.next->prev = waiter.prev; + } else { + assert(last == &waiter); + last = waiter.prev; + } + + if (waiter.prev != nullptr) { + assert(waiter.prev->next == &waiter); + waiter.prev->next = waiter.next; + } else { + assert(first == &waiter); + first = waiter.next; + } + + assert(!waiter.interrupted); + waiter.interrupted = true; + dispatcher->pushContext(waiter.context); + }; + if (first != nullptr) { static_cast(last)->next = &waiter; + waiter.prev = static_cast(last); } else { first = &waiter; } @@ -104,7 +135,11 @@ void Event::wait() { last = &waiter; dispatcher->dispatch(); assert(waiter.context == dispatcher->getCurrentContext()); + assert( waiter.context->interruptProcedure == nullptr); assert(dispatcher != nullptr); + if (waiter.interrupted) { + throw InterruptedException(); + } } } diff --git a/src/System/EventLock.cpp b/src/System/EventLock.cpp old mode 100644 new mode 100755 diff --git a/src/System/EventLock.h b/src/System/EventLock.h old mode 100644 new mode 100755 index e44532bb..e2dedf6b --- a/src/System/EventLock.h +++ b/src/System/EventLock.h @@ -25,6 +25,7 @@ class EventLock { public: explicit EventLock(Event& event); ~EventLock(); + EventLock& operator=(const EventLock&) = delete; private: Event& event; diff --git a/src/System/InterruptedException.cpp b/src/System/InterruptedException.cpp index 4456177f..186904ca 100755 --- a/src/System/InterruptedException.cpp +++ b/src/System/InterruptedException.cpp @@ -16,3 +16,7 @@ // along with Bytecoin. If not, see . #include "InterruptedException.h" + +namespace { +char suppressMSVCWarningLNK4221; +} diff --git a/src/System/InterruptedException.h b/src/System/InterruptedException.h index 81a34e0d..b0085525 100755 --- a/src/System/InterruptedException.h +++ b/src/System/InterruptedException.h @@ -22,6 +22,10 @@ namespace System { class InterruptedException : public std::exception { + public: + const char* what() const throw() { + return "interrupted"; + } }; } diff --git a/src/System/Ipv4Address.cpp b/src/System/Ipv4Address.cpp index 75e9fcee..bad6e95a 100755 --- a/src/System/Ipv4Address.cpp +++ b/src/System/Ipv4Address.cpp @@ -22,7 +22,7 @@ namespace System { namespace { -uint8_t readUint8(const std::string& source, std::size_t& offset) { +uint8_t readUint8(const std::string& source, size_t& offset) { if (offset == source.size() || source[offset] < '0' || source[offset] > '9') { throw std::runtime_error("Unable to read value from string"); } @@ -58,7 +58,7 @@ Ipv4Address::Ipv4Address(uint32_t value) : value(value) { } Ipv4Address::Ipv4Address(const std::string& dottedDecimal) { - std::size_t offset = 0; + size_t offset = 0; value = readUint8(dottedDecimal, offset); if (offset == dottedDecimal.size() || dottedDecimal[offset] != '.') { throw std::runtime_error("Invalid Ipv4 address string"); @@ -115,11 +115,11 @@ bool Ipv4Address::isLoopback() const { bool Ipv4Address::isPrivate() const { return // 10.0.0.0/8 - (value & UINT32_C(0xff000000)) == (UINT32_C(10) << 24) || + (value & 0xff000000) == (10 << 24) || // 172.16.0.0/12 - (value & UINT32_C(0xfff00000)) == ((UINT32_C(172) << 24) | (UINT32_C(16) << 16)) || + (value & 0xfff00000) == ((172 << 24) | (16 << 16)) || // 192.168.0.0/16 - (value & UINT32_C(0xffff0000)) == ((UINT32_C(192) << 24) | (UINT32_C(168) << 16)); + (value & 0xffff0000) == ((192 << 24) | (168 << 16)); } } diff --git a/src/System/Latch.cpp b/src/System/Latch.cpp deleted file mode 100755 index edc0e513..00000000 --- a/src/System/Latch.cpp +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "Latch.h" -#include -#include - -namespace System { - -namespace { - -struct LatchWaiter { - LatchWaiter* next; - void* context; -}; - -} - -Latch::Latch() : dispatcher(nullptr) { -} - -Latch::Latch(Dispatcher& dispatcher) : dispatcher(&dispatcher), value(0) { -} - -Latch::Latch(Latch&& other) : dispatcher(other.dispatcher) { - if (dispatcher != nullptr) { - value = other.value; - if (value > 0) { - assert(other.first == nullptr); - first = nullptr; - } - - other.dispatcher = nullptr; - } -} - -Latch::~Latch() { - assert(dispatcher == nullptr || value == 0 || first == nullptr); -} - -Latch& Latch::operator=(Latch&& other) { - assert(dispatcher == nullptr || value == 0 || first == nullptr); - dispatcher = other.dispatcher; - if (dispatcher != nullptr) { - value = other.value; - if (value > 0) { - assert(other.first == nullptr); - first = nullptr; - } - - other.dispatcher = nullptr; - } - - return *this; -} - -std::size_t Latch::get() const { - assert(dispatcher != nullptr); - return value; -} - -void Latch::increase(std::size_t value) { - assert(dispatcher != nullptr); - if (value > 0) { - if (this->value == 0) { - first = nullptr; - } - - this->value += value; - } -} - -void Latch::decrease(std::size_t value) { - assert(dispatcher != nullptr); - if (value > 0) { - assert(value <= this->value); - if (this->value > 0) { - this->value -= value; - if (this->value == 0) { - for (LatchWaiter* waiter = static_cast(first); waiter != nullptr; waiter = waiter->next) { - dispatcher->pushContext(waiter->context); - } - } - } - } -} - -void Latch::wait() { - assert(dispatcher != nullptr); - if (value > 0) { - LatchWaiter waiter = {nullptr, dispatcher->getCurrentContext()}; - if (first != nullptr) { - static_cast(last)->next = &waiter; - } else { - first = &waiter; - } - - last = &waiter; - dispatcher->dispatch(); - assert(waiter.context == dispatcher->getCurrentContext()); - assert(dispatcher != nullptr); - } -} - -} diff --git a/src/CryptoNote/MultisignatureOutput.h b/src/System/OperationTimeout.h similarity index 55% rename from src/CryptoNote/MultisignatureOutput.h rename to src/System/OperationTimeout.h index db1006ef..cb7cedaf 100755 --- a/src/CryptoNote/MultisignatureOutput.h +++ b/src/System/OperationTimeout.h @@ -17,24 +17,34 @@ #pragma once -#include "../crypto/crypto.h" +#include +#include +#include -namespace CryptoNote { +namespace System { -class MultisignatureOutput { +template class OperationTimeout { public: - MultisignatureOutput(uint64_t amount, std::vector&& keys, uint32_t requiredSignatureCount); - MultisignatureOutput(const MultisignatureOutput& other) = delete; - MultisignatureOutput& operator=(const MultisignatureOutput& other) = delete; - uint64_t getAmount() const; - uint32_t getKeyCount() const; - const crypto::public_key& getKey(uint32_t index) const; - uint32_t getRequiredSignatureCount() const; + OperationTimeout(Dispatcher& dispatcher, T& object, std::chrono::nanoseconds timeout) : + object(object), timerContext(dispatcher), timeoutTimer(dispatcher) { + timerContext.spawn([this, timeout]() { + try { + timeoutTimer.sleep(timeout); + timerContext.interrupt(); + } catch (std::exception&) { + } + }); + } + + ~OperationTimeout() { + timerContext.interrupt(); + timerContext.wait(); + } private: - uint64_t amount; - std::vector keys; - uint32_t requiredSignatureCount; + T& object; + ContextGroup timerContext; + Timer timeoutTimer; }; } diff --git a/src/System/RemoteEventLock.cpp b/src/System/RemoteEventLock.cpp new file mode 100755 index 00000000..3082ea00 --- /dev/null +++ b/src/System/RemoteEventLock.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "RemoteEventLock.h" +#include +#include +#include +#include +#include + +namespace System { + +RemoteEventLock::RemoteEventLock(Dispatcher& dispatcher, Event& event) : dispatcher(dispatcher), event(event) { + std::mutex mutex; + std::condition_variable condition; + bool locked = false; + + dispatcher.remoteSpawn([&]() { + while (!event.get()) { + event.wait(); + } + + event.clear(); + mutex.lock(); + locked = true; + condition.notify_one(); + mutex.unlock(); + }); + + std::unique_lock lock(mutex); + while (!locked) { + condition.wait(lock); + } +} + +RemoteEventLock::~RemoteEventLock() { + Event* eventPointer = &event; + dispatcher.remoteSpawn([=]() { + assert(!eventPointer->get()); + eventPointer->set(); + }); +} + +} diff --git a/src/System/RemoteEventLock.h b/src/System/RemoteEventLock.h new file mode 100755 index 00000000..a3b6a3eb --- /dev/null +++ b/src/System/RemoteEventLock.h @@ -0,0 +1,35 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +namespace System { + +class Dispatcher; +class Event; + +class RemoteEventLock { +public: + RemoteEventLock(Dispatcher& dispatcher, Event& event); + ~RemoteEventLock(); + +private: + Dispatcher& dispatcher; + Event& event; +}; + +} diff --git a/src/transfers/BlockchainSynchronizer.cpp b/src/Transfers/BlockchainSynchronizer.cpp old mode 100644 new mode 100755 similarity index 89% rename from src/transfers/BlockchainSynchronizer.cpp rename to src/Transfers/BlockchainSynchronizer.cpp index cadf8a46..9876fc2a --- a/src/transfers/BlockchainSynchronizer.cpp +++ b/src/Transfers/BlockchainSynchronizer.cpp @@ -16,12 +16,16 @@ // along with Bytecoin. If not, see . #include "BlockchainSynchronizer.h" -#include "cryptonote_core/TransactionApi.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include -#include -#include +#include +#include +#include +#include + +#include "CryptoNoteCore/TransactionApi.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" + +using namespace Crypto; namespace { @@ -36,7 +40,7 @@ inline std::vector stringToVector(const std::string& s) { namespace CryptoNote { -BlockchainSynchronizer::BlockchainSynchronizer(INode& node, const crypto::hash& genesisBlockHash) : +BlockchainSynchronizer::BlockchainSynchronizer(INode& node, const Hash& genesisBlockHash) : m_node(node), m_genesisBlockHash(genesisBlockHash), m_currentState(State::stopped), m_futureState(State::stopped), shouldSyncConsumersPool(true) { } @@ -89,7 +93,7 @@ void BlockchainSynchronizer::save(std::ostream& os) { } void BlockchainSynchronizer::load(std::istream& in) { - crypto::hash genesisBlockHash; + Hash genesisBlockHash; in.read(reinterpret_cast(&genesisBlockHash), sizeof(genesisBlockHash)); if (genesisBlockHash != m_genesisBlockHash) { throw std::runtime_error("Genesis block hash does not match stored state"); @@ -194,10 +198,10 @@ void BlockchainSynchronizer::stop() { workingThread->join(); } - workingThread.release(); + workingThread.reset(); } -void BlockchainSynchronizer::lastKnownBlockHeightUpdated(uint64_t height) { +void BlockchainSynchronizer::lastKnownBlockHeightUpdated(uint32_t height) { setFutureState(State::blockchainSync); } @@ -208,11 +212,11 @@ void BlockchainSynchronizer::poolChanged() { BlockchainSynchronizer::GetPoolRequest BlockchainSynchronizer::getUnionPoolHistory() { GetPoolRequest request; - std::unordered_set unionHistory; + std::unordered_set unionHistory; { std::unique_lock lk(m_consumersMutex); for (auto& consumer : m_consumers) { - std::vector consumerKnownIds; + std::vector consumerKnownIds; consumer.first->getKnownPoolTxIds(consumerKnownIds); for (auto& txId : consumerKnownIds) { unionHistory.insert(txId); @@ -238,7 +242,7 @@ BlockchainSynchronizer::GetPoolRequest BlockchainSynchronizer::getIntersectedPoo ++it; for (; it != m_consumers.end(); ++it) { //iterate over consumers - std::vector consumerKnownIds; + std::vector consumerKnownIds; it->first->getKnownPoolTxIds(consumerKnownIds); for (auto itReq = request.knownTxIds.begin(); itReq != request.knownTxIds.end();) { //iterate over intersection if (std::count(consumerKnownIds.begin(), consumerKnownIds.end(), *itReq) == 0) { //consumer doesn't contain id from intersection, so delete this id from intersection @@ -289,7 +293,8 @@ void BlockchainSynchronizer::startBlockchainSync() { asyncOperationCompleted = std::promise(); asyncOperationWaitFuture = asyncOperationCompleted.get_future(); - m_node.queryBlocks(std::move(req.knownBlocks), req.syncStart.timestamp, response.newBlocks, response.startHeight, + m_node.queryBlocks + (std::move(req.knownBlocks), req.syncStart.timestamp, response.newBlocks, response.startHeight, std::bind(&BlockchainSynchronizer::onGetBlocksCompleted, this, std::placeholders::_1)); std::error_code ec = asyncOperationWaitFuture.get(); @@ -297,11 +302,9 @@ void BlockchainSynchronizer::startBlockchainSync() { if (ec) { setFutureStateIf(State::idle, std::bind( [](State futureState) -> bool { - return futureState != State::stopped; - }, std::ref(m_futureState))); - m_observerManager.notify( - &IBlockchainSynchronizerObserver::synchronizationCompleted, - ec); + return futureState != State::stopped; + }, std::ref(m_futureState))); + m_observerManager.notify(&IBlockchainSynchronizerObserver::synchronizationCompleted, ec); } else { processBlocks(response); } @@ -324,48 +327,37 @@ void BlockchainSynchronizer::onGetBlocksCompleted(std::error_code ec) { } void BlockchainSynchronizer::processBlocks(GetBlocksResponse& response) { - auto newHeight = response.startHeight + response.newBlocks.size(); + uint32_t newHeight = response.startHeight + static_cast(response.newBlocks.size()); BlockchainInterval interval; interval.startHeight = response.startHeight; std::vector blocks; - // parse blocks - for (const auto& block : response.newBlocks) { + for (auto& block : response.newBlocks) { if (checkIfShouldStop()) { break; } CompleteBlock completeBlock; completeBlock.blockHash = block.blockHash; interval.blocks.push_back(completeBlock.blockHash); - if (!block.block.empty()) { - Block parsedBlock; - if (!parse_and_validate_block_from_blob(block.block, parsedBlock)) { - setFutureStateIf(State::idle, std::bind( - [](State futureState) -> bool { - return futureState != State::stopped; - }, std::ref(m_futureState))); - m_observerManager.notify( - &IBlockchainSynchronizerObserver::synchronizationCompleted, - std::make_error_code(std::errc::invalid_argument)); - return; - } - - completeBlock.block = std::move(parsedBlock); - - completeBlock.transactions.push_back(createTransaction(completeBlock.block->minerTx)); + if (block.hasBlock) { + completeBlock.block = std::move(block.block); + completeBlock.transactions.push_back(createTransactionPrefix(completeBlock.block->baseTransaction)); try { - for (const auto& txblob : block.txs) { - completeBlock.transactions.push_back(createTransaction(stringToVector(txblob))); + for (const auto& txShortInfo : block.txsShortInfo) { + completeBlock.transactions.push_back(createTransactionPrefix(txShortInfo.txPrefix, + reinterpret_cast(txShortInfo.txId))); } - } catch (std::exception &) { + } catch (std::exception&) { setFutureStateIf(State::idle, std::bind( [](State futureState) -> bool { - return futureState != State::stopped; - }, std::ref(m_futureState))); + return futureState != State::stopped; + }, std::ref(m_futureState))); + m_observerManager.notify( &IBlockchainSynchronizerObserver::synchronizationCompleted, std::make_error_code(std::errc::invalid_argument)); + return; } } @@ -430,17 +422,17 @@ BlockchainSynchronizer::UpdateConsumersResult BlockchainSynchronizer::updateCons } if (result.hasNewBlocks) { - size_t startOffset = result.newBlockHeight - interval.startHeight; + uint32_t startOffset = result.newBlockHeight - interval.startHeight; // update consumer if (kv.first->onNewBlocks( blocks.data() + startOffset, result.newBlockHeight, - blocks.size() - startOffset)) { + static_cast(blocks.size()) - startOffset)) { // update state if consumer succeeded kv.second->addBlocks( interval.blocks.data() + startOffset, result.newBlockHeight, - interval.blocks.size() - startOffset); + static_cast(interval.blocks.size()) - startOffset); smthChanged = true; } else { return UpdateConsumersResult::errorOccured; diff --git a/src/transfers/BlockchainSynchronizer.h b/src/Transfers/BlockchainSynchronizer.h old mode 100644 new mode 100755 similarity index 88% rename from src/transfers/BlockchainSynchronizer.h rename to src/Transfers/BlockchainSynchronizer.h index feeb508c..10591524 --- a/src/transfers/BlockchainSynchronizer.h +++ b/src/Transfers/BlockchainSynchronizer.h @@ -35,7 +35,7 @@ class BlockchainSynchronizer : public INodeObserver { public: - BlockchainSynchronizer(INode& node, const crypto::hash& genesisBlockHash); + BlockchainSynchronizer(INode& node, const Crypto::Hash& genesisBlockHash); ~BlockchainSynchronizer(); // IBlockchainSynchronizer @@ -51,14 +51,14 @@ public: virtual void load(std::istream& in) override; // INodeObserver - virtual void lastKnownBlockHeightUpdated(uint64_t height) override; + virtual void lastKnownBlockHeightUpdated(uint32_t height) override; virtual void poolChanged() override; private: struct GetBlocksResponse { - uint64_t startHeight; - std::list newBlocks; + uint32_t startHeight; + std::vector newBlocks; }; struct GetBlocksRequest { @@ -67,18 +67,18 @@ private: syncStart.height = 0; } SynchronizationStart syncStart; - std::list knownBlocks; + std::vector knownBlocks; }; struct GetPoolResponse { bool isLastKnownBlockActual; - std::vector newTxs; - std::vector deletedTxIds; + std::vector> newTxs; + std::vector deletedTxIds; }; struct GetPoolRequest { - std::vector knownTxIds; - crypto::hash lastKnownBlock; + std::vector knownTxIds; + Crypto::Hash lastKnownBlock; }; @@ -123,9 +123,9 @@ private: ConsumersMap m_consumers; INode& m_node; - const crypto::hash m_genesisBlockHash; + const Crypto::Hash m_genesisBlockHash; - crypto::hash lastBlockId; + Crypto::Hash lastBlockId; State m_currentState; State m_futureState; diff --git a/src/transfers/CommonTypes.h b/src/Transfers/CommonTypes.h old mode 100644 new mode 100755 similarity index 93% rename from src/transfers/CommonTypes.h rename to src/Transfers/CommonTypes.h index f349c8ea..9f0ac95d --- a/src/transfers/CommonTypes.h +++ b/src/Transfers/CommonTypes.h @@ -29,12 +29,12 @@ namespace CryptoNote { struct BlockchainInterval { - uint64_t startHeight; - std::vector blocks; + uint32_t startHeight; + std::vector blocks; }; struct CompleteBlock { - crypto::hash blockHash; + Crypto::Hash blockHash; boost::optional block; // first transaction is always coinbase std::list> transactions; diff --git a/src/transfers/IBlockchainSynchronizer.h b/src/Transfers/IBlockchainSynchronizer.h old mode 100644 new mode 100755 similarity index 74% rename from src/transfers/IBlockchainSynchronizer.h rename to src/Transfers/IBlockchainSynchronizer.h index 6842ed54..9deb7122 --- a/src/transfers/IBlockchainSynchronizer.h +++ b/src/Transfers/IBlockchainSynchronizer.h @@ -21,7 +21,7 @@ #include #include "crypto/crypto.h" -#include "cryptonote_core/cryptonote_basic.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" #include "IObservable.h" #include "IStreamSerializable.h" @@ -33,18 +33,18 @@ struct CompleteBlock; class IBlockchainSynchronizerObserver { public: - virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total) {} + virtual void synchronizationProgressUpdated(uint32_t current, uint32_t total) {} virtual void synchronizationCompleted(std::error_code result) {} }; class IBlockchainConsumer { public: - + virtual ~IBlockchainConsumer() {} virtual SynchronizationStart getSyncStart() = 0; - virtual void getKnownPoolTxIds(std::vector& ids) = 0; - virtual void onBlockchainDetach(uint64_t height) = 0; - virtual bool onNewBlocks(const CompleteBlock* blocks, uint64_t startHeight, size_t count) = 0; - virtual std::error_code onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) = 0; + virtual void getKnownPoolTxIds(std::vector& ids) = 0; + virtual void onBlockchainDetach(uint32_t height) = 0; + virtual bool onNewBlocks(const CompleteBlock* blocks, uint32_t startHeight, uint32_t count) = 0; + virtual std::error_code onPoolUpdated(const std::vector>& addedTransactions, const std::vector& deletedTransactions) = 0; }; diff --git a/src/transfers/IObservableImpl.h b/src/Transfers/IObservableImpl.h old mode 100644 new mode 100755 similarity index 95% rename from src/transfers/IObservableImpl.h rename to src/Transfers/IObservableImpl.h index 64868b1b..a3085c39 --- a/src/transfers/IObservableImpl.h +++ b/src/Transfers/IObservableImpl.h @@ -34,7 +34,7 @@ public: } protected: - tools::ObserverManager m_observerManager; + Tools::ObserverManager m_observerManager; }; } diff --git a/src/transfers/SynchronizationState.cpp b/src/Transfers/SynchronizationState.cpp old mode 100644 new mode 100755 similarity index 67% rename from src/transfers/SynchronizationState.cpp rename to src/Transfers/SynchronizationState.cpp index f537cf27..4b3d2224 --- a/src/transfers/SynchronizationState.cpp +++ b/src/Transfers/SynchronizationState.cpp @@ -17,23 +17,26 @@ #include "SynchronizationState.h" -#include "serialization/BinaryInputStreamSerializer.h" -#include "serialization/BinaryOutputStreamSerializer.h" -#include "cryptonote_core/cryptonote_serialization.h" +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "Serialization/BinaryInputStreamSerializer.h" +#include "Serialization/BinaryOutputStreamSerializer.h" +#include "CryptoNoteCore/CryptoNoteSerialization.h" + +using namespace Common; namespace CryptoNote { -SynchronizationState::ShortHistory SynchronizationState::getShortHistory(size_t localHeight) const { - +SynchronizationState::ShortHistory SynchronizationState::getShortHistory(uint32_t localHeight) const { ShortHistory history; - size_t i = 0; - size_t current_multiplier = 1; - size_t sz = std::min(m_blockchain.size(), localHeight + 1); + uint32_t i = 0; + uint32_t current_multiplier = 1; + uint32_t sz = std::min(static_cast(m_blockchain.size()), localHeight + 1); if (!sz) return history; - size_t current_back_offset = 1; + uint32_t current_back_offset = 1; bool genesis_included = false; while (current_back_offset < sz) { @@ -59,10 +62,10 @@ SynchronizationState::CheckResult SynchronizationState::checkInterval(const Bloc CheckResult result = { false, 0, false, 0 }; - size_t intervalEnd = interval.startHeight + interval.blocks.size(); - size_t iterationEnd = std::min(m_blockchain.size(), intervalEnd); + uint32_t intervalEnd = interval.startHeight + static_cast(interval.blocks.size()); + uint32_t iterationEnd = std::min(static_cast(m_blockchain.size()), intervalEnd); - for (size_t i = interval.startHeight; i < iterationEnd; ++i) { + for (uint32_t i = interval.startHeight; i < iterationEnd; ++i) { if (m_blockchain[i] != interval.blocks[i - interval.startHeight]) { result.detachRequired = true; result.detachHeight = i; @@ -78,35 +81,37 @@ SynchronizationState::CheckResult SynchronizationState::checkInterval(const Bloc if (intervalEnd > m_blockchain.size()) { result.hasNewBlocks = true; - result.newBlockHeight = m_blockchain.size(); + result.newBlockHeight = static_cast(m_blockchain.size()); } return result; } -void SynchronizationState::detach(uint64_t height) { +void SynchronizationState::detach(uint32_t height) { assert(height < m_blockchain.size()); m_blockchain.resize(height); } -void SynchronizationState::addBlocks(const crypto::hash* blockHashes, uint64_t height, size_t count) { +void SynchronizationState::addBlocks(const Crypto::Hash* blockHashes, uint32_t height, uint32_t count) { assert(blockHashes); auto size = m_blockchain.size(); assert( size == height); m_blockchain.insert(m_blockchain.end(), blockHashes, blockHashes + count); } -uint64_t SynchronizationState::getHeight() const { - return m_blockchain.size(); +uint32_t SynchronizationState::getHeight() const { + return static_cast(m_blockchain.size()); } void SynchronizationState::save(std::ostream& os) { - CryptoNote::BinaryOutputStreamSerializer s(os); + StdOutputStream stream(os); + CryptoNote::BinaryOutputStreamSerializer s(stream); serialize(s, "state"); } void SynchronizationState::load(std::istream& in) { - CryptoNote::BinaryInputStreamSerializer s(in); + StdInputStream stream(in); + CryptoNote::BinaryInputStreamSerializer s(stream); serialize(s, "state"); } diff --git a/src/transfers/SynchronizationState.h b/src/Transfers/SynchronizationState.h old mode 100644 new mode 100755 similarity index 75% rename from src/transfers/SynchronizationState.h rename to src/Transfers/SynchronizationState.h index 9e55345a..13990fa0 --- a/src/transfers/SynchronizationState.h +++ b/src/Transfers/SynchronizationState.h @@ -19,7 +19,7 @@ #include "CommonTypes.h" #include "IStreamSerializable.h" -#include "serialization/ISerializer.h" +#include "Serialization/ISerializer.h" #include #include @@ -30,23 +30,23 @@ public: struct CheckResult { bool detachRequired; - uint64_t detachHeight; + uint32_t detachHeight; bool hasNewBlocks; - uint64_t newBlockHeight; + uint32_t newBlockHeight; }; - typedef std::list ShortHistory; + typedef std::vector ShortHistory; - explicit SynchronizationState(const crypto::hash& genesisBlockHash) { + explicit SynchronizationState(const Crypto::Hash& genesisBlockHash) { m_blockchain.push_back(genesisBlockHash); } - ShortHistory getShortHistory(size_t localHeight) const; + ShortHistory getShortHistory(uint32_t localHeight) const; CheckResult checkInterval(const BlockchainInterval& interval) const; - void detach(uint64_t height); - void addBlocks(const crypto::hash* blockHashes, uint64_t height, size_t count); - uint64_t getHeight() const; + void detach(uint32_t height); + void addBlocks(const Crypto::Hash* blockHashes, uint32_t height, uint32_t count); + uint32_t getHeight() const; // IStreamSerializable virtual void save(std::ostream& os) override; @@ -57,7 +57,7 @@ public: private: - std::vector m_blockchain; + std::vector m_blockchain; }; } diff --git a/src/transfers/TransfersConsumer.cpp b/src/Transfers/TransfersConsumer.cpp old mode 100644 new mode 100755 similarity index 81% rename from src/transfers/TransfersConsumer.cpp rename to src/Transfers/TransfersConsumer.cpp index ab2b08f7..f68437c4 --- a/src/transfers/TransfersConsumer.cpp +++ b/src/Transfers/TransfersConsumer.cpp @@ -19,19 +19,21 @@ #include "CommonTypes.h" #include "Common/BlockingQueue.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/TransactionApi.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteCore/TransactionApi.h" -#include "IWallet.h" +#include "IWalletLegacy.h" #include "INode.h" #include +using namespace Crypto; + namespace { using namespace CryptoNote; void checkOutputKey( - const crypto::key_derivation& derivation, + const KeyDerivation& derivation, const PublicKey& key, size_t keyIndex, size_t outputIndex, @@ -39,9 +41,7 @@ void checkOutputKey( std::unordered_map>& outputs) { PublicKey spendKey; - crypto::underive_public_key(derivation, keyIndex, - reinterpret_cast(key), - reinterpret_cast(spendKey)); + underive_public_key(derivation, keyIndex, key, spendKey); if (spendKeys.find(spendKey) != spendKeys.end()) { outputs[spendKey].push_back(static_cast(outputIndex)); @@ -56,12 +56,9 @@ void findMyOutputs( std::unordered_map>& outputs) { auto txPublicKey = tx.getTransactionPublicKey(); - crypto::key_derivation derivation; + KeyDerivation derivation; - if (!crypto::generate_key_derivation( - reinterpret_cast(txPublicKey), - reinterpret_cast(viewSecretKey), - derivation)) { + if (!generate_key_derivation( txPublicKey, viewSecretKey, derivation)) { return; } @@ -74,15 +71,17 @@ void findMyOutputs( if (outType == TransactionTypes::OutputType::Key) { - TransactionTypes::OutputKey out; - tx.getOutput(idx, out); + uint64_t amount; + KeyOutput out; + tx.getOutput(idx, out, amount); checkOutputKey(derivation, out.key, keyIndex, idx, spendKeys, outputs); ++keyIndex; } else if (outType == TransactionTypes::OutputType::Multisignature) { - TransactionTypes::OutputMultisignature out; - tx.getOutput(idx, out); + uint64_t amount; + MultisignatureOutput out; + tx.getOutput(idx, out, amount); for (const auto& key : out.keys) { checkOutputKey(derivation, key, idx, idx, spendKeys, outputs); ++keyIndex; @@ -116,19 +115,19 @@ ITransfersSubscription& TransfersConsumer::addSubscription(const AccountSubscrip return *res; } -bool TransfersConsumer::removeSubscription(const AccountAddress& address) { +bool TransfersConsumer::removeSubscription(const AccountPublicAddress& address) { m_subscriptions.erase(address.spendPublicKey); m_spendKeys.erase(address.spendPublicKey); updateSyncStart(); return m_subscriptions.empty(); } -ITransfersSubscription* TransfersConsumer::getSubscription(const AccountAddress& acc) { +ITransfersSubscription* TransfersConsumer::getSubscription(const AccountPublicAddress& acc) { auto it = m_subscriptions.find(acc.spendPublicKey); return it == m_subscriptions.end() ? nullptr : it->second.get(); } -void TransfersConsumer::getSubscriptions(std::vector& subscriptions) { +void TransfersConsumer::getSubscriptions(std::vector& subscriptions) { for (const auto& kv : m_subscriptions) { subscriptions.push_back(kv.second->getAddress()); } @@ -137,7 +136,7 @@ void TransfersConsumer::getSubscriptions(std::vector& subscripti void TransfersConsumer::updateSyncStart() { SynchronizationStart start; - start.height = std::numeric_limits::max(); + start.height = std::numeric_limits::max(); start.timestamp = std::numeric_limits::max(); for (const auto& kv : m_subscriptions) { @@ -153,13 +152,13 @@ SynchronizationStart TransfersConsumer::getSyncStart() { return m_syncStart; } -void TransfersConsumer::onBlockchainDetach(uint64_t height) { +void TransfersConsumer::onBlockchainDetach(uint32_t height) { for (const auto& kv : m_subscriptions) { kv.second->onBlockchainDetach(height); } } -bool TransfersConsumer::onNewBlocks(const CompleteBlock* blocks, uint64_t startHeight, size_t count) { +bool TransfersConsumer::onNewBlocks(const CompleteBlock* blocks, uint32_t startHeight, uint32_t count) { assert(blocks); struct Tx { @@ -182,7 +181,7 @@ bool TransfersConsumer::onNewBlocks(const CompleteBlock* blocks, uint64_t startH std::atomic stopProcessing(false); auto pushingThread = std::async(std::launch::async, [&] { - for (size_t i = 0; i < count && !stopProcessing; ++i) { + for( uint32_t i = 0; i < count && !stopProcessing; ++i) { const auto& block = blocks[i].block; if (!block.is_initialized()) { @@ -201,7 +200,7 @@ bool TransfersConsumer::onNewBlocks(const CompleteBlock* blocks, uint64_t startH for (const auto& tx : blocks[i].transactions) { auto pubKey = tx->getTransactionPublicKey(); - if (*reinterpret_cast(&pubKey) == CryptoNote::null_pkey) { + if (pubKey == NULL_PUBLIC_KEY) { ++blockInfo.transactionIndex; continue; } @@ -282,14 +281,13 @@ bool TransfersConsumer::onNewBlocks(const CompleteBlock* blocks, uint64_t startH return true; } -std::error_code TransfersConsumer::onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) { +std::error_code TransfersConsumer::onPoolUpdated(const std::vector>& addedTransactions, const std::vector& deletedTransactions) { BlockInfo unconfirmedBlockInfo; unconfirmedBlockInfo.timestamp = 0; - unconfirmedBlockInfo.height = UNCONFIRMED_TRANSACTION_HEIGHT; + unconfirmedBlockInfo.height = WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT; std::error_code processingError; for (auto& cryptonoteTransaction : addedTransactions) { - auto transaction = CryptoNote::createTransaction(cryptonoteTransaction); - processingError = processTransaction(unconfirmedBlockInfo, *transaction.get()); + processingError = processTransaction(unconfirmedBlockInfo, *cryptonoteTransaction.get()); if (processingError) { break; } @@ -297,7 +295,7 @@ std::error_code TransfersConsumer::onPoolUpdated(const std::vector& if (processingError) { for (auto& sub : m_subscriptions) { - sub.second->onError(processingError, UNCONFIRMED_TRANSACTION_HEIGHT); + sub.second->onError(processingError, WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT); } return processingError; @@ -312,11 +310,11 @@ std::error_code TransfersConsumer::onPoolUpdated(const std::vector& return std::error_code(); } -void TransfersConsumer::getKnownPoolTxIds(std::vector& ids) { +void TransfersConsumer::getKnownPoolTxIds(std::vector& ids) { ids.clear(); - std::unordered_set knownIds; + std::unordered_set knownIds; for (auto& sub : m_subscriptions) { - std::vector subscriptionUnconfirmedTxIds; + std::vector subscriptionUnconfirmedTxIds; sub.second->getContainer().getUnconfirmedTransactions(subscriptionUnconfirmedTxIds); knownIds.insert(subscriptionUnconfirmedTxIds.begin(), subscriptionUnconfirmedTxIds.end()); } @@ -330,7 +328,7 @@ std::error_code createTransfers( const BlockInfo& blockInfo, const ITransactionReader& tx, const std::vector& outputs, - const std::vector& globalIdxs, + const std::vector& globalIdxs, std::vector& transfers) { auto txPubKey = tx.getTransactionPublicKey(); @@ -354,32 +352,34 @@ std::error_code createTransfers( info.type = outType; info.transactionPublicKey = txPubKey; info.outputInTransaction = idx; - info.globalOutputIndex = (blockInfo.height == UNCONFIRMED_TRANSACTION_HEIGHT) ? + info.globalOutputIndex = (blockInfo.height == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) ? UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX : globalIdxs[idx]; if (outType == TransactionTypes::OutputType::Key) { - TransactionTypes::OutputKey out; - tx.getOutput(idx, out); + uint64_t amount; + KeyOutput out; + tx.getOutput(idx, out, amount); CryptoNote::KeyPair in_ephemeral; CryptoNote::generate_key_image_helper( - reinterpret_cast(account), - reinterpret_cast(txPubKey), + account, + txPubKey, idx, in_ephemeral, - reinterpret_cast(info.keyImage)); + info.keyImage); - assert(out.key == reinterpret_cast(in_ephemeral.pub)); + assert(out.key == reinterpret_cast(in_ephemeral.publicKey)); - info.amount = out.amount; + info.amount = amount; info.outputKey = out.key; } else if (outType == TransactionTypes::OutputType::Multisignature) { - TransactionTypes::OutputMultisignature out; - tx.getOutput(idx, out); + uint64_t amount; + MultisignatureOutput out; + tx.getOutput(idx, out, amount); - info.amount = out.amount; - info.requiredSignatures = out.requiredSignatures; + info.amount = amount; + info.requiredSignatures = out.requiredSignatureCount; } transfers.push_back(info); @@ -398,8 +398,8 @@ std::error_code TransfersConsumer::preprocessOutputs(const BlockInfo& blockInfo, std::error_code errorCode; auto txHash = tx.getTransactionHash(); - if (blockInfo.height != UNCONFIRMED_TRANSACTION_HEIGHT) { - errorCode = getGlobalIndices(reinterpret_cast(txHash), info.globalIdxs); + if (blockInfo.height != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { + errorCode = getGlobalIndices(reinterpret_cast(txHash), info.globalIdxs); if (errorCode) { return errorCode; } @@ -448,13 +448,13 @@ std::error_code TransfersConsumer::processTransaction(const BlockInfo& blockInfo std::error_code TransfersConsumer::processOutputs(const BlockInfo& blockInfo, TransfersSubscription& sub, - const ITransactionReader& tx, const std::vector& transfers, const std::vector& globalIdxs) { + const ITransactionReader& tx, const std::vector& transfers, const std::vector& globalIdxs) { - if (blockInfo.height != UNCONFIRMED_TRANSACTION_HEIGHT) { + if (blockInfo.height != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { TransactionInformation subscribtionTxInfo; int64_t txBalance; if (sub.getContainer().getTransactionInformation(tx.getTransactionHash(), subscribtionTxInfo, txBalance)) { - if (subscribtionTxInfo.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { + if (subscribtionTxInfo.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { // pool->blockchain sub.markTransactionConfirmed(blockInfo, tx.getTransactionHash(), globalIdxs); return std::error_code(); @@ -471,7 +471,7 @@ std::error_code TransfersConsumer::processOutputs(const BlockInfo& blockInfo, Tr } -std::error_code TransfersConsumer::getGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices) { +std::error_code TransfersConsumer::getGlobalIndices(const Hash& transactionHash, std::vector& outsGlobalIndices) { std::promise prom; std::future f = prom.get_future(); diff --git a/src/transfers/TransfersConsumer.h b/src/Transfers/TransfersConsumer.h old mode 100644 new mode 100755 similarity index 66% rename from src/transfers/TransfersConsumer.h rename to src/Transfers/TransfersConsumer.h index bff34238..e8166bec --- a/src/transfers/TransfersConsumer.h +++ b/src/Transfers/TransfersConsumer.h @@ -36,20 +36,20 @@ class TransfersConsumer : public IBlockchainConsumer { public: - TransfersConsumer(const CryptoNote::Currency& currency, INode& node, const SecretKey& viewSecret); + TransfersConsumer(const CryptoNote::Currency& currency, INode& node, const Crypto::SecretKey& viewSecret); ITransfersSubscription& addSubscription(const AccountSubscription& subscription); // returns true if no subscribers left - bool removeSubscription(const AccountAddress& address); - ITransfersSubscription* getSubscription(const AccountAddress& acc); - void getSubscriptions(std::vector& subscriptions); + bool removeSubscription(const AccountPublicAddress& address); + ITransfersSubscription* getSubscription(const AccountPublicAddress& acc); + void getSubscriptions(std::vector& subscriptions); // IBlockchainConsumer virtual SynchronizationStart getSyncStart() override; - virtual void onBlockchainDetach(uint64_t height) override; - virtual bool onNewBlocks(const CompleteBlock* blocks, uint64_t startHeight, size_t count) override; - virtual std::error_code onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) override; - virtual void getKnownPoolTxIds(std::vector& ids) override; + virtual void onBlockchainDetach(uint32_t height) override; + virtual bool onNewBlocks(const CompleteBlock* blocks, uint32_t startHeight, uint32_t count) override; + virtual std::error_code onPoolUpdated(const std::vector>& addedTransactions, const std::vector& deletedTransactions) override; + virtual void getKnownPoolTxIds(std::vector& ids) override; private: @@ -61,25 +61,25 @@ private: } struct PreprocessInfo { - std::unordered_map> outputs; - std::vector globalIdxs; + std::unordered_map> outputs; + std::vector globalIdxs; }; std::error_code preprocessOutputs(const BlockInfo& blockInfo, const ITransactionReader& tx, PreprocessInfo& info); std::error_code processTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx); std::error_code processTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx, const PreprocessInfo& info); std::error_code processOutputs(const BlockInfo& blockInfo, TransfersSubscription& sub, const ITransactionReader& tx, - const std::vector& outputs, const std::vector& globalIdxs); + const std::vector& outputs, const std::vector& globalIdxs); - std::error_code getGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices); + std::error_code getGlobalIndices(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices); void updateSyncStart(); SynchronizationStart m_syncStart; - const SecretKey m_viewSecret; + const Crypto::SecretKey m_viewSecret; // map { spend public key -> subscription } - std::unordered_map> m_subscriptions; - std::unordered_set m_spendKeys; + std::unordered_map> m_subscriptions; + std::unordered_set m_spendKeys; INode& m_node; const CryptoNote::Currency& m_currency; diff --git a/src/transfers/TransfersContainer.cpp b/src/Transfers/TransfersContainer.cpp old mode 100644 new mode 100755 similarity index 92% rename from src/transfers/TransfersContainer.cpp rename to src/Transfers/TransfersContainer.cpp index ed469165..39dba73f --- a/src/transfers/TransfersContainer.cpp +++ b/src/Transfers/TransfersContainer.cpp @@ -16,11 +16,15 @@ // along with Bytecoin. If not, see . #include "TransfersContainer.h" -#include "IWallet.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "IWalletLegacy.h" +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "Serialization/BinaryInputStreamSerializer.h" +#include "Serialization/BinaryOutputStreamSerializer.h" -#include "serialization/BinaryInputStreamSerializer.h" -#include "serialization/BinaryOutputStreamSerializer.h" +using namespace Common; +using namespace Crypto; namespace CryptoNote { @@ -116,7 +120,7 @@ SpentOutputDescriptor::SpentOutputDescriptor(const KeyImage* keyImage) { assign(keyImage); } -SpentOutputDescriptor::SpentOutputDescriptor(uint64_t amount, uint64_t globalOutputIndex) { +SpentOutputDescriptor::SpentOutputDescriptor(uint64_t amount, uint32_t globalOutputIndex) { assign(amount, globalOutputIndex); } @@ -125,7 +129,7 @@ void SpentOutputDescriptor::assign(const KeyImage* keyImage) { m_keyImage = keyImage; } -void SpentOutputDescriptor::assign(uint64_t amount, uint64_t globalOutputIndex) { +void SpentOutputDescriptor::assign(uint64_t amount, uint32_t globalOutputIndex) { m_type = TransactionTypes::OutputType::Multisignature; m_amount = amount; m_globalOutputIndex = globalOutputIndex; @@ -149,7 +153,7 @@ bool SpentOutputDescriptor::operator==(const SpentOutputDescriptor& other) const size_t SpentOutputDescriptor::hash() const { if (m_type == TransactionTypes::OutputType::Key) { static_assert(sizeof(size_t) < sizeof(*m_keyImage), "sizeof(size_t) < sizeof(*m_keyImage)"); - return *reinterpret_cast(m_keyImage->data()); + return *reinterpret_cast(m_keyImage->data); } else if (m_type == TransactionTypes::OutputType::Multisignature) { size_t hashValue = boost::hash_value(m_amount); boost::hash_combine(hashValue, m_globalOutputIndex); @@ -186,7 +190,7 @@ bool TransfersContainer::addTransaction(const BlockInfo& block, const ITransacti addTransaction(block, tx); } - if (block.height != UNCONFIRMED_TRANSACTION_HEIGHT) { + if (block.height != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { m_currentHeight = block.height; } @@ -210,7 +214,7 @@ void TransfersContainer::addTransaction(const BlockInfo& block, const ITransacti txInfo.extra = tx.getExtra(); if (!tx.getPaymentId(txInfo.paymentId)) { - txInfo.paymentId.fill(0); + txInfo.paymentId = NULL_HASH; } auto result = m_transactions.emplace(std::move(txInfo)); @@ -226,7 +230,7 @@ bool TransfersContainer::addTransactionOutputs(const BlockInfo& block, const ITr bool outputsAdded = false; auto txHash = tx.getTransactionHash(); - bool transactionIsUnconfimed = (block.height == UNCONFIRMED_TRANSACTION_HEIGHT); + bool transactionIsUnconfimed = (block.height == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT); for (const auto& transfer : transfers) { assert(transfer.outputInTransaction < tx.getOutputCount()); assert(transfer.type == tx.getOutputType(transfer.outputInTransaction)); @@ -283,7 +287,7 @@ bool TransfersContainer::addTransactionInputs(const BlockInfo& block, const ITra auto inputType = tx.getInputType(i); if (inputType == TransactionTypes::InputType::Key) { - TransactionTypes::InputKey input; + KeyInput input; tx.getInput(i, input); SpentOutputDescriptor descriptor(&input.keyImage); @@ -325,7 +329,7 @@ bool TransfersContainer::addTransactionInputs(const BlockInfo& block, const ITra inputsAdded = true; } else if (inputType == TransactionTypes::InputType::Multisignature) { - TransactionTypes::InputMultisignature input; + MultisignatureInput input; tx.getInput(i, input); auto& outputDescriptorIndex = m_availableTransfers.get(); @@ -351,7 +355,7 @@ bool TransfersContainer::deleteUnconfirmedTransaction(const Hash& transactionHas auto it = m_transactions.find(transactionHash); if (it == m_transactions.end()) { return false; - } else if (it->blockHeight != UNCONFIRMED_TRANSACTION_HEIGHT) { + } else if (it->blockHeight != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { return false; } else { deleteTransactionTransfers(it->transactionHash); @@ -361,9 +365,9 @@ bool TransfersContainer::deleteUnconfirmedTransaction(const Hash& transactionHas } bool TransfersContainer::markTransactionConfirmed(const BlockInfo& block, const Hash& transactionHash, - const std::vector& globalIndices) { - if (block.height == UNCONFIRMED_TRANSACTION_HEIGHT) { - throw std::invalid_argument("Block height equals UNCONFIRMED_TRANSACTION_HEIGHT"); + const std::vector& globalIndices) { + if (block.height == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { + throw std::invalid_argument("Block height equals WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT"); } std::unique_lock lock(m_mutex); @@ -373,7 +377,7 @@ bool TransfersContainer::markTransactionConfirmed(const BlockInfo& block, const return false; } - if (transactionIt->blockHeight != UNCONFIRMED_TRANSACTION_HEIGHT) { + if (transactionIt->blockHeight != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { return false; } @@ -385,7 +389,7 @@ bool TransfersContainer::markTransactionConfirmed(const BlockInfo& block, const auto availableRange = m_unconfirmedTransfers.get().equal_range(transactionHash); for (auto transferIt = availableRange.first; transferIt != availableRange.second; ) { auto transfer = *transferIt; - assert(transfer.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT); + assert(transfer.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT); assert(transfer.globalOutputIndex == UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); if (transfer.outputInTransaction >= globalIndices.size()) { throw std::invalid_argument("Not enough elements in globalIndices"); @@ -419,7 +423,7 @@ bool TransfersContainer::markTransactionConfirmed(const BlockInfo& block, const auto spentRange = spendingTransactionIndex.equal_range(transactionHash); for (auto transferIt = spentRange.first; transferIt != spentRange.second; ++transferIt) { auto transfer = *transferIt; - assert(transfer.spendingBlock.height == UNCONFIRMED_TRANSACTION_HEIGHT); + assert(transfer.spendingBlock.height == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT); transfer.spendingBlock = block; spendingTransactionIndex.replace(transferIt, transfer); @@ -435,7 +439,7 @@ void TransfersContainer::deleteTransactionTransfers(const Hash& transactionHash) auto& spendingTransactionIndex = m_spentTransfers.get(); auto spentTransfersRange = spendingTransactionIndex.equal_range(transactionHash); for (auto it = spentTransfersRange.first; it != spentTransfersRange.second;) { - assert(it->blockHeight != UNCONFIRMED_TRANSACTION_HEIGHT); + assert(it->blockHeight != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT); assert(it->globalOutputIndex != UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); auto result = m_availableTransfers.emplace(static_cast(*it)); @@ -476,7 +480,7 @@ void TransfersContainer::deleteTransactionTransfers(const Hash& transactionHash) */ void TransfersContainer::copyToSpent(const BlockInfo& block, const ITransactionReader& tx, size_t inputIndex, const TransactionOutputInformationEx& output) { - assert(output.blockHeight != UNCONFIRMED_TRANSACTION_HEIGHT); + assert(output.blockHeight != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT); assert(output.globalOutputIndex != UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); SpentTransactionOutput spentOutput; @@ -489,9 +493,9 @@ void TransfersContainer::copyToSpent(const BlockInfo& block, const ITransactionR assert(result.second); } -std::vector TransfersContainer::detach(uint64_t height) { - // This method expects that UNCONFIRMED_TRANSACTION_HEIGHT is a big positive number - assert(height < UNCONFIRMED_TRANSACTION_HEIGHT); +std::vector TransfersContainer::detach(uint32_t height) { + // This method expects that WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT is a big positive number + assert(height < WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT); std::lock_guard lk(m_mutex); @@ -503,14 +507,14 @@ std::vector TransfersContainer::detach(uint64_t height) { --it; bool doDelete = false; - if (it->blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { + if (it->blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { auto range = spendingTransactionIndex.equal_range(it->transactionHash); for (auto spentTransferIt = range.first; spentTransferIt != range.second; ++spentTransferIt) { if (spentTransferIt->blockHeight >= height) { doDelete = true; break; - } - } + } + } } else if (it->blockHeight >= height) { doDelete = true; } else { @@ -521,7 +525,7 @@ std::vector TransfersContainer::detach(uint64_t height) { deleteTransactionTransfers(it->transactionHash); deletedTransactions.emplace_back(it->transactionHash); it = blockHeightIndex.erase(it); - } + } } // TODO: notification on detach @@ -579,7 +583,7 @@ void TransfersContainer::updateTransfersVisibility(const KeyImage& keyImage) { } } -bool TransfersContainer::advanceHeight(uint64_t height) { +bool TransfersContainer::advanceHeight(uint32_t height) { std::lock_guard lk(m_mutex); if (m_currentHeight <= height) { @@ -648,7 +652,7 @@ bool TransfersContainer::getTransactionInformation(const Hash& transactionHash, info = *it; int64_t amountOut = 0; - if (info.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { + if (info.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { auto unconfirmedOutputsRange = m_unconfirmedTransfers.get().equal_range(transactionHash); for (auto it = unconfirmedOutputsRange.first; it != unconfirmedOutputsRange.second; ++it) { amountOut += static_cast(it->amount); @@ -702,12 +706,12 @@ std::vector TransfersContainer::getTransactionOutp return result; } -void TransfersContainer::getUnconfirmedTransactions(std::vector& transactions) { +void TransfersContainer::getUnconfirmedTransactions(std::vector& transactions) { std::lock_guard lk(m_mutex); transactions.clear(); for (auto& element : m_transactions) { - if (element.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { - transactions.push_back(*reinterpret_cast(&element.transactionHash)); + if (element.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { + transactions.push_back(*reinterpret_cast(&element.transactionHash)); } } } @@ -737,7 +741,8 @@ std::vector TransfersContainer::getSpentOutpu void TransfersContainer::save(std::ostream& os) { std::lock_guard lk(m_mutex); - CryptoNote::BinaryOutputStreamSerializer s(os); + StdOutputStream stream(os); + CryptoNote::BinaryOutputStreamSerializer s(stream); s(const_cast(TRANSFERS_CONTAINER_STORAGE_VERSION), "version"); @@ -750,7 +755,8 @@ void TransfersContainer::save(std::ostream& os) { void TransfersContainer::load(std::istream& in) { std::lock_guard lk(m_mutex); - CryptoNote::BinaryInputStreamSerializer s(in); + StdInputStream stream(in); + CryptoNote::BinaryInputStreamSerializer s(stream); uint32_t version = 0; s(version, "version"); @@ -759,7 +765,7 @@ void TransfersContainer::load(std::istream& in) { throw std::runtime_error("Unsupported transfers storage version"); } - uint64_t currentHeight = 0; + uint32_t currentHeight = 0; TransactionMultiIndex transactions; UnconfirmedTransfersMultiIndex unconfirmedTransfers; AvailableTransfersMultiIndex availableTransfers; @@ -793,7 +799,7 @@ bool TransfersContainer::isSpendTimeUnlocked(uint64_t unlockTime) const { bool TransfersContainer::isIncluded(const TransactionOutputInformationEx& info, uint32_t flags) const { uint32_t state; - if (info.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT || !isSpendTimeUnlocked(info.unlockTime)) { + if (info.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT || !isSpendTimeUnlocked(info.unlockTime)) { state = IncludeStateLocked; } else if (m_currentHeight < info.blockHeight + m_transactionSpendableAge) { state = IncludeStateSoftLocked; diff --git a/src/transfers/TransfersContainer.h b/src/Transfers/TransfersContainer.h old mode 100644 new mode 100755 similarity index 80% rename from src/transfers/TransfersContainer.h rename to src/Transfers/TransfersContainer.h index 72d4c17a..003208de --- a/src/transfers/TransfersContainer.h +++ b/src/Transfers/TransfersContainer.h @@ -28,14 +28,14 @@ #include #include "crypto/crypto.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/Currency.h" -#include "serialization/ISerializer.h" -#include "serialization/SerializationOverloads.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" +#include "CryptoNoteCore/CryptoNoteSerialization.h" +#include "CryptoNoteCore/Currency.h" +#include "Serialization/ISerializer.h" +#include "Serialization/SerializationOverloads.h" #include "ITransaction.h" #include "ITransfersContainer.h" -#include "SerializationHelpers.h" namespace CryptoNote { @@ -45,11 +45,11 @@ class SpentOutputDescriptor { public: SpentOutputDescriptor(); SpentOutputDescriptor(const TransactionOutputInformationIn& transactionInfo); - SpentOutputDescriptor(const KeyImage* keyImage); - SpentOutputDescriptor(uint64_t amount, uint64_t globalOutputIndex); + SpentOutputDescriptor(const Crypto::KeyImage* keyImage); + SpentOutputDescriptor(uint64_t amount, uint32_t globalOutputIndex); - void assign(const KeyImage* keyImage); - void assign(uint64_t amount, uint64_t globalOutputIndex); + void assign(const Crypto::KeyImage* keyImage); + void assign(uint64_t amount, uint32_t globalOutputIndex); bool isValid() const; @@ -59,10 +59,10 @@ public: private: TransactionTypes::OutputType m_type; union { - const KeyImage* m_keyImage; + const Crypto::KeyImage* m_keyImage; struct { uint64_t m_amount; - uint64_t m_globalOutputIndex; + uint32_t m_globalOutputIndex; }; }; }; @@ -74,17 +74,17 @@ struct SpentOutputDescriptorHasher { }; struct TransactionOutputInformationIn : public TransactionOutputInformation { - KeyImage keyImage; //!< \attention Used only for TransactionTypes::OutputType::Key + Crypto::KeyImage keyImage; //!< \attention Used only for TransactionTypes::OutputType::Key }; struct TransactionOutputInformationEx : public TransactionOutputInformationIn { uint64_t unlockTime; - uint64_t blockHeight; + uint32_t blockHeight; uint32_t transactionIndex; bool visible; SpentOutputDescriptor getSpentOutputDescriptor() const { return SpentOutputDescriptor(*this); } - const Hash& getTransactionHash() const { return transactionHash; } + const Crypto::Hash& getTransactionHash() const { return transactionHash; } void serialize(CryptoNote::ISerializer& s) { s(reinterpret_cast(type), "type"); @@ -108,7 +108,7 @@ struct TransactionOutputInformationEx : public TransactionOutputInformationIn { }; struct BlockInfo { - uint64_t height; + uint32_t height; uint64_t timestamp; uint32_t transactionIndex; @@ -121,10 +121,10 @@ struct BlockInfo { struct SpentTransactionOutput : TransactionOutputInformationEx { BlockInfo spendingBlock; - Hash spendingTransactionHash; + Crypto::Hash spendingTransactionHash; uint32_t inputInTransaction; - const Hash& getSpendingTransactionHash() const { + const Crypto::Hash& getSpendingTransactionHash() const { return spendingTransactionHash; } @@ -154,20 +154,20 @@ public: TransfersContainer(const CryptoNote::Currency& currency, size_t transactionSpendableAge); bool addTransaction(const BlockInfo& block, const ITransactionReader& tx, const std::vector& transfers); - bool deleteUnconfirmedTransaction(const Hash& transactionHash); - bool markTransactionConfirmed(const BlockInfo& block, const Hash& transactionHash, const std::vector& globalIndices); + bool deleteUnconfirmedTransaction(const Crypto::Hash& transactionHash); + bool markTransactionConfirmed(const BlockInfo& block, const Crypto::Hash& transactionHash, const std::vector& globalIndices); - std::vector detach(uint64_t height); - bool advanceHeight(uint64_t height); + std::vector detach(uint32_t height); + bool advanceHeight(uint32_t height); // ITransfersContainer virtual size_t transfersCount() override; virtual size_t transactionsCount() override; virtual uint64_t balance(uint32_t flags) override; virtual void getOutputs(std::vector& transfers, uint32_t flags) override; - virtual bool getTransactionInformation(const Hash& transactionHash, TransactionInformation& info, int64_t& txBalance) override; - virtual std::vector getTransactionOutputs(const Hash& transactionHash, uint32_t flags) override; - virtual void getUnconfirmedTransactions(std::vector& transactions) override; + virtual bool getTransactionInformation(const Crypto::Hash& transactionHash, TransactionInformation& info, int64_t& txBalance) override; + virtual std::vector getTransactionOutputs(const Crypto::Hash& transactionHash, uint32_t flags) override; + virtual void getUnconfirmedTransactions(std::vector& transactions) override; virtual std::vector getSpentOutputs() override; // IStreamSerializable @@ -182,8 +182,8 @@ private: typedef boost::multi_index_container< TransactionInformation, boost::multi_index::indexed_by< - boost::multi_index::hashed_unique, - boost::multi_index::ordered_non_unique + boost::multi_index::hashed_unique, + boost::multi_index::ordered_non_unique < BOOST_MULTI_INDEX_MEMBER(TransactionInformation, uint32_t, blockHeight) > > > TransactionMultiIndex; @@ -202,7 +202,7 @@ private: boost::multi_index::tag, boost::multi_index::const_mem_fun< TransactionOutputInformationEx, - const Hash&, + const Crypto::Hash&, &TransactionOutputInformationEx::getTransactionHash> > > @@ -223,7 +223,7 @@ private: boost::multi_index::tag, boost::multi_index::const_mem_fun< TransactionOutputInformationEx, - const Hash&, + const Crypto::Hash&, &TransactionOutputInformationEx::getTransactionHash> > > @@ -244,14 +244,14 @@ private: boost::multi_index::tag, boost::multi_index::const_mem_fun< TransactionOutputInformationEx, - const Hash&, + const Crypto::Hash&, &SpentTransactionOutput::getTransactionHash> >, boost::multi_index::hashed_non_unique < boost::multi_index::tag, boost::multi_index::const_mem_fun < SpentTransactionOutput, - const Hash&, + const Crypto::Hash&, &SpentTransactionOutput::getSpendingTransactionHash> > > @@ -262,11 +262,11 @@ private: bool addTransactionOutputs(const BlockInfo& block, const ITransactionReader& tx, const std::vector& transfers); bool addTransactionInputs(const BlockInfo& block, const ITransactionReader& tx); - void deleteTransactionTransfers(const Hash& transactionHash); + void deleteTransactionTransfers(const Crypto::Hash& transactionHash); bool isSpendTimeUnlocked(uint64_t unlockTime) const; bool isIncluded(const TransactionOutputInformationEx& info, uint32_t flags) const; static bool isIncluded(TransactionTypes::OutputType type, uint32_t state, uint32_t flags); - void updateTransfersVisibility(const KeyImage& keyImage); + void updateTransfersVisibility(const Crypto::KeyImage& keyImage); void copyToSpent(const BlockInfo& block, const ITransactionReader& tx, size_t inputIndex, const TransactionOutputInformationEx& output); @@ -277,7 +277,7 @@ private: SpentTransfersMultiIndex m_spentTransfers; //std::unordered_map> m_keyImages; - uint64_t m_currentHeight; // current height is needed to check if a transfer is unlocked + uint32_t m_currentHeight; // current height is needed to check if a transfer is unlocked size_t m_transactionSpendableAge; const CryptoNote::Currency& m_currency; std::mutex m_mutex; diff --git a/src/transfers/TransfersSubscription.cpp b/src/Transfers/TransfersSubscription.cpp old mode 100644 new mode 100755 similarity index 69% rename from src/transfers/TransfersSubscription.cpp rename to src/Transfers/TransfersSubscription.cpp index cb2309ce..7f5f4f44 --- a/src/transfers/TransfersSubscription.cpp +++ b/src/Transfers/TransfersSubscription.cpp @@ -16,65 +16,66 @@ // along with Bytecoin. If not, see . #include "TransfersSubscription.h" -#include "IWallet.h" +#include "IWalletLegacy.h" + +using namespace Crypto; namespace CryptoNote { TransfersSubscription::TransfersSubscription(const CryptoNote::Currency& currency, const AccountSubscription& sub) - : m_subscription(sub), m_transfers(currency, sub.transactionSpendableAge) {} + : subscription(sub), transfers(currency, sub.transactionSpendableAge) {} SynchronizationStart TransfersSubscription::getSyncStart() { - return m_subscription.syncStart; + return subscription.syncStart; } -void TransfersSubscription::onBlockchainDetach(uint64_t height) { - std::vector deletedTransactions = m_transfers.detach(height); +void TransfersSubscription::onBlockchainDetach(uint32_t height) { + std::vector deletedTransactions = transfers.detach(height); for (auto& hash : deletedTransactions) { m_observerManager.notify(&ITransfersObserver::onTransactionDeleted, this, hash); } } -void TransfersSubscription::onError(const std::error_code& ec, uint64_t height) { - if (height != UNCONFIRMED_TRANSACTION_HEIGHT) { - m_transfers.detach(height); +void TransfersSubscription::onError(const std::error_code& ec, uint32_t height) { + if (height != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { + transfers.detach(height); } m_observerManager.notify(&ITransfersObserver::onError, this, height, ec); } -bool TransfersSubscription::advanceHeight(uint64_t height) { - return m_transfers.advanceHeight(height); +bool TransfersSubscription::advanceHeight(uint32_t height) { + return transfers.advanceHeight(height); } const AccountKeys& TransfersSubscription::getKeys() const { - return m_subscription.keys; + return subscription.keys; } void TransfersSubscription::addTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx, - const std::vector& transfers) { - - bool added = m_transfers.addTransaction(blockInfo, tx, transfers); + const std::vector& transfersList) { + bool added = transfers.addTransaction(blockInfo, tx, transfersList); if (added) { m_observerManager.notify(&ITransfersObserver::onTransactionUpdated, this, tx.getTransactionHash()); } } -AccountAddress TransfersSubscription::getAddress() { - return m_subscription.keys.address; +AccountPublicAddress TransfersSubscription::getAddress() { + return subscription.keys.address; } ITransfersContainer& TransfersSubscription::getContainer() { - return m_transfers; + return transfers; } void TransfersSubscription::deleteUnconfirmedTransaction(const Hash& transactionHash) { - m_transfers.deleteUnconfirmedTransaction(transactionHash); + transfers.deleteUnconfirmedTransaction(transactionHash); m_observerManager.notify(&ITransfersObserver::onTransactionDeleted, this, transactionHash); } void TransfersSubscription::markTransactionConfirmed(const BlockInfo& block, const Hash& transactionHash, - const std::vector& globalIndices) { - m_transfers.markTransactionConfirmed(block, transactionHash, globalIndices); + const std::vector& globalIndices) { + transfers.markTransactionConfirmed(block, transactionHash, globalIndices); m_observerManager.notify(&ITransfersObserver::onTransactionUpdated, this, transactionHash); } diff --git a/src/transfers/TransfersSubscription.h b/src/Transfers/TransfersSubscription.h old mode 100644 new mode 100755 similarity index 75% rename from src/transfers/TransfersSubscription.h rename to src/Transfers/TransfersSubscription.h index da3630dc..0dda4132 --- a/src/transfers/TransfersSubscription.h +++ b/src/Transfers/TransfersSubscription.h @@ -28,23 +28,23 @@ public: TransfersSubscription(const CryptoNote::Currency& currency, const AccountSubscription& sub); SynchronizationStart getSyncStart(); - void onBlockchainDetach(uint64_t height); - void onError(const std::error_code& ec, uint64_t height); - bool advanceHeight(uint64_t height); + void onBlockchainDetach(uint32_t height); + void onError(const std::error_code& ec, uint32_t height); + bool advanceHeight(uint32_t height); const AccountKeys& getKeys() const; void addTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx, const std::vector& transfers); - void deleteUnconfirmedTransaction(const Hash& transactionHash); - void markTransactionConfirmed(const BlockInfo& block, const Hash& transactionHash, const std::vector& globalIndices); + void deleteUnconfirmedTransaction(const Crypto::Hash& transactionHash); + void markTransactionConfirmed(const BlockInfo& block, const Crypto::Hash& transactionHash, const std::vector& globalIndices); // ITransfersSubscription - virtual AccountAddress getAddress() override; + virtual AccountPublicAddress getAddress() override; virtual ITransfersContainer& getContainer() override; private: - TransfersContainer m_transfers; - AccountSubscription m_subscription; + TransfersContainer transfers; + AccountSubscription subscription; }; } diff --git a/src/transfers/TransfersSynchronizer.cpp b/src/Transfers/TransfersSynchronizer.cpp old mode 100644 new mode 100755 similarity index 88% rename from src/transfers/TransfersSynchronizer.cpp rename to src/Transfers/TransfersSynchronizer.cpp index 3dafa027..961d2051 --- a/src/transfers/TransfersSynchronizer.cpp +++ b/src/Transfers/TransfersSynchronizer.cpp @@ -18,16 +18,16 @@ #include "TransfersSynchronizer.h" #include "TransfersConsumer.h" -#include "serialization/BinaryInputStreamSerializer.h" -#include "serialization/BinaryOutputStreamSerializer.h" +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "Serialization/BinaryInputStreamSerializer.h" +#include "Serialization/BinaryOutputStreamSerializer.h" + +using namespace Common; +using namespace Crypto; namespace CryptoNote { -void serialize(AccountAddress& acc, CryptoNote::ISerializer& s) { - s(acc.spendPublicKey, "spendKey"); - s(acc.viewPublicKey, "viewKey"); -} - const uint32_t TRANSFERS_STORAGE_ARCHIVE_VERSION = 0; TransfersSyncronizer::TransfersSyncronizer(const CryptoNote::Currency& currency, IBlockchainSynchronizer& sync, INode& node) : @@ -54,7 +54,7 @@ ITransfersSubscription& TransfersSyncronizer::addSubscription(const AccountSubsc return it->second->addSubscription(acc); } -bool TransfersSyncronizer::removeSubscription(const AccountAddress& acc) { +bool TransfersSyncronizer::removeSubscription(const AccountPublicAddress& acc) { auto it = m_consumers.find(acc.viewPublicKey); if (it == m_consumers.end()) return false; @@ -67,13 +67,13 @@ bool TransfersSyncronizer::removeSubscription(const AccountAddress& acc) { return true; } -void TransfersSyncronizer::getSubscriptions(std::vector& subscriptions) { +void TransfersSyncronizer::getSubscriptions(std::vector& subscriptions) { for (const auto& kv : m_consumers) { kv.second->getSubscriptions(subscriptions); } } -ITransfersSubscription* TransfersSyncronizer::getSubscription(const AccountAddress& acc) { +ITransfersSubscription* TransfersSyncronizer::getSubscription(const AccountPublicAddress& acc) { auto it = m_consumers.find(acc.viewPublicKey); return (it == m_consumers.end()) ? 0 : it->second->getSubscription(acc); } @@ -81,7 +81,8 @@ ITransfersSubscription* TransfersSyncronizer::getSubscription(const AccountAddre void TransfersSyncronizer::save(std::ostream& os) { m_sync.save(os); - CryptoNote::BinaryOutputStreamSerializer s(os); + StdOutputStream stream(os); + CryptoNote::BinaryOutputStreamSerializer s(stream); s(const_cast(TRANSFERS_STORAGE_ARCHIVE_VERSION), "version"); size_t subscriptionCount = m_consumers.size(); @@ -99,7 +100,7 @@ void TransfersSyncronizer::save(std::ostream& os) { std::string blob = consumerState.str(); s(blob, "state"); - std::vector subscriptions; + std::vector subscriptions; consumer.second->getSubscriptions(subscriptions); size_t subCount = subscriptions.size(); @@ -144,7 +145,8 @@ void setObjectState(IStreamSerializable& obj, const std::string& state) { void TransfersSyncronizer::load(std::istream& is) { m_sync.load(is); - CryptoNote::BinaryInputStreamSerializer s(is); + StdInputStream inputStream(is); + CryptoNote::BinaryInputStreamSerializer s(inputStream); uint32_t version = 0; s(version, "version"); @@ -157,7 +159,7 @@ void TransfersSyncronizer::load(std::istream& is) { struct ConsumerState { PublicKey viewKey; std::string state; - std::vector> subscriptionStates; + std::vector> subscriptionStates; }; std::vector updatedStates; @@ -194,7 +196,7 @@ void TransfersSyncronizer::load(std::istream& is) { while (subCount--) { s.beginObject(""); - AccountAddress acc; + AccountPublicAddress acc; std::string state; s(acc, "address"); diff --git a/src/transfers/TransfersSynchronizer.h b/src/Transfers/TransfersSynchronizer.h similarity index 83% rename from src/transfers/TransfersSynchronizer.h rename to src/Transfers/TransfersSynchronizer.h index 27f1ff75..c66a8bed 100644 --- a/src/transfers/TransfersSynchronizer.h +++ b/src/Transfers/TransfersSynchronizer.h @@ -42,9 +42,9 @@ public: // ITransfersSynchronizer virtual ITransfersSubscription& addSubscription(const AccountSubscription& acc) override; - virtual bool removeSubscription(const AccountAddress& acc) override; - virtual void getSubscriptions(std::vector& subscriptions) override; - virtual ITransfersSubscription* getSubscription(const AccountAddress& acc) override; + virtual bool removeSubscription(const AccountPublicAddress& acc) override; + virtual void getSubscriptions(std::vector& subscriptions) override; + virtual ITransfersSubscription* getSubscription(const AccountPublicAddress& acc) override; // IStreamSerializable virtual void save(std::ostream& os) override; @@ -53,7 +53,7 @@ public: private: // map { view public key -> consumer } - std::unordered_map> m_consumers; + std::unordered_map> m_consumers; // std::unordered_map> m_subscriptions; IBlockchainSynchronizer& m_sync; diff --git a/src/transfers/TypeHelpers.h b/src/Transfers/TypeHelpers.h similarity index 71% rename from src/transfers/TypeHelpers.h rename to src/Transfers/TypeHelpers.h index 5b95fd71..556354b8 100644 --- a/src/transfers/TypeHelpers.h +++ b/src/Transfers/TypeHelpers.h @@ -23,8 +23,8 @@ namespace CryptoNote { -inline bool operator==(const AccountAddress &_v1, const AccountAddress &_v2) { - return memcmp(&_v1, &_v2, sizeof(AccountAddress)) == 0; +inline bool operator==(const AccountPublicAddress &_v1, const AccountPublicAddress &_v2) { + return memcmp(&_v1, &_v2, sizeof(AccountPublicAddress)) == 0; } } @@ -32,19 +32,12 @@ inline bool operator==(const AccountAddress &_v1, const AccountAddress &_v2) { namespace std { template<> -struct hash < CryptoNote::AccountAddress > { - std::size_t operator()(const CryptoNote::AccountAddress& val) const { +struct hash < CryptoNote::AccountPublicAddress > { + size_t operator()(const CryptoNote::AccountPublicAddress& val) const { size_t spend = *(reinterpret_cast(&val.spendPublicKey)); size_t view = *(reinterpret_cast(&val.viewPublicKey)); return spend ^ view; } }; -template<> -struct hash < CryptoNote::PublicKey > { - std::size_t operator()(const CryptoNote::PublicKey& val) const { - return *reinterpret_cast(&val); - } -}; - } diff --git a/src/wallet/LegacyKeysImporter.cpp b/src/Wallet/LegacyKeysImporter.cpp similarity index 60% rename from src/wallet/LegacyKeysImporter.cpp rename to src/Wallet/LegacyKeysImporter.cpp index 44110f34..cee143c5 100755 --- a/src/wallet/LegacyKeysImporter.cpp +++ b/src/Wallet/LegacyKeysImporter.cpp @@ -1,98 +1,100 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "LegacyKeysImporter.h" - -#include -#include - -#include "Common/StringTools.h" - -#include "cryptonote_core/Currency.h" -#include "cryptonote_core/account.h" - -#include "serialization/binary_utils.h" -#include "serialization/SerializationTools.h" - -#include "wallet/WalletSerializer.h" -#include "wallet/WalletUserTransactionsCache.h" -#include "wallet/WalletErrors.h" - -namespace { - -struct keys_file_data { - crypto::chacha8_iv iv; - std::string account_data; - - BEGIN_SERIALIZE_OBJECT() - FIELD(iv) - FIELD(account_data) - END_SERIALIZE() -}; - -bool verify_keys(const crypto::secret_key& sec, const crypto::public_key& expected_pub) { - crypto::public_key pub; - bool r = crypto::secret_key_to_public_key(sec, pub); - return r && expected_pub == pub; -} - -void loadKeysFromFile(const std::string& filename, const std::string& password, CryptoNote::account_base& account) { - keys_file_data keys_file_data; - std::string buf; - - if (!Common::loadFileToString(filename, buf)) { - throw std::system_error(make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR), "failed to load \"" + filename + '\"'); - } - - if (!::serialization::parse_binary(buf, keys_file_data)) { - throw std::system_error(make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR), "failed to deserialize \"" + filename + '\"'); - } - - crypto::chacha8_key key; - crypto::cn_context cn_context; - crypto::generate_chacha8_key(cn_context, password, key); - std::string account_data; - account_data.resize(keys_file_data.account_data.size()); - crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); - - const CryptoNote::account_keys& keys = account.get_keys(); - - if (CryptoNote::loadFromBinaryKeyValue(account, account_data) && - verify_keys(keys.m_view_secret_key, keys.m_account_address.m_viewPublicKey) && - verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spendPublicKey)) { - return; - } - - throw std::system_error(make_error_code(CryptoNote::error::WRONG_PASSWORD)); -} - -} - -namespace CryptoNote { - -void importLegacyKeys(const std::string& legacyKeysFilename, const std::string& password, std::ostream& destination) { - CryptoNote::account_base account; - - loadKeysFromFile(legacyKeysFilename, password, account); - - CryptoNote::WalletUserTransactionsCache transactionsCache; - std::string cache; - CryptoNote::WalletSerializer importer(account, transactionsCache); - importer.serialize(destination, password, false, cache); -} - -} //namespace CryptoNote +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "LegacyKeysImporter.h" + +#include +#include + +#include "Common/StringTools.h" + +#include "CryptoNoteCore/Currency.h" +#include "CryptoNoteCore/Account.h" +#include "CryptoNoteCore/CryptoNoteTools.h" + +#include "Serialization/SerializationTools.h" + +#include "WalletLegacy/WalletLegacySerializer.h" +#include "WalletLegacy/WalletUserTransactionsCache.h" +#include "Wallet/WalletErrors.h" + +using namespace Crypto; + +namespace { + +struct keys_file_data { + chacha8_iv iv; + std::string account_data; + + void serialize(CryptoNote::ISerializer& s) { + s(iv, "iv"); + s(account_data, "account_data"); + } +}; + +bool verify_keys(const SecretKey& sec, const PublicKey& expected_pub) { + PublicKey pub; + bool r = secret_key_to_public_key(sec, pub); + return r && expected_pub == pub; +} + +void loadKeysFromFile(const std::string& filename, const std::string& password, CryptoNote::AccountBase& account) { + keys_file_data keys_file_data; + std::string buf; + + if (!Common::loadFileToString(filename, buf)) { + throw std::system_error(make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR), "failed to load \"" + filename + '\"'); + } + + if (!CryptoNote::fromBinaryArray(keys_file_data, Common::asBinaryArray(buf))) { + throw std::system_error(make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR), "failed to deserialize \"" + filename + '\"'); + } + + chacha8_key key; + cn_context cn_context; + generate_chacha8_key(cn_context, password, key); + std::string account_data; + account_data.resize(keys_file_data.account_data.size()); + chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); + + const CryptoNote::AccountKeys& keys = account.getAccountKeys(); + + if (CryptoNote::loadFromBinaryKeyValue(account, account_data) && + verify_keys(keys.viewSecretKey, keys.address.viewPublicKey) && + verify_keys(keys.spendSecretKey, keys.address.spendPublicKey)) { + return; + } + + throw std::system_error(make_error_code(CryptoNote::error::WRONG_PASSWORD)); +} + +} + +namespace CryptoNote { + +void importLegacyKeys(const std::string& legacyKeysFilename, const std::string& password, std::ostream& destination) { + CryptoNote::AccountBase account; + + loadKeysFromFile(legacyKeysFilename, password, account); + + CryptoNote::WalletUserTransactionsCache transactionsCache; + std::string cache; + CryptoNote::WalletLegacySerializer importer(account, transactionsCache); + importer.serialize(destination, password, false, cache); +} + +} //namespace CryptoNote diff --git a/src/wallet/LegacyKeysImporter.h b/src/Wallet/LegacyKeysImporter.h similarity index 100% rename from src/wallet/LegacyKeysImporter.h rename to src/Wallet/LegacyKeysImporter.h diff --git a/src/wallet/WalletAsyncContextCounter.cpp b/src/Wallet/WalletAsyncContextCounter.cpp similarity index 100% rename from src/wallet/WalletAsyncContextCounter.cpp rename to src/Wallet/WalletAsyncContextCounter.cpp diff --git a/src/wallet/WalletAsyncContextCounter.h b/src/Wallet/WalletAsyncContextCounter.h similarity index 100% rename from src/wallet/WalletAsyncContextCounter.h rename to src/Wallet/WalletAsyncContextCounter.h diff --git a/src/wallet/WalletErrors.cpp b/src/Wallet/WalletErrors.cpp similarity index 100% rename from src/wallet/WalletErrors.cpp rename to src/Wallet/WalletErrors.cpp diff --git a/src/wallet/WalletErrors.h b/src/Wallet/WalletErrors.h similarity index 96% rename from src/wallet/WalletErrors.h rename to src/Wallet/WalletErrors.h index 445f2105..346638fa 100644 --- a/src/wallet/WalletErrors.h +++ b/src/Wallet/WalletErrors.h @@ -39,7 +39,8 @@ enum WalletErrorCodes { TX_CANCEL_IMPOSSIBLE, TX_CANCELLED, OPERATION_CANCELLED, - TX_TRANSFER_IMPOSSIBLE + TX_TRANSFER_IMPOSSIBLE, + WRONG_VERSION }; // custom category: @@ -71,6 +72,7 @@ public: case WRONG_STATE: return "The wallet is in wrong state (maybe loading or saving), try again later"; case OPERATION_CANCELLED: return "The operation you've requested has been cancelled"; case TX_TRANSFER_IMPOSSIBLE: return "Transaction transfer impossible"; + case WRONG_VERSION: return "Wrong version"; default: return "Unknown error"; } } diff --git a/src/Wallet/WalletGreen.cpp b/src/Wallet/WalletGreen.cpp new file mode 100755 index 00000000..e6125444 --- /dev/null +++ b/src/Wallet/WalletGreen.cpp @@ -0,0 +1,1218 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "WalletGreen.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ITransaction.h" + +#include "Common/ShuffleGenerator.h" +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "Common/StringTools.h" +#include "CryptoNoteCore/Account.h" +#include "CryptoNoteCore/Currency.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteCore/CryptoNoteTools.h" +#include "CryptoNoteCore/TransactionApi.h" +#include "crypto/crypto.h" +#include "Transfers/TransfersContainer.h" +#include "WalletSerialization.h" +#include "WalletErrors.h" + +using namespace Common; +using namespace Crypto; +using namespace CryptoNote; + +namespace { + +const uint32_t WALLET_SOFTLOCK_BLOCKS_COUNT = 1; + +const uint64_t DUST_THRESHOLD = 10000; + +void asyncRequestCompletion(System::Event& requestFinished) { + requestFinished.set(); +} + +void parseAddressString(const std::string& string, const CryptoNote::Currency& currency, CryptoNote::AccountPublicAddress& address) { + if (!currency.parseAccountAddressString(string, address)) { + throw std::system_error(make_error_code(CryptoNote::error::BAD_ADDRESS)); + } +} + +bool validateAddress(const std::string& address, const CryptoNote::Currency& currency) { + CryptoNote::AccountPublicAddress ignore; + return currency.parseAccountAddressString(address, ignore); +} + +void validateAddresses(const std::vector& destinations, const CryptoNote::Currency& currency) { + for (const auto& destination: destinations) { + if (!validateAddress(destination.address, currency)) { + throw std::system_error(make_error_code(CryptoNote::error::BAD_ADDRESS)); + } + } +} + +uint64_t countNeededMoney(const std::vector& destinations, uint64_t fee) { + uint64_t neededMoney = 0; + for (const auto& transfer: destinations) { + if (transfer.amount == 0) { + throw std::system_error(make_error_code(CryptoNote::error::ZERO_DESTINATION)); + } else if (transfer.amount < 0) { + throw std::system_error(make_error_code(std::errc::invalid_argument)); + } + + //to supress warning + uint64_t uamount = static_cast(transfer.amount); + neededMoney += uamount; + if (neededMoney < uamount) { + throw std::system_error(make_error_code(CryptoNote::error::SUM_OVERFLOW)); + } + } + + neededMoney += fee; + if (neededMoney < fee) { + throw std::system_error(make_error_code(CryptoNote::error::SUM_OVERFLOW)); + } + + return neededMoney; +} + +void checkIfEnoughMixins(std::vector& mixinResult, uint64_t mixIn) { + auto notEnoughIt = std::find_if(mixinResult.begin(), mixinResult.end(), + [mixIn] (const CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& ofa) { return ofa.outs.size() < mixIn; } ); + + if (mixIn == 0 && mixinResult.empty()) { + throw std::system_error(make_error_code(CryptoNote::error::MIXIN_COUNT_TOO_BIG)); + } + + if (notEnoughIt != mixinResult.end()) { + throw std::system_error(make_error_code(CryptoNote::error::MIXIN_COUNT_TOO_BIG)); + } +} + +CryptoNote::WalletEvent makeTransactionUpdatedEvent(size_t id) { + CryptoNote::WalletEvent event; + event.type = CryptoNote::WalletEventType::TRANSACTION_UPDATED; + event.transactionUpdated.transactionIndex = id; + + return event; +} + +CryptoNote::WalletEvent makeTransactionCreatedEvent(size_t id) { + CryptoNote::WalletEvent event; + event.type = CryptoNote::WalletEventType::TRANSACTION_CREATED; + event.transactionCreated.transactionIndex = id; + + return event; +} + +CryptoNote::WalletEvent makeMoneyUnlockedEvent() { + CryptoNote::WalletEvent event; + event.type = CryptoNote::WalletEventType::BALANCE_UNLOCKED; + + return event; +} + +} + +namespace CryptoNote { + +WalletGreen::WalletGreen(System::Dispatcher& dispatcher, const Currency& currency, INode& node) : + m_dispatcher(dispatcher), + m_currency(currency), + m_node(node), + m_blockchainSynchronizer(node, currency.genesisBlockHash()), + m_synchronizer(currency, m_blockchainSynchronizer, node), + m_eventOccured(m_dispatcher), + m_readyEvent(m_dispatcher) +{ + m_upperTransactionSizeLimit = m_currency.blockGrantedFullRewardZone() * 125 / 100 - m_currency.minerTxBlobReservedSize(); + m_readyEvent.set(); +} + +WalletGreen::~WalletGreen() { + if (m_state == WalletState::INITIALIZED) { + doShutdown(); + } + + m_dispatcher.yield(); //let remote spawns finish +} + +void WalletGreen::initialize(const std::string& password) { + if (m_state != WalletState::NOT_INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::ALREADY_INITIALIZED)); + } + + throwIfStopped(); + + Crypto::generate_keys(m_viewPublicKey, m_viewSecretKey); + m_password = password; + + m_blockchainSynchronizer.addObserver(this); + + m_state = WalletState::INITIALIZED; +} + +void WalletGreen::shutdown() { + throwIfNotInitialized(); + doShutdown(); + + m_dispatcher.yield(); //let remote spawns finish +} + +void WalletGreen::doShutdown() { + m_blockchainSynchronizer.stop(); + m_blockchainSynchronizer.removeObserver(this); + + clearCaches(); + + std::queue noEvents; + std::swap(m_events, noEvents); + + m_state = WalletState::NOT_INITIALIZED; +} + +void WalletGreen::clearCaches() { + std::vector subscriptions; + m_synchronizer.getSubscriptions(subscriptions); + std::for_each(subscriptions.begin(), subscriptions.end(), [this] (const AccountPublicAddress& address) { m_synchronizer.removeSubscription(address); }); + + m_walletsContainer.clear(); + m_spentOutputs.clear(); + m_unlockTransactionsJob.clear(); + m_transactions.clear(); + m_transfers.clear(); + m_change.clear(); + m_actualBalance = 0; + m_pendingBalance = 0; +} + +void WalletGreen::save(std::ostream& destination, bool saveDetails, bool saveCache) { + throwIfNotInitialized(); + throwIfStopped(); + + if (m_walletsContainer.get().size() != 0) { + m_blockchainSynchronizer.stop(); + } + + unsafeSave(destination, saveDetails, saveCache); + + if (m_walletsContainer.get().size() != 0) { + m_blockchainSynchronizer.start(); + } +} + +void WalletGreen::unsafeSave(std::ostream& destination, bool saveDetails, bool saveCache) { + WalletSerializer s( + *this, + m_viewPublicKey, + m_viewSecretKey, + m_actualBalance, + m_pendingBalance, + m_walletsContainer, + m_synchronizer, + m_spentOutputs, + m_unlockTransactionsJob, + m_change, + m_transactions, + m_transfers + ); + + StdOutputStream output(destination); + s.save(m_password, output, saveDetails, saveCache); +} + +void WalletGreen::load(std::istream& source, const std::string& password) { + if (m_state != WalletState::NOT_INITIALIZED) { + throw std::system_error(make_error_code(error::WRONG_STATE)); + } + + throwIfStopped(); + + if (m_walletsContainer.get().size() != 0) { + m_blockchainSynchronizer.stop(); + } + + unsafeLoad(source, password); + + if (m_walletsContainer.get().size() != 0) { + m_blockchainSynchronizer.start(); + } + + m_state = WalletState::INITIALIZED; +} + +void WalletGreen::unsafeLoad(std::istream& source, const std::string& password) { + WalletSerializer s( + *this, + m_viewPublicKey, + m_viewSecretKey, + m_actualBalance, + m_pendingBalance, + m_walletsContainer, + m_synchronizer, + m_spentOutputs, + m_unlockTransactionsJob, + m_change, + m_transactions, + m_transfers + ); + + StdInputStream inputStream(source); + s.load(password, inputStream); + + m_password = password; + m_blockchainSynchronizer.addObserver(this); +} + +void WalletGreen::changePassword(const std::string& oldPassword, const std::string& newPassword) { + throwIfNotInitialized(); + throwIfStopped(); + + if (m_password.compare(oldPassword)) { + throw std::system_error(make_error_code(error::WRONG_PASSWORD)); + } + + m_password = newPassword; +} + +size_t WalletGreen::getAddressCount() const { + throwIfNotInitialized(); + throwIfStopped(); + + return m_walletsContainer.get().size(); +} + +std::string WalletGreen::getAddress(size_t index) const { + throwIfNotInitialized(); + throwIfStopped(); + + if (index >= m_walletsContainer.get().size()) { + throw std::system_error(std::make_error_code(std::errc::invalid_argument)); + } + + const WalletRecord& wallet = m_walletsContainer.get()[index]; + return m_currency.accountAddressAsString({ wallet.spendPublicKey, m_viewPublicKey }); +} + +std::string WalletGreen::createAddress() { + KeyPair spendKey; + + Crypto::generate_keys(spendKey.publicKey, spendKey.secretKey); + return createAddress(spendKey); +} + +std::string WalletGreen::createAddress(const KeyPair& spendKey) { + throwIfNotInitialized(); + throwIfStopped(); + + if (m_walletsContainer.get().size() != 0) { + m_blockchainSynchronizer.stop(); + } + + addWallet(spendKey); + std::string address = m_currency.accountAddressAsString({ spendKey.publicKey, m_viewPublicKey }); + + m_blockchainSynchronizer.start(); + + return address; +} + +void WalletGreen::addWallet(const KeyPair& spendKey) { + time_t creationTimestamp = time(nullptr); + + AccountSubscription sub; + sub.keys.address.viewPublicKey = m_viewPublicKey; + sub.keys.address.spendPublicKey = spendKey.publicKey; + sub.keys.viewSecretKey = m_viewSecretKey; + sub.keys.spendSecretKey = spendKey.secretKey; + sub.transactionSpendableAge = 10; + sub.syncStart.height = 0; + sub.syncStart.timestamp = static_cast(creationTimestamp) - (60 * 60 * 24); + + auto& trSubscription = m_synchronizer.addSubscription(sub); + ITransfersContainer* container = &trSubscription.getContainer(); + + WalletRecord wallet; + wallet.spendPublicKey = spendKey.publicKey; + wallet.spendSecretKey = spendKey.secretKey; + wallet.container = container; + wallet.creationTimestamp = creationTimestamp; + trSubscription.addObserver(this); + + m_walletsContainer.get().push_back(std::move(wallet)); +} + +void WalletGreen::deleteAddress(const std::string& address) { + throwIfNotInitialized(); + throwIfStopped(); + + CryptoNote::AccountPublicAddress pubAddr = parseAddress(address); + + auto it = m_walletsContainer.get().find(pubAddr.spendPublicKey); + if (it == m_walletsContainer.get().end()) { + throw std::system_error(std::make_error_code(std::errc::invalid_argument)); + } + + m_blockchainSynchronizer.stop(); + + m_actualBalance -= it->actualBalance; + m_pendingBalance -= it->pendingBalance; + + m_synchronizer.removeSubscription(pubAddr); + + m_spentOutputs.get().erase(&(*it)); + m_walletsContainer.get().erase(it); + + if (m_walletsContainer.get().size() != 0) { + m_blockchainSynchronizer.start(); + } +} + +uint64_t WalletGreen::getActualBalance() const { + throwIfNotInitialized(); + throwIfStopped(); + + return m_actualBalance; +} + +uint64_t WalletGreen::getActualBalance(const std::string& address) const { + throwIfNotInitialized(); + throwIfStopped(); + + const auto& wallet = getWalletRecord(address); + return wallet.actualBalance; +} + +uint64_t WalletGreen::getPendingBalance() const { + throwIfNotInitialized(); + throwIfStopped(); + + return m_pendingBalance; +} + +uint64_t WalletGreen::getPendingBalance(const std::string& address) const { + throwIfNotInitialized(); + throwIfStopped(); + + const auto& wallet = getWalletRecord(address); + return wallet.pendingBalance; +} + +size_t WalletGreen::getTransactionCount() const { + throwIfNotInitialized(); + throwIfStopped(); + + return m_transactions.get().size(); +} + +WalletTransaction WalletGreen::getTransaction(size_t transactionIndex) const { + throwIfNotInitialized(); + throwIfStopped(); + + return m_transactions.get().at(transactionIndex); +} + +size_t WalletGreen::getTransactionTransferCount(size_t transactionIndex) const { + throwIfNotInitialized(); + throwIfStopped(); + + auto bounds = getTransactionTransfers(transactionIndex); + return static_cast(std::distance(bounds.first, bounds.second)); +} + +WalletTransfer WalletGreen::getTransactionTransfer(size_t transactionIndex, size_t transferIndex) const { + throwIfNotInitialized(); + throwIfStopped(); + + auto bounds = getTransactionTransfers(transactionIndex); + + if (transferIndex >= static_cast(std::distance(bounds.first, bounds.second))) { + throw std::system_error(std::make_error_code(std::errc::invalid_argument)); + } + + auto it = bounds.first; + std::advance(it, transferIndex); + return it->second; +} + +std::pair WalletGreen::getTransactionTransfers( + size_t transactionIndex) const { + + auto val = std::make_pair(transactionIndex, WalletTransfer()); + + auto bounds = std::equal_range(m_transfers.begin(), m_transfers.end(), val, [] (const TransactionTransferPair& a, const TransactionTransferPair& b) { + return a.first < b.first; + }); + + return bounds; +} + +size_t WalletGreen::transfer(const WalletTransfer& destination, + uint64_t fee, + uint64_t mixIn, + std::string const& extra, + uint64_t unlockTimestamp) +{ + std::vector destinations { destination }; + return transfer(destinations, fee, mixIn, extra, unlockTimestamp); +} + +size_t WalletGreen::transfer( + const std::vector& destinations, + uint64_t fee, + uint64_t mixIn, + const std::string& extra, + uint64_t unlockTimestamp) { + + System::EventLock lk(m_readyEvent); + + throwIfNotInitialized(); + throwIfStopped(); + + return doTransfer(pickWalletsWithMoney(), destinations, fee, mixIn, extra, unlockTimestamp); +} + +size_t WalletGreen::transfer( + const std::string& sourceAddress, + const WalletTransfer& destination, + uint64_t fee, + uint64_t mixIn, + std::string const& extra, + uint64_t unlockTimestamp) { + std::vector destinations { destination }; + return transfer(sourceAddress, destinations, fee, mixIn, extra, unlockTimestamp); +} + +size_t WalletGreen::transfer( + const std::string& sourceAddress, + const std::vector& destinations, + uint64_t fee, + uint64_t mixIn, + const std::string& extra, + uint64_t unlockTimestamp) { + System::EventLock lk(m_readyEvent); + + throwIfNotInitialized(); + throwIfStopped(); + + WalletOuts wallet = pickWallet(sourceAddress); + std::vector wallets; + + if (!wallet.outs.empty()) { + wallets.push_back(wallet); + } + + return doTransfer(std::move(wallets), destinations, fee, mixIn, extra, unlockTimestamp); +} + +size_t WalletGreen::doTransfer(std::vector&& wallets, + const std::vector& destinations, + uint64_t fee, + uint64_t mixIn, + const std::string& extra, + uint64_t unlockTimestamp) { + if (destinations.empty()) { + throw std::system_error(make_error_code(error::ZERO_DESTINATION)); + } + + validateAddresses(destinations, m_currency); + + uint64_t neededMoney = countNeededMoney(destinations, fee); + + std::vector selectedTransfers; + uint64_t foundMoney = selectTransfers(neededMoney, mixIn == 0, DUST_THRESHOLD, std::move(wallets), selectedTransfers); + + if (foundMoney < neededMoney) { + throw std::system_error(make_error_code(error::WRONG_AMOUNT), "Not enough money"); + } + + typedef CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount outs_for_amount; + std::vector mixinResult; + + if (mixIn != 0) { + requestMixinOuts(selectedTransfers, mixIn, mixinResult); + } + + std::vector keysInfo; + prepareInputs(selectedTransfers, mixinResult, mixIn, keysInfo); + + WalletTransfer changeDestination; + changeDestination.address = m_currency.accountAddressAsString({ m_walletsContainer.get()[0].spendPublicKey, m_viewPublicKey }); + changeDestination.amount = foundMoney - neededMoney; + + std::vector decomposedOutputs; + splitDestinations(destinations, changeDestination, DUST_THRESHOLD, m_currency, decomposedOutputs); + + std::unique_ptr tx = makeTransaction(decomposedOutputs, keysInfo, extra, unlockTimestamp); + + size_t txId = insertOutgoingTransaction(tx->getTransactionHash(), -static_cast(neededMoney), fee, tx->getExtra(), unlockTimestamp); + pushBackOutgoingTransfers(txId, destinations); + + try { + sendTransaction(tx.get()); + } catch (std::exception&) { + pushEvent(makeTransactionCreatedEvent(txId)); + throw; + } + + auto txIt = m_transactions.get().begin(); + std::advance(txIt, txId); + m_transactions.get().modify(txIt, + [] (WalletTransaction& tx) { tx.state = WalletTransactionState::SUCCEEDED; }); + + markOutputsSpent(tx->getTransactionHash(), selectedTransfers); + m_change[tx->getTransactionHash()] = changeDestination.amount; + updateUsedWalletsBalances(selectedTransfers); + + pushEvent(makeTransactionCreatedEvent(txId)); + + return txId; +} + +void WalletGreen::pushBackOutgoingTransfers(size_t txId, const std::vector &destinations) { + for (const auto& dest: destinations) { + WalletTransfer d { dest.address, -dest.amount }; + m_transfers.push_back(std::make_pair(txId, d)); + } +} + +size_t WalletGreen::insertOutgoingTransaction(const Hash& transactionHash, int64_t totalAmount, uint64_t fee, const BinaryArray& extra, uint64_t unlockTimestamp) { + WalletTransaction insertTx; + insertTx.state = WalletTransactionState::FAILED; + insertTx.creationTime = static_cast(time(nullptr)); + insertTx.unlockTime = unlockTimestamp; + insertTx.blockHeight = CryptoNote::WALLET_UNCONFIRMED_TRANSACTION_HEIGHT; + insertTx.extra.assign(reinterpret_cast(extra.data()), extra.size()); + insertTx.fee = fee; + insertTx.hash = transactionHash; + insertTx.totalAmount = totalAmount; + insertTx.timestamp = 0; //0 until included in a block + + size_t txId = m_transactions.get().size(); + m_transactions.get().push_back(std::move(insertTx)); + + return txId; +} + +bool WalletGreen::transactionExists(const Hash& hash) { + auto& hashIndex = m_transactions.get(); + auto it = hashIndex.find(hash); + return it != hashIndex.end(); +} + +void WalletGreen::updateTransactionHeight(const Hash& hash, uint32_t blockHeight) { + auto& hashIndex = m_transactions.get(); + + auto it = hashIndex.find(hash); + if (it != hashIndex.end()) { + bool r = hashIndex.modify(it, [&blockHeight] (WalletTransaction& transaction) { + transaction.blockHeight = blockHeight; + //transaction may be deleted first than added again + transaction.state = WalletTransactionState::SUCCEEDED; + }); + assert(r); + return; + } + + throw std::system_error(make_error_code(std::errc::invalid_argument)); +} + +size_t WalletGreen::insertIncomingTransaction(const TransactionInformation& info, int64_t txBalance) { + auto& index = m_transactions.get(); + + WalletTransaction tx; + tx.state = WalletTransactionState::SUCCEEDED; + tx.timestamp = info.timestamp; + tx.blockHeight = info.blockHeight; + tx.hash = info.transactionHash; + tx.fee = info.totalAmountIn - info.totalAmountOut; + tx.unlockTime = info.unlockTime; + tx.extra.assign(reinterpret_cast(info.extra.data()), info.extra.size()); + tx.totalAmount = txBalance; + tx.creationTime = info.timestamp; + + index.push_back(std::move(tx)); + return index.size() - 1; +} + +void WalletGreen::insertIncomingTransfer(size_t txId, const std::string& address, int64_t amount) { + auto it = std::upper_bound(m_transfers.begin(), m_transfers.end(), txId, [] (size_t val, const TransactionTransferPair& a) { + return val < a.first; + }); + + WalletTransfer tr { address, amount }; + m_transfers.insert(it, std::make_pair(txId, std::move(tr))); +} + +std::unique_ptr WalletGreen::makeTransaction(const std::vector& decomposedOutputs, + std::vector& keysInfo, const std::string& extra, uint64_t unlockTimestamp) { + + std::unique_ptr tx = createTransaction(); + + for (const auto& output: decomposedOutputs) { + for (auto amount: output.amounts) { + tx->addOutput(amount, output.receiver); + } + } + + tx->setUnlockTime(unlockTimestamp); + tx->appendExtra(Common::asBinaryArray(extra)); + + for (auto& input: keysInfo) { + tx->addInput(makeAccountKeys(*input.walletRecord), input.keyInfo, input.ephKeys); + } + + size_t i = 0; + for(auto& input: keysInfo) { + tx->signInputKey(i++, input.keyInfo, input.ephKeys); + } + + return tx; +} + +void WalletGreen::sendTransaction(ITransaction* tx) { + System::Event completion(m_dispatcher); + std::error_code ec; + CryptoNote::Transaction oldTxFormat; + + const auto& ba = tx->getTransactionData(); + + if (ba.size() > m_upperTransactionSizeLimit) { + throw std::system_error(make_error_code(error::TRANSACTION_SIZE_TOO_BIG)); + } + + if (!fromBinaryArray(oldTxFormat, ba)) { + throw std::system_error(make_error_code(error::INTERNAL_WALLET_ERROR)); + } + + throwIfStopped(); + m_node.relayTransaction(oldTxFormat, [&ec, &completion, this] (std::error_code error) { + ec = error; + this->m_dispatcher.remoteSpawn(std::bind(asyncRequestCompletion, std::ref(completion))); + }); + completion.wait(); + + if (ec) { + throw std::system_error(ec); + } +} + +AccountKeys WalletGreen::makeAccountKeys(const WalletRecord& wallet) const { + AccountKeys keys; + keys.address.spendPublicKey = wallet.spendPublicKey; + keys.address.viewPublicKey = m_viewPublicKey; + keys.spendSecretKey = wallet.spendSecretKey; + keys.viewSecretKey = m_viewSecretKey; + + return keys; +} + +void WalletGreen::requestMixinOuts( + const std::vector& selectedTransfers, + uint64_t mixIn, + std::vector& mixinResult) { + + std::vector amounts; + for (const auto& out: selectedTransfers) { + amounts.push_back(out.out.amount); + } + + System::Event requestFinished(m_dispatcher); + std::error_code mixinError; + + throwIfStopped(); + + m_node.getRandomOutsByAmounts(std::move(amounts), mixIn, mixinResult, [&requestFinished, &mixinError, this] (std::error_code ec) { + mixinError = ec; + this->m_dispatcher.remoteSpawn(std::bind(asyncRequestCompletion, std::ref(requestFinished))); + }); + + requestFinished.wait(); + + checkIfEnoughMixins(mixinResult, mixIn); + + if (mixinError) { + throw std::system_error(mixinError); + } +} + +uint64_t WalletGreen::selectTransfers( + uint64_t neededMoney, + bool dust, + uint64_t dustThreshold, + std::vector&& wallets, + std::vector& selectedTransfers) { + + uint64_t foundMoney = 0; + + std::vector walletOuts = wallets; + std::default_random_engine randomGenerator(Crypto::rand()); + + while (foundMoney < neededMoney && !walletOuts.empty()) { + std::uniform_int_distribution walletsDistribution(0, walletOuts.size() - 1); + + size_t walletIndex = walletsDistribution(randomGenerator); + std::vector& addressOuts = walletOuts[walletIndex].outs; + + assert(addressOuts.size() > 0); + std::uniform_int_distribution outDistribution(0, addressOuts.size() - 1); + size_t outIndex = outDistribution(randomGenerator); + + TransactionOutputInformation out = addressOuts[outIndex]; + if (!isOutputUsed(out) && (out.amount > dustThreshold || dust)) { + if (out.amount <= dustThreshold) { + dust = false; + } + + foundMoney += out.amount; + + selectedTransfers.push_back( { std::move(out), walletOuts[walletIndex].wallet } ); + } + + addressOuts.erase(addressOuts.begin() + outIndex); + if (addressOuts.empty()) { + walletOuts.erase(walletOuts.begin() + walletIndex); + } + } + + if (!dust) { + return foundMoney; + } + + for (const auto& addressOuts : walletOuts) { + auto it = std::find_if(addressOuts.outs.begin(), addressOuts.outs.end(), + [dustThreshold, this] (const TransactionOutputInformation& out) { + return out.amount <= dustThreshold && (!this->isOutputUsed(out)); + } + ); + + if (it != addressOuts.outs.end()) { + foundMoney += it->amount; + selectedTransfers.push_back({ *it, addressOuts.wallet }); + break; + } + } + + return foundMoney; +}; + +std::vector WalletGreen::pickWalletsWithMoney() { + auto& walletsIndex = m_walletsContainer.get(); + + std::vector walletOuts; + for (const auto& wallet: walletsIndex) { + if (wallet.actualBalance == 0) { + continue; + } + + ITransfersContainer* container = wallet.container; + + WalletOuts outs; + container->getOutputs(outs.outs, ITransfersContainer::IncludeKeyUnlocked); + outs.wallet = const_cast(&wallet); + + walletOuts.push_back(std::move(outs)); + }; + + return walletOuts; +} + +WalletGreen::WalletOuts WalletGreen::pickWallet(const std::string& address) { + const auto& wallet = getWalletRecord(address); + + ITransfersContainer* container = wallet.container; + WalletOuts outs; + container->getOutputs(outs.outs, ITransfersContainer::IncludeKeyUnlocked); + outs.wallet = const_cast(&wallet); + + return outs; +} + +void WalletGreen::splitDestinations(const std::vector& destinations, + const CryptoNote::WalletTransfer& changeDestination, + uint64_t dustThreshold, + const CryptoNote::Currency& currency, + std::vector& decomposedOutputs) { + + for (const auto& destination: destinations) { + ReceiverAmounts receiverAmounts; + + parseAddressString(destination.address, currency, receiverAmounts.receiver); + decomposeAmount(destination.amount, dustThreshold, receiverAmounts.amounts); + + decomposedOutputs.push_back(std::move(receiverAmounts)); + } + + ReceiverAmounts changeAmounts; + parseAddressString(changeDestination.address, currency, changeAmounts.receiver); + decomposeAmount(changeDestination.amount, dustThreshold, changeAmounts.amounts); + + decomposedOutputs.push_back(std::move(changeAmounts)); +} + +void WalletGreen::prepareInputs( + const std::vector& selectedTransfers, + std::vector& mixinResult, + uint64_t mixIn, + std::vector& keysInfo) { + + typedef CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry; + + size_t i = 0; + for (const auto& input: selectedTransfers) { + TransactionTypes::InputKeyInfo keyInfo; + keyInfo.amount = input.out.amount; + + if(mixinResult.size()) { + std::sort(mixinResult[i].outs.begin(), mixinResult[i].outs.end(), + [] (const out_entry& a, const out_entry& b) { return a.global_amount_index < b.global_amount_index; }); + for (auto& fakeOut: mixinResult[i].outs) { + + if (input.out.globalOutputIndex == fakeOut.global_amount_index) { + continue; + } + + TransactionTypes::GlobalOutput globalOutput; + globalOutput.outputIndex = static_cast(fakeOut.global_amount_index); + globalOutput.targetKey = reinterpret_cast(fakeOut.out_key); + keyInfo.outputs.push_back(std::move(globalOutput)); + if(keyInfo.outputs.size() >= mixIn) + break; + } + } + + //paste real transaction to the random index + auto insertIn = std::find_if(keyInfo.outputs.begin(), keyInfo.outputs.end(), [&](const TransactionTypes::GlobalOutput& a) { + return a.outputIndex >= input.out.globalOutputIndex; + }); + + TransactionTypes::GlobalOutput realOutput; + realOutput.outputIndex = input.out.globalOutputIndex; + realOutput.targetKey = reinterpret_cast(input.out.outputKey); + + auto insertedIn = keyInfo.outputs.insert(insertIn, realOutput); + + keyInfo.realOutput.transactionPublicKey = reinterpret_cast(input.out.transactionPublicKey); + keyInfo.realOutput.transactionIndex = static_cast(insertedIn - keyInfo.outputs.begin()); + keyInfo.realOutput.outputInTransaction = input.out.outputInTransaction; + + InputInfo inputInfo; + inputInfo.keyInfo = std::move(keyInfo); + inputInfo.walletRecord = input.wallet; + keysInfo.push_back(std::move(inputInfo)); + ++i; + } +} + +void WalletGreen::start() { + m_stopped = false; +} + +void WalletGreen::stop() { + m_stopped = true; + m_eventOccured.set(); +} + +WalletEvent WalletGreen::getEvent() { + throwIfNotInitialized(); + throwIfStopped(); + + while(m_events.empty()) { + m_eventOccured.wait(); + m_eventOccured.clear(); + throwIfStopped(); + } + + WalletEvent event = std::move(m_events.front()); + m_events.pop(); + + return event; +} + +void WalletGreen::throwIfNotInitialized() const { + if (m_state != WalletState::INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + } +} + +void WalletGreen::onError(ITransfersSubscription* object, uint32_t height, std::error_code ec) { +} + +void WalletGreen::synchronizationProgressUpdated(uint32_t current, uint32_t total) { + m_dispatcher.remoteSpawn( [current, this] () { this->onSynchronizationProgressUpdated(current); } ); +} + +void WalletGreen::onSynchronizationProgressUpdated(uint32_t current) { + System::EventLock lk(m_readyEvent); + + if (m_state == WalletState::NOT_INITIALIZED) { + return; + } + + unlockBalances(current); +} + +void WalletGreen::unlockBalances(uint32_t height) { + auto& index = m_unlockTransactionsJob.get(); + auto upper = index.upper_bound(height); + + for (auto it = index.begin(); it != upper; ++it) { + updateBalance(it->container); + } + + index.erase(index.begin(), upper); + pushEvent(makeMoneyUnlockedEvent()); +} + +void WalletGreen::onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) { + m_dispatcher.remoteSpawn([object, transactionHash, this] () { this->transactionUpdated(object, transactionHash); } ); +} + +void WalletGreen::transactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) { + System::EventLock lk(m_readyEvent); + + if (m_state == WalletState::NOT_INITIALIZED) { + return; + } + + CryptoNote::ITransfersContainer* container = &object->getContainer(); + + deleteSpentOutputs(transactionHash); + + CryptoNote::TransactionInformation info; + int64_t txBalance; + bool found = container->getTransactionInformation(transactionHash, info, txBalance); + assert(found); + + WalletEvent event; + + if (transactionExists(info.transactionHash)) { + updateTransactionHeight(info.transactionHash, info.blockHeight); + + auto id = getTransactionId(info.transactionHash); + event = makeTransactionUpdatedEvent(id); + } else { + auto id = insertIncomingTransaction(info, txBalance); + insertIncomingTransfer(id, m_currency.accountAddressAsString({ getWalletRecord(container).spendPublicKey, m_viewPublicKey }), txBalance); + + event = makeTransactionCreatedEvent(id); + } + + if (info.blockHeight != CryptoNote::WALLET_UNCONFIRMED_TRANSACTION_HEIGHT) { + //TODO: make proper calculation of unlock height + uint32_t height = info.blockHeight + static_cast(info.unlockTime) + WALLET_SOFTLOCK_BLOCKS_COUNT + 1; + m_change.erase(transactionHash); + insertUnlockTransactionJob(transactionHash, height, container); + } + + updateBalance(container); + pushEvent(event); +} + +void WalletGreen::pushEvent(const WalletEvent& event) { + m_events.push(event); + m_eventOccured.set(); +} + +size_t WalletGreen::getTransactionId(const Hash& transactionHash) const { + auto it = m_transactions.get().find(transactionHash); + + if (it == m_transactions.get().end()) { + throw std::system_error(std::make_error_code(std::errc::invalid_argument)); + } + + auto rndIt = m_transactions.project(it); + auto txId = std::distance(m_transactions.get().begin(), rndIt); + + return txId; +} + +void WalletGreen::onTransactionDeleted(ITransfersSubscription* object, const Hash& transactionHash) { + m_dispatcher.remoteSpawn([object, transactionHash, this] () { + this->transactionDeleted(object, transactionHash); }); +} + +void WalletGreen::transactionDeleted(ITransfersSubscription* object, const Hash& transactionHash) { + System::EventLock lk(m_readyEvent); + + if (m_state == WalletState::NOT_INITIALIZED) { + return; + } + + auto it = m_transactions.get().find(transactionHash); + if (it == m_transactions.get().end()) { + return; + } + + CryptoNote::ITransfersContainer* container = &object->getContainer(); + deleteUnlockTransactionJob(transactionHash); + m_change.erase(transactionHash); + deleteSpentOutputs(transactionHash); + + m_transactions.get().modify(it, [] (CryptoNote::WalletTransaction& tx) { + tx.state = WalletTransactionState::CANCELLED; + tx.blockHeight = WALLET_UNCONFIRMED_TRANSACTION_HEIGHT; + }); + + auto rndIt = m_transactions.project(it); + auto id = std::distance(m_transactions.get().begin(), rndIt); + + updateBalance(container); + pushEvent(makeTransactionUpdatedEvent(id)); +} + +void WalletGreen::insertUnlockTransactionJob(const Hash& transactionHash, uint32_t blockHeight, CryptoNote::ITransfersContainer* container) { + auto& index = m_unlockTransactionsJob.get(); + index.insert( { blockHeight, container, transactionHash } ); +} + +void WalletGreen::deleteUnlockTransactionJob(const Hash& transactionHash) { + auto& index = m_unlockTransactionsJob.get(); + index.erase(transactionHash); +} + +void WalletGreen::updateBalance(CryptoNote::ITransfersContainer* container) { + auto it = m_walletsContainer.get().find(container); + + if (it == m_walletsContainer.get().end()) { + return; + } + + uint64_t actual = container->balance(ITransfersContainer::IncludeAllUnlocked); + uint64_t pending = container->balance(ITransfersContainer::IncludeAllLocked); + + uint64_t unconfirmedBalance = countSpentBalance(&(*it)); + + actual -= unconfirmedBalance; + + //xxx: i don't like this special case. Decompose this function + if (container == m_walletsContainer.get()[0].container) { + uint64_t change = 0; + std::for_each(m_change.begin(), m_change.end(), [&change] (const TransactionChanges::value_type& item) { change += item.second; }); + pending += change; + } + + if (it->actualBalance < actual) { + m_actualBalance += actual - it->actualBalance; + } else { + m_actualBalance -= it->actualBalance - actual; + } + + if (it->pendingBalance < pending) { + m_pendingBalance += pending - it->pendingBalance; + } else { + m_pendingBalance -= it->pendingBalance - pending; + } + + m_walletsContainer.get().modify(it, [actual, pending] (WalletRecord& wallet) { + wallet.actualBalance = actual; + wallet.pendingBalance = pending; + }); +} + +const WalletRecord& WalletGreen::getWalletRecord(const PublicKey& key) const { + auto it = m_walletsContainer.get().find(key); + if (it == m_walletsContainer.get().end()) { + throw std::system_error(std::make_error_code(std::errc::invalid_argument)); + } + + return *it; +} + +const WalletRecord& WalletGreen::getWalletRecord(const std::string& address) const { + CryptoNote::AccountPublicAddress pubAddr = parseAddress(address); + return getWalletRecord(pubAddr.spendPublicKey); +} + +const WalletRecord& WalletGreen::getWalletRecord(CryptoNote::ITransfersContainer* container) const { + auto it = m_walletsContainer.get().find(container); + if (it == m_walletsContainer.get().end()) { + throw std::system_error(std::make_error_code(std::errc::invalid_argument)); + } + + return *it; +} + +CryptoNote::AccountPublicAddress WalletGreen::parseAddress(const std::string& address) const { + CryptoNote::AccountPublicAddress pubAddr; + + if (!m_currency.parseAccountAddressString(address, pubAddr)) { + throw std::system_error(std::make_error_code(std::errc::invalid_argument)); + } + + return pubAddr; +} + +bool WalletGreen::isOutputUsed(const TransactionOutputInformation& out) const { + return m_spentOutputs.get().find(boost::make_tuple(out.transactionHash, out.outputInTransaction)) + != + m_spentOutputs.get().end(); +} + +void WalletGreen::markOutputsSpent(const Hash& transactionHash,const std::vector& selectedTransfers) { + auto& index = m_spentOutputs.get(); + + for (const auto& output: selectedTransfers) { + index.insert( {output.out.amount, output.out.transactionHash, output.out.outputInTransaction, output.wallet, transactionHash} ); + } +} + +void WalletGreen::deleteSpentOutputs(const Hash& transactionHash) { + auto& index = m_spentOutputs.get(); + index.erase(transactionHash); +} + +uint64_t WalletGreen::countSpentBalance(const WalletRecord* wallet) { + uint64_t amount = 0; + + auto bounds = m_spentOutputs.get().equal_range(wallet); + for (auto it = bounds.first; it != bounds.second; ++it) { + amount += it->amount; + } + + return amount; +} + +void WalletGreen::updateUsedWalletsBalances(const std::vector& selectedTransfers) { + std::set wallets; + + // wallet #0 recieves change, so we have to update it after transfer + wallets.insert(const_cast(&m_walletsContainer.get()[0])); + + std::for_each(selectedTransfers.begin(), selectedTransfers.end(), [&wallets] (const OutputToTransfer& output) { wallets.insert(output.wallet); } ); + std::for_each(wallets.begin(), wallets.end(), [this] (WalletRecord* wallet) { + this->updateBalance(wallet->container); + }); +} + +void WalletGreen::throwIfStopped() const { + if (m_stopped) { + throw std::system_error(make_error_code(error::OPERATION_CANCELLED)); + } +} + +} //namespace CryptoNote diff --git a/src/Wallet/WalletGreen.h b/src/Wallet/WalletGreen.h new file mode 100755 index 00000000..00031d9a --- /dev/null +++ b/src/Wallet/WalletGreen.h @@ -0,0 +1,211 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "IWallet.h" + +#include + +#include "WalletIndices.h" + +#include +#include +#include "Transfers/TransfersSynchronizer.h" +#include "Transfers/BlockchainSynchronizer.h" + +namespace CryptoNote { + +class WalletGreen : public IWallet, + ITransfersObserver, + IBlockchainSynchronizerObserver { +public: + WalletGreen(System::Dispatcher& dispatcher, const Currency& currency, INode& node); + virtual ~WalletGreen(); + + virtual void initialize(const std::string& password) override; + virtual void load(std::istream& source, const std::string& password) override; + virtual void shutdown() override; + + virtual void changePassword(const std::string& oldPassword, const std::string& newPassword) override; + virtual void save(std::ostream& destination, bool saveDetails = true, bool saveCache = true) override; + + virtual size_t getAddressCount() const override; + virtual std::string getAddress(size_t index) const override; + virtual std::string createAddress() override; + virtual std::string createAddress(const KeyPair& spendKey) override; + virtual void deleteAddress(const std::string& address) override; + + virtual uint64_t getActualBalance() const override; + virtual uint64_t getActualBalance(const std::string& address) const override; + virtual uint64_t getPendingBalance() const override; + virtual uint64_t getPendingBalance(const std::string& address) const override; + + virtual size_t getTransactionCount() const override; + virtual WalletTransaction getTransaction(size_t transactionIndex) const override; + virtual size_t getTransactionTransferCount(size_t transactionIndex) const override; + virtual WalletTransfer getTransactionTransfer(size_t transactionIndex, size_t transferIndex) const override; + + virtual size_t transfer(const WalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override; + virtual size_t transfer(const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override; + virtual size_t transfer(const std::string& sourceAddress, const WalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override; + virtual size_t transfer(const std::string& sourceAddress, const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override; + + virtual void start() override; + virtual void stop() override; + virtual WalletEvent getEvent() override; + +protected: + void throwIfNotInitialized() const; + void throwIfStopped() const; + void doShutdown(); + void clearCaches(); + + struct InputInfo { + TransactionTypes::InputKeyInfo keyInfo; + WalletRecord* walletRecord = nullptr; + KeyPair ephKeys; + }; + + struct OutputToTransfer { + TransactionOutputInformation out; + WalletRecord* wallet; + }; + + struct ReceiverAmounts { + CryptoNote::AccountPublicAddress receiver; + std::vector amounts; + }; + + struct WalletOuts { + WalletRecord* wallet; + std::vector outs; + }; + + virtual void onError(ITransfersSubscription* object, uint32_t height, std::error_code ec) override; + virtual void onTransactionUpdated(ITransfersSubscription* object, const Crypto::Hash& transactionHash) override; + virtual void onTransactionDeleted(ITransfersSubscription* object, const Crypto::Hash& transactionHash) override; + virtual void synchronizationProgressUpdated(uint32_t current, uint32_t total) override; + + void transactionUpdated(ITransfersSubscription* object, const Crypto::Hash& transactionHash); + void transactionDeleted(ITransfersSubscription* object, const Crypto::Hash& transactionHash); + void onSynchronizationProgressUpdated(uint32_t current); + + std::vector pickWalletsWithMoney(); + WalletOuts pickWallet(const std::string& address); + + void updateBalance(CryptoNote::ITransfersContainer* container); + void unlockBalances(uint32_t height); + + const WalletRecord& getWalletRecord(const Crypto::PublicKey& key) const; + const WalletRecord& getWalletRecord(const std::string& address) const; + const WalletRecord& getWalletRecord(CryptoNote::ITransfersContainer* container) const; + + CryptoNote::AccountPublicAddress parseAddress(const std::string& address) const; + void addWallet(const KeyPair& spendKey); + bool isOutputUsed(const TransactionOutputInformation& out) const; + void markOutputsSpent(const Crypto::Hash& transactionHash, const std::vector& selectedTransfers); + void deleteSpentOutputs(const Crypto::Hash& transactionHash); + uint64_t countSpentBalance(const WalletRecord* wallet); + void updateUsedWalletsBalances(const std::vector& selectedTransfers); + AccountKeys makeAccountKeys(const WalletRecord& wallet) const; + size_t getTransactionId(const Crypto::Hash& transactionHash) const; + void pushEvent(const WalletEvent& event); + + size_t doTransfer(std::vector&& wallets, + const std::vector& destinations, + uint64_t fee, + uint64_t mixIn, + const std::string& extra, + uint64_t unlockTimestamp); + + void requestMixinOuts(const std::vector& selectedTransfers, + uint64_t mixIn, + std::vector& mixinResult); + + void prepareInputs(const std::vector& selectedTransfers, + std::vector& mixinResult, + uint64_t mixIn, + std::vector& keysInfo); + + uint64_t selectTransfers(uint64_t needeMoney, + bool dust, + uint64_t dustThreshold, + std::vector&& wallets, + std::vector& selectedTransfers); + + void splitDestinations(const std::vector& destinations, const WalletTransfer& changeDestination, + uint64_t dustThreshold, const Currency& currency, std::vector& decomposedOutputs); + + std::unique_ptr makeTransaction(const std::vector& decomposedOutputs, + std::vector& keysInfo, const std::string& extra, uint64_t unlockTimestamp); + + void sendTransaction(ITransaction* tx); + + size_t insertOutgoingTransaction(const Crypto::Hash& transactionHash, int64_t totalAmount, uint64_t fee, const BinaryArray& extra, uint64_t unlockTimestamp); + bool transactionExists(const Crypto::Hash& hash); + void updateTransactionHeight(const Crypto::Hash& hash, uint32_t blockHeight); + size_t insertIncomingTransaction(const TransactionInformation& info, int64_t txBalance); + void insertIncomingTransfer(size_t txId, const std::string& address, int64_t amount); + void pushBackOutgoingTransfers(size_t txId, const std::vector &destinations); + void insertUnlockTransactionJob(const Crypto::Hash& transactionHash, uint32_t blockHeight, CryptoNote::ITransfersContainer* container); + void deleteUnlockTransactionJob(const Crypto::Hash& transactionHash); + + void unsafeLoad(std::istream& source, const std::string& password); + void unsafeSave(std::ostream& destination, bool saveDetails, bool saveCache); + + + enum class WalletState { + INITIALIZED, + NOT_INITIALIZED + }; + + std::pair getTransactionTransfers(size_t transactionIndex) const; + + System::Dispatcher& m_dispatcher; + const Currency& m_currency; + INode& m_node; + bool m_stopped = false; + + WalletsContainer m_walletsContainer; + SpentOutputs m_spentOutputs; + UnlockTransactionJobs m_unlockTransactionsJob; + WalletTransactions m_transactions; + WalletTransfers m_transfers; //sorted + TransactionChanges m_change; + + BlockchainSynchronizer m_blockchainSynchronizer; + TransfersSyncronizer m_synchronizer; + + System::Event m_eventOccured; + std::queue m_events; + mutable System::Event m_readyEvent; + + WalletState m_state = WalletState::NOT_INITIALIZED; + + std::string m_password; + + Crypto::PublicKey m_viewPublicKey; + Crypto::SecretKey m_viewSecretKey; + + uint64_t m_actualBalance = 0; + uint64_t m_pendingBalance = 0; + + uint64_t m_upperTransactionSizeLimit; +}; + +} //namespace CryptoNote diff --git a/src/Wallet/WalletIndices.h b/src/Wallet/WalletIndices.h new file mode 100644 index 00000000..4b7fac4f --- /dev/null +++ b/src/Wallet/WalletIndices.h @@ -0,0 +1,124 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +#include "ITransfersContainer.h" +#include "IWallet.h" +#include "IWalletLegacy.h" //TODO: make common types for all of our APIs (such as PublicKey, KeyPair, etc) + +#include +#include +#include +#include +#include +#include + +struct WalletRecord { + Crypto::PublicKey spendPublicKey; + Crypto::SecretKey spendSecretKey; + CryptoNote::ITransfersContainer* container = nullptr; + uint64_t pendingBalance = 0; + uint64_t actualBalance = 0; + time_t creationTimestamp; +}; + +struct SpentOutput { + uint64_t amount; + Crypto::Hash transactionHash; + uint32_t outputInTransaction; + const WalletRecord* wallet; + Crypto::Hash spendingTransactionHash; +}; + +struct RandomAccessIndex {}; +struct KeysIndex {}; +struct TransfersContainerIndex {}; + +struct WalletIndex {}; +struct TransactionOutputIndex {}; +struct BlockHeightIndex {}; + +struct TransactionHashIndex {}; +struct TransactionIndex {}; + +typedef boost::multi_index_container < + WalletRecord, + boost::multi_index::indexed_by < + boost::multi_index::random_access < boost::multi_index::tag >, + boost::multi_index::hashed_unique < boost::multi_index::tag , + BOOST_MULTI_INDEX_MEMBER(WalletRecord, Crypto::PublicKey, spendPublicKey)>, + boost::multi_index::hashed_unique < boost::multi_index::tag , + BOOST_MULTI_INDEX_MEMBER(WalletRecord, CryptoNote::ITransfersContainer*, container) > + > +> WalletsContainer; + +struct OutputIndex: boost::multi_index::composite_key < + SpentOutput, + BOOST_MULTI_INDEX_MEMBER(SpentOutput, Crypto::Hash, transactionHash), + BOOST_MULTI_INDEX_MEMBER(SpentOutput, uint32_t, outputInTransaction) +> {}; + +typedef boost::multi_index_container < + SpentOutput, + boost::multi_index::indexed_by < + boost::multi_index::hashed_unique < boost::multi_index::tag , + OutputIndex + >, + boost::multi_index::hashed_non_unique < boost::multi_index::tag , + BOOST_MULTI_INDEX_MEMBER(SpentOutput, Crypto::Hash, spendingTransactionHash) + >, + boost::multi_index::hashed_non_unique < boost::multi_index::tag , + BOOST_MULTI_INDEX_MEMBER(SpentOutput, const WalletRecord *, wallet) + > + > +> SpentOutputs; + +struct UnlockTransactionJob { + uint32_t blockHeight; + CryptoNote::ITransfersContainer* container; + Crypto::Hash transactionHash; +}; + +typedef boost::multi_index_container < + UnlockTransactionJob, + boost::multi_index::indexed_by < + boost::multi_index::ordered_non_unique < boost::multi_index::tag , + BOOST_MULTI_INDEX_MEMBER(UnlockTransactionJob, uint32_t, blockHeight) + >, + boost::multi_index::hashed_unique < boost::multi_index::tag , + BOOST_MULTI_INDEX_MEMBER(UnlockTransactionJob, Crypto::Hash, transactionHash) + > + > +> UnlockTransactionJobs; + +typedef boost::multi_index_container < + CryptoNote::WalletTransaction, + boost::multi_index::indexed_by < + boost::multi_index::random_access < boost::multi_index::tag >, + boost::multi_index::hashed_unique < boost::multi_index::tag , + boost::multi_index::member + > + > +> WalletTransactions; + +typedef std::unordered_map TransactionChanges; + +typedef std::pair TransactionTransferPair; +typedef std::vector WalletTransfers; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/Wallet/WalletRpcServer.cpp similarity index 87% rename from src/wallet/wallet_rpc_server.cpp rename to src/Wallet/WalletRpcServer.cpp index bf03f2a3..4d76d463 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/Wallet/WalletRpcServer.cpp @@ -15,24 +15,24 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "wallet_rpc_server.h" +#include "WalletRpcServer.h" #include -#include "Common/command_line.h" +#include "Common/CommandLine.h" #include "Common/StringTools.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/account.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteCore/Account.h" #include "crypto/hash.h" -#include "WalletHelper.h" +#include "WalletLegacy/WalletHelper.h" // #include "wallet_errors.h" -#include "rpc/JsonRpc.h" +#include "Rpc/JsonRpc.h" using namespace Logging; using namespace CryptoNote; -namespace tools { +namespace Tools { const command_line::arg_descriptor wallet_rpc_server::arg_rpc_bind_port = { "rpc-bind-port", "Starts wallet as rpc server for wallet operations, sets bind port for server", 0, true }; const command_line::arg_descriptor wallet_rpc_server::arg_rpc_bind_ip = { "rpc-bind-ip", "Specify ip to bind rpc server", "127.0.0.1" }; @@ -45,7 +45,7 @@ void wallet_rpc_server::init_options(boost::program_options::options_description wallet_rpc_server::wallet_rpc_server( System::Dispatcher& dispatcher, Logging::ILogger& log, - CryptoNote::IWallet&w, + CryptoNote::IWalletLegacy&w, CryptoNote::INode& n, CryptoNote::Currency& currency, const std::string& walletFile) @@ -55,7 +55,6 @@ wallet_rpc_server::wallet_rpc_server( m_dispatcher(dispatcher), m_stopComplete(dispatcher), m_wallet(w), - m_syncWallet(w), m_node(n), m_currency(currency), m_walletFilename(walletFile) { @@ -130,15 +129,15 @@ void wallet_rpc_server::processRequest(const CryptoNote::HttpRequest& request, C //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res) { - res.locked_amount = m_wallet.pendingBalance(); - res.available_balance = m_wallet.actualBalance(); + res.locked_amount = m_wallet.pendingBalance(); + res.available_balance = m_wallet.actualBalance(); return true; } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res) { - std::vector transfers; + std::vector transfers; for (auto it = req.destinations.begin(); it != req.destinations.end(); it++) { - CryptoNote::Transfer transfer; + CryptoNote::WalletLegacyTransfer transfer; transfer.address = it->address; transfer.amount = it->amount; transfers.push_back(transfer); @@ -148,15 +147,15 @@ bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::requ if (!req.payment_id.empty()) { std::string payment_id_str = req.payment_id; - crypto::hash payment_id; + Crypto::Hash payment_id; if (!CryptoNote::parsePaymentId(payment_id_str, payment_id)) { throw JsonRpc::JsonRpcError(WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID, "Payment id has invalid format: \"" + payment_id_str + "\", expected 64-character string"); } - std::string extra_nonce; - CryptoNote::set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); - if (!CryptoNote::add_extra_nonce_to_tx_extra(extra, extra_nonce)) { + BinaryArray extra_nonce; + CryptoNote::setPaymentIdToTransactionExtraNonce(extra_nonce, payment_id); + if (!CryptoNote::addExtraNonceToTransactionExtra(extra, extra_nonce)) { throw JsonRpc::JsonRpcError(WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID, "Something went wrong with payment_id. Please check its format: \"" + payment_id_str + "\", expected 64-character string"); } @@ -169,7 +168,7 @@ bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::requ WalletHelper::IWalletRemoveObserverGuard removeGuard(m_wallet, sent); CryptoNote::TransactionId tx = m_wallet.sendTransaction(transfers, req.fee, extraString, req.mixin, req.unlock_time); - if (tx == INVALID_TRANSACTION_ID) { + if (tx == WALLET_LEGACY_INVALID_TRANSACTION_ID) { throw std::runtime_error("Couldn't send transaction"); } @@ -180,7 +179,7 @@ bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::requ throw std::system_error(sendError); } - CryptoNote::TransactionInfo txInfo; + CryptoNote::WalletLegacyTransaction txInfo; m_wallet.getTransaction(tx, txInfo); res.tx_hash = Common::podToHex(txInfo.hash); @@ -201,10 +200,10 @@ bool wallet_rpc_server::on_store(const wallet_rpc::COMMAND_RPC_STORE::request& r } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res) { - crypto::hash expectedPaymentId; - CryptoNote::blobdata payment_id_blob; + Crypto::Hash expectedPaymentId; + CryptoNote::BinaryArray payment_id_blob; - if (!hexToBlob(req.payment_id, payment_id_blob)) { + if (!Common::fromHex(req.payment_id, payment_id_blob)) { throw JsonRpc::JsonRpcError(WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID, "Payment ID has invald format"); } @@ -212,12 +211,12 @@ bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMEN throw JsonRpc::JsonRpcError(WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID, "Payment ID has invalid size"); } - expectedPaymentId = *reinterpret_cast(payment_id_blob.data()); + expectedPaymentId = *reinterpret_cast(payment_id_blob.data()); size_t transactionsCount = m_wallet.getTransactionCount(); for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { - TransactionInfo txInfo; + WalletLegacyTransaction txInfo; m_wallet.getTransaction(trantransactionNumber, txInfo); - if (txInfo.state != TransactionState::Active || txInfo.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { + if (txInfo.state != WalletLegacyTransactionState::Active || txInfo.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { continue; } @@ -226,7 +225,7 @@ bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMEN extraVec.reserve(txInfo.extra.size()); std::for_each(txInfo.extra.begin(), txInfo.extra.end(), [&extraVec](const char el) { extraVec.push_back(el); }); - crypto::hash paymentId; + Crypto::Hash paymentId; if (getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId == expectedPaymentId) { wallet_rpc::payment_details rpc_payment; rpc_payment.tx_hash = Common::podToHex(txInfo.hash); @@ -244,16 +243,16 @@ bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANS res.transfers.clear(); size_t transactionsCount = m_wallet.getTransactionCount(); for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { - TransactionInfo txInfo; + WalletLegacyTransaction txInfo; m_wallet.getTransaction(trantransactionNumber, txInfo); - if (txInfo.state != TransactionState::Active || txInfo.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { + if (txInfo.state != WalletLegacyTransactionState::Active || txInfo.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { continue; } std::string address = ""; if (txInfo.totalAmount < 0) { if (txInfo.transferCount > 0) { - Transfer tr; + WalletLegacyTransfer tr; m_wallet.getTransfer(txInfo.firstTransferId, tr); address = tr.address; } @@ -274,8 +273,8 @@ bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANS extraVec.reserve(txInfo.extra.size()); std::for_each(txInfo.extra.begin(), txInfo.extra.end(), [&extraVec](const char el) { extraVec.push_back(el); }); - crypto::hash paymentId; - transfer.paymentId = (getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId != null_hash ? Common::podToHex(paymentId) : ""); + Crypto::Hash paymentId; + transfer.paymentId = (getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId != NULL_HASH ? Common::podToHex(paymentId) : ""); res.transfers.push_back(transfer); } diff --git a/src/wallet/wallet_rpc_server.h b/src/Wallet/WalletRpcServer.h old mode 100644 new mode 100755 similarity index 92% rename from src/wallet/wallet_rpc_server.h rename to src/Wallet/WalletRpcServer.h index 26b80f23..c76b3a0c --- a/src/wallet/wallet_rpc_server.h +++ b/src/Wallet/WalletRpcServer.h @@ -20,15 +20,14 @@ #include #include #include -#include "wallet_rpc_server_commans_defs.h" -#include "Wallet.h" -#include "SyncWallet.h" -#include "Common/command_line.h" -#include "rpc/HttpServer.h" +#include "WalletRpcServerCommandsDefinitions.h" +#include "WalletLegacy/WalletLegacy.h" +#include "Common/CommandLine.h" +#include "Rpc/HttpServer.h" #include -namespace tools +namespace Tools { /************************************************************************/ /* */ @@ -40,7 +39,7 @@ namespace tools wallet_rpc_server( System::Dispatcher& dispatcher, Logging::ILogger& log, - CryptoNote::IWallet &w, + CryptoNote::IWalletLegacy &w, CryptoNote::INode &n, CryptoNote::Currency& currency, const std::string& walletFilename); @@ -71,8 +70,7 @@ namespace tools bool handle_command_line(const boost::program_options::variables_map& vm); Logging::LoggerRef logger; - CryptoNote::IWallet& m_wallet; - CryptoNote::SyncWallet m_syncWallet; + CryptoNote::IWalletLegacy& m_wallet; CryptoNote::INode& m_node; uint16_t m_port; std::string m_bind_ip; diff --git a/src/wallet/wallet_rpc_server_commans_defs.h b/src/Wallet/WalletRpcServerCommandsDefinitions.h old mode 100644 new mode 100755 similarity index 95% rename from src/wallet/wallet_rpc_server_commans_defs.h rename to src/Wallet/WalletRpcServerCommandsDefinitions.h index 8ef7be5a..b61f127b --- a/src/wallet/wallet_rpc_server_commans_defs.h +++ b/src/Wallet/WalletRpcServerCommandsDefinitions.h @@ -16,12 +16,12 @@ // along with Bytecoin. If not, see . #pragma once -#include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "cryptonote_core/cryptonote_basic.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" #include "crypto/hash.h" -#include "wallet_rpc_server_error_codes.h" +#include "WalletRpcServerErrorCodes.h" -namespace tools +namespace Tools { namespace wallet_rpc { diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/Wallet/WalletRpcServerErrorCodes.h old mode 100644 new mode 100755 similarity index 100% rename from src/wallet/wallet_rpc_server_error_codes.h rename to src/Wallet/WalletRpcServerErrorCodes.h diff --git a/src/Wallet/WalletSerialization.cpp b/src/Wallet/WalletSerialization.cpp new file mode 100755 index 00000000..d68adff1 --- /dev/null +++ b/src/Wallet/WalletSerialization.cpp @@ -0,0 +1,873 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "WalletSerialization.h" + +#include +#include +#include + +#include "Common/MemoryInputStream.h" +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "CryptoNoteCore/CryptoNoteSerialization.h" + +#include "Serialization/BinaryOutputStreamSerializer.h" +#include "Serialization/BinaryInputStreamSerializer.h" +#include "Serialization/SerializationOverloads.h" + +#include "Wallet/WalletErrors.h" +#include "WalletLegacy/KeysStorage.h" +#include "WalletLegacy/WalletLegacySerialization.h" + +using namespace Common; +using namespace Crypto; + +namespace { + +//DO NOT CHANGE IT +struct WalletRecordDto { + PublicKey spendPublicKey; + SecretKey spendSecretKey; + uint64_t pendingBalance = 0; + uint64_t actualBalance = 0; + uint64_t creationTimestamp = 0; +}; + +//DO NOT CHANGE IT +struct SpentOutputDto { + uint64_t amount; + Hash transactionHash; + uint32_t outputInTransaction; + uint64_t walletIndex; + Crypto::Hash spendingTransactionHash; +}; + +//DO NOT CHANGE IT +struct ChangeDto { + Hash txHash; + uint64_t amount; +}; + +//DO NOT CHANGE IT +struct UnlockTransactionJobDto { + uint32_t blockHeight; + Hash transactionHash; + uint64_t walletIndex; +}; + +//DO NOT CHANGE IT +struct WalletTransactionDto { + WalletTransactionDto() {} + + WalletTransactionDto(const CryptoNote::WalletTransaction& wallet) { + state = wallet.state; + timestamp = wallet.timestamp; + blockHeight = wallet.blockHeight; + hash = wallet.hash; + totalAmount = wallet.totalAmount; + fee = wallet.fee; + creationTime = wallet.creationTime; + unlockTime = wallet.unlockTime; + extra = wallet.extra; + } + + CryptoNote::WalletTransactionState state; + uint64_t timestamp; + uint32_t blockHeight; + Hash hash; + int64_t totalAmount; + uint64_t fee; + uint64_t creationTime; + uint64_t unlockTime; + std::string extra; +}; + +//DO NOT CHANGE IT +struct WalletTransferDto { + WalletTransferDto() {} + WalletTransferDto(const CryptoNote::WalletTransfer& tr) { + address = tr.address; + amount = tr.amount; + } + + std::string address; + uint64_t amount; +}; + +void serialize(WalletRecordDto& value, CryptoNote::ISerializer& serializer) { + serializer(value.spendPublicKey, "spend_public_key"); + serializer(value.spendSecretKey, "spend_secret_key"); + serializer(value.pendingBalance, "pending_balance"); + serializer(value.actualBalance, "actual_balance"); + serializer(value.creationTimestamp, "creation_timestamp"); +} + +void serialize(SpentOutputDto& value, CryptoNote::ISerializer& serializer) { + serializer(value.amount, "amount"); + serializer(value.transactionHash, "transaction_hash"); + serializer(value.outputInTransaction, "output_in_transaction"); + serializer(value.walletIndex, "wallet_index"); + serializer(value.spendingTransactionHash, "spending_transaction_hash"); +} + +void serialize(ChangeDto& value, CryptoNote::ISerializer& serializer) { + serializer(value.txHash, "transaction_hash"); + serializer(value.amount, "amount"); +} + +void serialize(UnlockTransactionJobDto& value, CryptoNote::ISerializer& serializer) { + serializer(value.blockHeight, "block_height"); + serializer(value.transactionHash, "transaction_hash"); + serializer(value.walletIndex, "wallet_index"); +} + +void serialize(WalletTransactionDto& value, CryptoNote::ISerializer& serializer) { + typedef std::underlying_type::type StateType; + + StateType state = static_cast(value.state); + serializer(state, "state"); + value.state = static_cast(state); + + serializer(value.timestamp, "timestamp"); + serializer(value.blockHeight, "block_height"); + serializer(value.hash, "hash"); + serializer(value.totalAmount, "total_amount"); + serializer(value.fee, "fee"); + serializer(value.creationTime, "creation_time"); + serializer(value.unlockTime, "unlock_time"); + serializer(value.extra, "extra"); +} + +void serialize(WalletTransferDto& value, CryptoNote::ISerializer& serializer) { + serializer(value.address, "address"); + serializer(value.amount, "amount"); +} + + +template +std::string serialize(Object& obj, const std::string& name) { + std::stringstream stream; + StdOutputStream output(stream); + CryptoNote::BinaryOutputStreamSerializer s(output); + + s(obj, Common::StringView(name)); + + stream.flush(); + return stream.str(); +} + +std::string encrypt(const std::string& plain, CryptoNote::CryptoContext& cryptoContext) { + std::string cipher; + cipher.resize(plain.size()); + + Crypto::chacha8(plain.data(), plain.size(), cryptoContext.key, cryptoContext.iv, &cipher[0]); + + return cipher; +} + +void addToStream(const std::string& cipher, const std::string& name, Common::IOutputStream& destination) { + CryptoNote::BinaryOutputStreamSerializer s(destination); + s(const_cast(cipher), name); +} + +template +void serializeEncrypted(Object& obj, const std::string& name, CryptoNote::CryptoContext& cryptoContext, Common::IOutputStream& destination) { + std::string plain = serialize(obj, name); + std::string cipher = encrypt(plain, cryptoContext); + + addToStream(cipher, name, destination); +} + +std::string readCipher(Common::IInputStream& source, const std::string& name) { + std::string cipher; + CryptoNote::BinaryInputStreamSerializer s(source); + s(cipher, name); + + return cipher; +} + +std::string decrypt(const std::string& cipher, CryptoNote::CryptoContext& cryptoContext) { + std::string plain; + plain.resize(cipher.size()); + + Crypto::chacha8(cipher.data(), cipher.size(), cryptoContext.key, cryptoContext.iv, &plain[0]); + return plain; +} + +template +void deserialize(Object& obj, const std::string& name, const std::string& plain) { + MemoryInputStream stream(plain.data(), plain.size()); + CryptoNote::BinaryInputStreamSerializer s(stream); + s(obj, Common::StringView(name)); +} + +template +void deserializeEncrypted(Object& obj, const std::string& name, CryptoNote::CryptoContext& cryptoContext, Common::IInputStream& source) { + std::string cipher = readCipher(source, name); + std::string plain = decrypt(cipher, cryptoContext); + + deserialize(obj, name, plain); +} + +bool verifyKeys(const SecretKey& sec, const PublicKey& expected_pub) { + PublicKey pub; + bool r = Crypto::secret_key_to_public_key(sec, pub); + + return r && expected_pub == pub; +} + +void throwIfKeysMissmatch(const SecretKey& sec, const PublicKey& expected_pub) { + if (!verifyKeys(sec, expected_pub)) + throw std::system_error(make_error_code(CryptoNote::error::WRONG_PASSWORD)); +} + +CryptoNote::WalletTransaction convert(const CryptoNote::WalletLegacyTransaction& tx) { + CryptoNote::WalletTransaction mtx; + + mtx.state = CryptoNote::WalletTransactionState::SUCCEEDED; + mtx.timestamp = tx.timestamp; + mtx.blockHeight = tx.blockHeight; + mtx.hash = tx.hash; + mtx.totalAmount = tx.totalAmount; + mtx.fee = tx.fee; + mtx.creationTime = tx.sentTime; + mtx.unlockTime = tx.unlockTime; + mtx.extra = tx.extra; + + return mtx; +} + +CryptoNote::WalletTransfer convert(const CryptoNote::WalletLegacyTransfer& tr) { + CryptoNote::WalletTransfer mtr; + + mtr.address = tr.address; + mtr.amount = tr.amount; + + return mtr; +} + +} + +namespace CryptoNote { + +const uint32_t WalletSerializer::SERIALIZATION_VERSION = 2; + +void CryptoContext::incIv() { + uint64_t * i = reinterpret_cast(&iv.data[0]); + (*i)++; +} + +WalletSerializer::WalletSerializer( + ITransfersObserver& transfersObserver, + PublicKey& viewPublicKey, + SecretKey& viewSecretKey, + uint64_t& actualBalance, + uint64_t& pendingBalance, + WalletsContainer& walletsContainer, + TransfersSyncronizer& synchronizer, + SpentOutputs& spentOutputs, + UnlockTransactionJobs& unlockTransactions, + TransactionChanges& change, + WalletTransactions& transactions, + WalletTransfers& transfers +) : + m_transfersObserver(transfersObserver), + m_viewPublicKey(viewPublicKey), + m_viewSecretKey(viewSecretKey), + m_actualBalance(actualBalance), + m_pendingBalance(pendingBalance), + m_walletsContainer(walletsContainer), + m_synchronizer(synchronizer), + m_spentOutputs(spentOutputs), + m_unlockTransactions(unlockTransactions), + m_change(change), + m_transactions(transactions), + m_transfers(transfers) +{ } + +void WalletSerializer::save(const std::string& password, Common::IOutputStream& destination, bool saveDetails, bool saveCache) { + CryptoContext cryptoContext = generateCryptoContext(password); + + CryptoNote::BinaryOutputStreamSerializer s(destination); + s.beginObject("wallet"); + + saveVersion(destination); + saveIv(destination, cryptoContext.iv); + + saveKeys(destination, cryptoContext); + saveWallets(destination, saveCache, cryptoContext); + saveFlags(saveDetails, saveCache, destination, cryptoContext); + + if (saveDetails) { + saveTransactions(destination, cryptoContext); + saveTransfers(destination, cryptoContext); + } + + if (saveCache) { + saveBalances(destination, saveCache, cryptoContext); + saveTransfersSynchronizer(destination, cryptoContext); + saveSpentOutputs(destination, cryptoContext); + saveUnlockTransactionsJobs(destination, cryptoContext); + saveChange(destination, cryptoContext); + } + + s.endObject(); +} + +CryptoContext WalletSerializer::generateCryptoContext(const std::string& password) { + CryptoContext context; + + Crypto::cn_context c; + Crypto::generate_chacha8_key(c, password, context.key); + + context.iv = Crypto::rand(); + + return context; +} + +void WalletSerializer::saveVersion(Common::IOutputStream& destination) { + uint32_t version = SERIALIZATION_VERSION; + + BinaryOutputStreamSerializer s(destination); + s(version, "version"); +} + +void WalletSerializer::saveIv(Common::IOutputStream& destination, Crypto::chacha8_iv& iv) { + BinaryOutputStreamSerializer s(destination); + s.binary(reinterpret_cast(&iv.data), sizeof(iv.data), "chacha_iv"); +} + +void WalletSerializer::saveKeys(Common::IOutputStream& destination, CryptoContext& cryptoContext) { + savePublicKey(destination, cryptoContext); + saveSecretKey(destination, cryptoContext); +} + +void WalletSerializer::savePublicKey(Common::IOutputStream& destination, CryptoContext& cryptoContext) { + serializeEncrypted(m_viewPublicKey, "public_key", cryptoContext, destination); + cryptoContext.incIv(); +} + +void WalletSerializer::saveSecretKey(Common::IOutputStream& destination, CryptoContext& cryptoContext) { + serializeEncrypted(m_viewSecretKey, "secret_key", cryptoContext, destination); + cryptoContext.incIv(); +} + +void WalletSerializer::saveFlags(bool saveDetails, bool saveCache, Common::IOutputStream& destination, CryptoContext& cryptoContext) { + serializeEncrypted(saveDetails, "details", cryptoContext, destination); + cryptoContext.incIv(); + + serializeEncrypted(saveCache, "cache", cryptoContext, destination); + cryptoContext.incIv(); +} + +void WalletSerializer::saveWallets(Common::IOutputStream& destination, bool saveCache, CryptoContext& cryptoContext) { + auto& index = m_walletsContainer.get(); + + uint64_t count = index.size(); + serializeEncrypted(count, "wallets_count", cryptoContext, destination); + cryptoContext.incIv(); + + for (const auto& w: index) { + WalletRecordDto dto; + dto.spendPublicKey = w.spendPublicKey; + dto.spendSecretKey = w.spendSecretKey; + dto.pendingBalance = saveCache ? w.pendingBalance : 0; + dto.actualBalance = saveCache ? w.actualBalance : 0; + dto.creationTimestamp = static_cast(w.creationTimestamp); + + serializeEncrypted(dto, "", cryptoContext, destination); + cryptoContext.incIv(); + } +} + +void WalletSerializer::saveBalances(Common::IOutputStream& destination, bool saveCache, CryptoContext& cryptoContext) { + uint64_t actual = saveCache ? m_actualBalance : 0; + uint64_t pending = saveCache ? m_pendingBalance : 0; + + serializeEncrypted(actual, "actual_balance", cryptoContext, destination); + cryptoContext.incIv(); + + serializeEncrypted(pending, "pending_balance", cryptoContext, destination); + cryptoContext.incIv(); +} + +void WalletSerializer::saveTransfersSynchronizer(Common::IOutputStream& destination, CryptoContext& cryptoContext) { + std::stringstream stream; + m_synchronizer.save(stream); + stream.flush(); + + std::string plain = stream.str(); + serializeEncrypted(plain, "transfers_synchronizer", cryptoContext, destination); + cryptoContext.incIv(); +} + +void WalletSerializer::saveSpentOutputs(Common::IOutputStream& destination, CryptoContext& cryptoContext) { + auto& index = m_spentOutputs.get(); + + uint64_t outsCount = index.size(); + serializeEncrypted(outsCount, "spent_outputs_count", cryptoContext, destination); + cryptoContext.incIv(); + + for (const auto& o: index) { + auto it = m_walletsContainer.get().iterator_to(*o.wallet); + uint64_t walletIndex = std::distance(m_walletsContainer.get().begin(), it); + + SpentOutputDto dto; + dto.amount = o.amount; + dto.transactionHash = o.transactionHash; + dto.outputInTransaction = o.outputInTransaction; + dto.walletIndex = walletIndex; + dto.spendingTransactionHash = o.spendingTransactionHash; + + serializeEncrypted(dto, "", cryptoContext, destination); + cryptoContext.incIv(); + } +} + +void WalletSerializer::saveUnlockTransactionsJobs(Common::IOutputStream& destination, CryptoContext& cryptoContext) { + auto& index = m_unlockTransactions.get(); + auto& wallets = m_walletsContainer.get(); + + uint64_t jobsCount = index.size(); + serializeEncrypted(jobsCount, "unlock_transactions_jobs_count", cryptoContext, destination); + cryptoContext.incIv(); + + for (const auto& j: index) { + auto containerIt = wallets.find(j.container); + assert(containerIt != wallets.end()); + + auto rndIt = m_walletsContainer.project(containerIt); + assert(rndIt != m_walletsContainer.get().end()); + + uint64_t walletIndex = std::distance(m_walletsContainer.get().begin(), rndIt); + + UnlockTransactionJobDto dto; + dto.blockHeight = j.blockHeight; + dto.transactionHash = j.transactionHash; + dto.walletIndex = walletIndex; + + serializeEncrypted(dto, "", cryptoContext, destination); + cryptoContext.incIv(); + } +} + +void WalletSerializer::saveChange(Common::IOutputStream& destination, CryptoContext& cryptoContext) { + uint64_t count = m_change.size(); + serializeEncrypted(count, "changes_count", cryptoContext, destination); + cryptoContext.incIv(); + + for (const auto& kv: m_change) { + ChangeDto dto; + dto.txHash = kv.first; + dto.amount = kv.second; + + serializeEncrypted(dto, "", cryptoContext, destination); + cryptoContext.incIv(); + } +} + +void WalletSerializer::saveTransactions(Common::IOutputStream& destination, CryptoContext& cryptoContext) { + uint64_t count = m_transactions.size(); + serializeEncrypted(count, "transactions_count", cryptoContext, destination); + cryptoContext.incIv(); + + for (const auto& tx: m_transactions) { + WalletTransactionDto dto(tx); + serializeEncrypted(dto, "", cryptoContext, destination); + cryptoContext.incIv(); + } +} + +void WalletSerializer::saveTransfers(Common::IOutputStream& destination, CryptoContext& cryptoContext) { + uint64_t count = m_transfers.size(); + serializeEncrypted(count, "transfers_count", cryptoContext, destination); + cryptoContext.incIv(); + + for (const auto& kv: m_transfers) { + uint64_t txId = kv.first; + WalletTransferDto tr(kv.second); + + serializeEncrypted(txId, "transaction_id", cryptoContext, destination); + cryptoContext.incIv(); + + serializeEncrypted(tr, "transfer", cryptoContext, destination); + cryptoContext.incIv(); + } +} + +void WalletSerializer::load(const std::string& password, Common::IInputStream& source) { + CryptoNote::BinaryInputStreamSerializer s(source); + s.beginObject("wallet"); + + uint32_t version = loadVersion(source); + + if (version == SERIALIZATION_VERSION) { + loadCurrentVersion(source, password); + } else if (version == 1) { + loadWalletV1(source, password); + } else { + throw std::system_error(make_error_code(error::WRONG_VERSION)); + } + + s.endObject(); +} + +void WalletSerializer::loadCurrentVersion(Common::IInputStream& source, const std::string& password) { + CryptoNote::CryptoContext cryptoContext; + + bool details = false; + bool cache = false; + + loadIv(source, cryptoContext.iv); + generateKey(password, cryptoContext.key); + + loadKeys(source, cryptoContext); + checkKeys(); + + loadWallets(source, cryptoContext); + subscribeWallets(); + + loadFlags(details, cache, source, cryptoContext); + + if (details) { + loadTransactions(source, cryptoContext); + loadTransfers(source, cryptoContext); + } + + if (cache) { + loadBalances(source, cryptoContext); + loadTransfersSynchronizer(source, cryptoContext); + loadSpentOutputs(source, cryptoContext); + loadUnlockTransactionsJobs(source, cryptoContext); + loadChange(source, cryptoContext); + } +} + +void WalletSerializer::loadWalletV1(Common::IInputStream& source, const std::string& password) { + CryptoNote::CryptoContext cryptoContext; + + CryptoNote::BinaryInputStreamSerializer encrypted(source); + + encrypted(cryptoContext.iv, "iv"); + generateKey(password, cryptoContext.key); + + std::string cipher; + encrypted(cipher, "data"); + + std::string plain = decrypt(cipher, cryptoContext); + + MemoryInputStream decryptedStream(plain.data(), plain.size()); + CryptoNote::BinaryInputStreamSerializer serializer(decryptedStream); + + loadWalletV1Keys(serializer); + checkKeys(); + + subscribeWallets(); + + bool detailsSaved; + serializer(detailsSaved, "has_details"); + + if (detailsSaved) { + loadWalletV1Details(serializer); + } +} + +void WalletSerializer::loadWalletV1Keys(CryptoNote::BinaryInputStreamSerializer& serializer) { + CryptoNote::KeysStorage keys; + keys.serialize(serializer, "keys"); + + m_viewPublicKey = keys.viewPublicKey; + m_viewSecretKey = keys.viewSecretKey; + + WalletRecord wallet; + wallet.spendPublicKey = keys.spendPublicKey; + wallet.spendSecretKey = keys.spendSecretKey; + wallet.actualBalance = 0; + wallet.pendingBalance = 0; + wallet.creationTimestamp = static_cast(keys.creationTimestamp); + + m_walletsContainer.get().push_back(wallet); +} + +void WalletSerializer::loadWalletV1Details(CryptoNote::BinaryInputStreamSerializer& serializer) { + std::vector txs; + std::vector trs; + + serializer(txs, "transactions"); + serializer(trs, "transfers"); + + addWalletV1Details(txs, trs); +} + +uint32_t WalletSerializer::loadVersion(Common::IInputStream& source) { + CryptoNote::BinaryInputStreamSerializer s(source); + + uint32_t version = std::numeric_limits::max(); + s(version, "version"); + + return version; +} + +void WalletSerializer::loadIv(Common::IInputStream& source, Crypto::chacha8_iv& iv) { + CryptoNote::BinaryInputStreamSerializer s(source); + + s.binary(static_cast(&iv.data), sizeof(iv.data), "chacha_iv"); +} + +void WalletSerializer::generateKey(const std::string& password, Crypto::chacha8_key& key) { + Crypto::cn_context context; + Crypto::generate_chacha8_key(context, password, key); +} + +void WalletSerializer::loadKeys(Common::IInputStream& source, CryptoContext& cryptoContext) { + loadPublicKey(source, cryptoContext); + loadSecretKey(source, cryptoContext); +} + +void WalletSerializer::loadPublicKey(Common::IInputStream& source, CryptoContext& cryptoContext) { + deserializeEncrypted(m_viewPublicKey, "public_key", cryptoContext, source); + cryptoContext.incIv(); +} + +void WalletSerializer::loadSecretKey(Common::IInputStream& source, CryptoContext& cryptoContext) { + deserializeEncrypted(m_viewSecretKey, "secret_key", cryptoContext, source); + cryptoContext.incIv(); +} + +void WalletSerializer::checkKeys() { + throwIfKeysMissmatch(m_viewSecretKey, m_viewPublicKey); +} + +void WalletSerializer::loadFlags(bool& details, bool& cache, Common::IInputStream& source, CryptoContext& cryptoContext) { + deserializeEncrypted(details, "details", cryptoContext, source); + cryptoContext.incIv(); + + deserializeEncrypted(cache, "cache", cryptoContext, source); + cryptoContext.incIv(); +} + +void WalletSerializer::loadWallets(Common::IInputStream& source, CryptoContext& cryptoContext) { + auto& index = m_walletsContainer.get(); + + uint64_t count = 0; + deserializeEncrypted(count, "wallets_count", cryptoContext, source); + cryptoContext.incIv(); + + for (uint64_t i = 0; i < count; ++i) { + WalletRecordDto dto; + deserializeEncrypted(dto, "", cryptoContext, source); + cryptoContext.incIv(); + + WalletRecord wallet; + wallet.spendPublicKey = dto.spendPublicKey; + wallet.spendSecretKey = dto.spendSecretKey; + wallet.actualBalance = dto.actualBalance; + wallet.pendingBalance = dto.pendingBalance; + wallet.creationTimestamp = static_cast(dto.creationTimestamp); + wallet.container = reinterpret_cast(i); //dirty hack. container field must be unique + + index.push_back(wallet); + } +} + +void WalletSerializer::subscribeWallets() { + auto& index = m_walletsContainer.get(); + + for (auto it = index.begin(); it != index.end(); ++it) { + const auto& wallet = *it; + + AccountSubscription sub; + sub.keys.address.viewPublicKey = m_viewPublicKey; + sub.keys.address.spendPublicKey = wallet.spendPublicKey; + sub.keys.viewSecretKey = m_viewSecretKey; + sub.keys.spendSecretKey = wallet.spendSecretKey; + sub.transactionSpendableAge = 10; + sub.syncStart.height = 0; + sub.syncStart.timestamp = static_cast(wallet.creationTimestamp) - (60 * 60 * 24); + + auto& subscription = m_synchronizer.addSubscription(sub); + bool r = index.modify(it, [&subscription] (WalletRecord& rec) { rec.container = &subscription.getContainer(); }); + assert(r); + + subscription.addObserver(&m_transfersObserver); + } +} + +void WalletSerializer::loadBalances(Common::IInputStream& source, CryptoContext& cryptoContext) { + deserializeEncrypted(m_actualBalance, "actual_balance", cryptoContext, source); + cryptoContext.incIv(); + + deserializeEncrypted(m_pendingBalance, "pending_balance", cryptoContext, source); + cryptoContext.incIv(); +} + +void WalletSerializer::loadTransfersSynchronizer(Common::IInputStream& source, CryptoContext& cryptoContext) { + std::string deciphered; + deserializeEncrypted(deciphered, "transfers_synchronizer", cryptoContext, source); + cryptoContext.incIv(); + + std::stringstream stream(deciphered); + deciphered.clear(); + + m_synchronizer.load(stream); +} + +void WalletSerializer::loadSpentOutputs(Common::IInputStream& source, CryptoContext& cryptoContext) { + auto& index = m_spentOutputs.get(); + auto& walletsIndex = m_walletsContainer.get(); + const uint64_t walletsSize = walletsIndex.size(); + + uint64_t count = 0; + deserializeEncrypted(count, "spent_outputs_count", cryptoContext, source); + cryptoContext.incIv(); + + for (uint64_t i = 0; i < count; ++i) { + SpentOutputDto dto; + deserializeEncrypted(dto, "", cryptoContext, source); + cryptoContext.incIv(); + + assert(dto.walletIndex < walletsSize); + + SpentOutput output; + output.amount = dto.amount; + output.transactionHash = dto.transactionHash; + output.outputInTransaction = dto.outputInTransaction; + output.spendingTransactionHash = dto.spendingTransactionHash; + output.wallet = &walletsIndex[dto.walletIndex]; + + index.insert(std::move(output)); + } +} + +void WalletSerializer::loadUnlockTransactionsJobs(Common::IInputStream& source, CryptoContext& cryptoContext) { + auto& index = m_unlockTransactions.get(); + auto& walletsIndex = m_walletsContainer.get(); + const uint64_t walletsSize = walletsIndex.size(); + + uint64_t jobsCount = 0; + deserializeEncrypted(jobsCount, "unlock_transactions_jobs_count", cryptoContext, source); + cryptoContext.incIv(); + + for (uint64_t i = 0; i < jobsCount; ++i) { + UnlockTransactionJobDto dto; + deserializeEncrypted(dto, "", cryptoContext, source); + cryptoContext.incIv(); + + assert(dto.walletIndex < walletsSize); + + UnlockTransactionJob job; + job.blockHeight = dto.blockHeight; + job.transactionHash = dto.transactionHash; + job.container = walletsIndex[dto.walletIndex].container; + + index.insert(std::move(job)); + } +} + +void WalletSerializer::loadChange(Common::IInputStream& source, CryptoContext& cryptoContext) { + uint64_t count = 0; + deserializeEncrypted(count, "changes_count", cryptoContext, source); + cryptoContext.incIv(); + + for (uint64_t i = 0; i < count; i++) { + ChangeDto dto; + deserializeEncrypted(dto, "", cryptoContext, source); + cryptoContext.incIv(); + + m_change[dto.txHash] = dto.amount; + } +} + +void WalletSerializer::loadTransactions(Common::IInputStream& source, CryptoContext& cryptoContext) { + uint64_t count = 0; + deserializeEncrypted(count, "transactions_count", cryptoContext, source); + cryptoContext.incIv(); + + m_transactions.get().reserve(count); + + for (uint64_t i = 0; i < count; ++i) { + WalletTransactionDto dto; + deserializeEncrypted(dto, "", cryptoContext, source); + cryptoContext.incIv(); + + WalletTransaction tx; + tx.state = dto.state; + tx.timestamp = dto.timestamp; + tx.blockHeight = dto.blockHeight; + tx.hash = dto.hash; + tx.totalAmount = dto.totalAmount; + tx.fee = dto.fee; + tx.creationTime = dto.creationTime; + tx.unlockTime = dto.unlockTime; + tx.extra = dto.extra; + + m_transactions.get().push_back(std::move(tx)); + } +} + +void WalletSerializer::loadTransfers(Common::IInputStream& source, CryptoContext& cryptoContext) { + uint64_t count = 0; + deserializeEncrypted(count, "transfers_count", cryptoContext, source); + cryptoContext.incIv(); + + m_transfers.reserve(count); + + for (uint64_t i = 0; i < count; ++i) { + uint64_t txId = 0; + deserializeEncrypted(txId, "transaction_id", cryptoContext, source); + cryptoContext.incIv(); + + WalletTransferDto dto; + deserializeEncrypted(dto, "transfer", cryptoContext, source); + cryptoContext.incIv(); + + WalletTransfer tr; + tr.address = dto.address; + tr.amount = dto.amount; + + m_transfers.push_back(std::make_pair(txId, tr)); + } +} + +void WalletSerializer::addWalletV1Details(const std::vector& txs, const std::vector& trs) { + size_t txId = 0; + m_transfers.reserve(trs.size()); + + for (const auto& tx: txs) { + WalletTransaction mtx = convert(tx); + m_transactions.get().push_back(std::move(mtx)); + + if (tx.firstTransferId != WALLET_LEGACY_INVALID_TRANSFER_ID && tx.transferCount != 0) { + size_t firstTr = tx.firstTransferId; + size_t lastTr = firstTr + tx.transferCount; + + if (lastTr > trs.size()) { + throw std::system_error(make_error_code(error::INTERNAL_WALLET_ERROR)); + } + + for (; firstTr < lastTr; firstTr++) { + WalletTransfer tr = convert(trs[firstTr]); + m_transfers.push_back(std::make_pair(txId, tr)); + } + } + + txId++; + } +} + +} //namespace CryptoNote diff --git a/src/Wallet/WalletSerialization.h b/src/Wallet/WalletSerialization.h new file mode 100755 index 00000000..4a518a9c --- /dev/null +++ b/src/Wallet/WalletSerialization.h @@ -0,0 +1,117 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "IWallet.h" +#include "WalletIndices.h" +#include "Common/IInputStream.h" +#include "Common/IOutputStream.h" +#include "Transfers/TransfersSynchronizer.h" +#include "Serialization/BinaryInputStreamSerializer.h" + +#include "crypto/chacha8.h" + +namespace CryptoNote { + +struct CryptoContext { + Crypto::chacha8_key key; + Crypto::chacha8_iv iv; + + void incIv(); +}; + +class WalletSerializer { +public: + WalletSerializer( + ITransfersObserver& transfersObserver, + Crypto::PublicKey& viewPublicKey, + Crypto::SecretKey& viewSecretKey, + uint64_t& actualBalance, + uint64_t& pendingBalance, + WalletsContainer& walletsContainer, + TransfersSyncronizer& synchronizer, + SpentOutputs& spentOutputs, + UnlockTransactionJobs& unlockTransactions, + TransactionChanges& change, + WalletTransactions& transactions, + WalletTransfers& transfers + ); + + void save(const std::string& password, Common::IOutputStream& destination, bool saveDetails, bool saveCache); + void load(const std::string& password, Common::IInputStream& source); + +private: + static const uint32_t SERIALIZATION_VERSION; + + void loadCurrentVersion(Common::IInputStream& source, const std::string& password); + void loadWalletV1(Common::IInputStream& source, const std::string& password); + + CryptoContext generateCryptoContext(const std::string& password); + + void saveVersion(Common::IOutputStream& destination); + void saveIv(Common::IOutputStream& destination, Crypto::chacha8_iv& iv); + void saveKeys(Common::IOutputStream& destination, CryptoContext& cryptoContext); + void savePublicKey(Common::IOutputStream& destination, CryptoContext& cryptoContext); + void saveSecretKey(Common::IOutputStream& destination, CryptoContext& cryptoContext); + void saveFlags(bool saveDetails, bool saveCache, Common::IOutputStream& destination, CryptoContext& cryptoContext); + void saveWallets(Common::IOutputStream& destination, bool saveCache, CryptoContext& cryptoContext); + void saveBalances(Common::IOutputStream& destination, bool saveCache, CryptoContext& cryptoContext); + void saveTransfersSynchronizer(Common::IOutputStream& destination, CryptoContext& cryptoContext); + void saveSpentOutputs(Common::IOutputStream& destination, CryptoContext& cryptoContext); + void saveUnlockTransactionsJobs(Common::IOutputStream& destination, CryptoContext& cryptoContext); + void saveChange(Common::IOutputStream& destination, CryptoContext& cryptoContext); + void saveTransactions(Common::IOutputStream& destination, CryptoContext& cryptoContext); + void saveTransfers(Common::IOutputStream& destination, CryptoContext& cryptoContext); + + uint32_t loadVersion(Common::IInputStream& source); + void loadIv(Common::IInputStream& source, Crypto::chacha8_iv& iv); + void generateKey(const std::string& password, Crypto::chacha8_key& key); + void loadKeys(Common::IInputStream& source, CryptoContext& cryptoContext); + void loadPublicKey(Common::IInputStream& source, CryptoContext& cryptoContext); + void loadSecretKey(Common::IInputStream& source, CryptoContext& cryptoContext); + void checkKeys(); + void loadFlags(bool& details, bool& cache, Common::IInputStream& source, CryptoContext& cryptoContext); + void loadWallets(Common::IInputStream& source, CryptoContext& cryptoContext); + void subscribeWallets(); + void loadBalances(Common::IInputStream& source, CryptoContext& cryptoContext); + void loadTransfersSynchronizer(Common::IInputStream& source, CryptoContext& cryptoContext); + void loadSpentOutputs(Common::IInputStream& source, CryptoContext& cryptoContext); + void loadUnlockTransactionsJobs(Common::IInputStream& source, CryptoContext& cryptoContext); + void loadChange(Common::IInputStream& source, CryptoContext& cryptoContext); + void loadTransactions(Common::IInputStream& source, CryptoContext& cryptoContext); + void loadTransfers(Common::IInputStream& source, CryptoContext& cryptoContext); + + void loadWalletV1Keys(CryptoNote::BinaryInputStreamSerializer& serializer); + void loadWalletV1Details(CryptoNote::BinaryInputStreamSerializer& serializer); + void addWalletV1Details(const std::vector& txs, const std::vector& trs); + + ITransfersObserver& m_transfersObserver; + Crypto::PublicKey& m_viewPublicKey; + Crypto::SecretKey& m_viewSecretKey; + uint64_t& m_actualBalance; + uint64_t& m_pendingBalance; + WalletsContainer& m_walletsContainer; + TransfersSyncronizer& m_synchronizer; + SpentOutputs& m_spentOutputs; + UnlockTransactionJobs& m_unlockTransactions; + TransactionChanges& m_change; + WalletTransactions& m_transactions; + WalletTransfers& m_transfers; +}; + +} //namespace CryptoNote diff --git a/src/wallet/KeysStorage.cpp b/src/WalletLegacy/KeysStorage.cpp old mode 100644 new mode 100755 similarity index 86% rename from src/wallet/KeysStorage.cpp rename to src/WalletLegacy/KeysStorage.cpp index 503a4df1..ce5ca7e2 --- a/src/wallet/KeysStorage.cpp +++ b/src/WalletLegacy/KeysStorage.cpp @@ -17,10 +17,10 @@ #include "KeysStorage.h" -#include "WalletSerialization.h" -#include "serialization/ISerializer.h" -#include "serialization/SerializationOverloads.h" -#include "cryptonote_core/cryptonote_serialization.h" +#include "WalletLegacy/WalletLegacySerialization.h" +#include "Serialization/ISerializer.h" +#include "Serialization/SerializationOverloads.h" +#include "CryptoNoteCore/CryptoNoteSerialization.h" namespace CryptoNote { diff --git a/src/wallet/KeysStorage.h b/src/WalletLegacy/KeysStorage.h similarity index 84% rename from src/wallet/KeysStorage.h rename to src/WalletLegacy/KeysStorage.h index f7275f2b..dd712e32 100644 --- a/src/wallet/KeysStorage.h +++ b/src/WalletLegacy/KeysStorage.h @@ -25,14 +25,15 @@ namespace CryptoNote { class ISerializer; +//This is DTO structure. Do not change it. struct KeysStorage { uint64_t creationTimestamp; - crypto::public_key spendPublicKey; - crypto::secret_key spendSecretKey; + Crypto::PublicKey spendPublicKey; + Crypto::SecretKey spendSecretKey; - crypto::public_key viewPublicKey; - crypto::secret_key viewSecretKey; + Crypto::PublicKey viewPublicKey; + Crypto::SecretKey viewSecretKey; void serialize(ISerializer& serializer, const std::string& name); }; diff --git a/src/wallet/WalletHelper.cpp b/src/WalletLegacy/WalletHelper.cpp similarity index 86% rename from src/wallet/WalletHelper.cpp rename to src/WalletLegacy/WalletHelper.cpp index 86fc7548..3d6bb364 100755 --- a/src/wallet/WalletHelper.cpp +++ b/src/WalletLegacy/WalletHelper.cpp @@ -16,13 +16,11 @@ // along with Bytecoin. If not, see . #include "WalletHelper.h" +#include "Common/PathTools.h" #include #include -#include "Common/PathTools.h" -#include "cryptonote_protocol/blobdatatype.h" - using namespace CryptoNote; namespace { @@ -34,7 +32,7 @@ void openOutputFileStream(const std::string& filename, std::ofstream& file) { } } -std::error_code walletSaveWrapper(CryptoNote::IWallet& wallet, std::ofstream& file, bool saveDetailes, bool saveCache) { +std::error_code walletSaveWrapper(CryptoNote::IWalletLegacy& wallet, std::ofstream& file, bool saveDetailes, bool saveCache) { CryptoNote::WalletHelper::SaveWalletResultObserver o; std::error_code e; @@ -75,11 +73,21 @@ void WalletHelper::SendCompleteResultObserver::sendTransactionCompleted(CryptoNo std::error_code WalletHelper::SendCompleteResultObserver::wait(CryptoNote::TransactionId transactionId) { std::unique_lock lock(m_mutex); - m_condition.wait(lock, [this, &transactionId] { return m_finishedTransactions.find(transactionId) != m_finishedTransactions.end(); }); - return m_finishedTransactions.find(transactionId)->second; + + m_condition.wait(lock, [this, &transactionId] { + auto it = m_finishedTransactions.find(transactionId); + if (it == m_finishedTransactions.end()) { + return false; + } + + m_result = it->second; + return true; + }); + + return m_result; } -WalletHelper::IWalletRemoveObserverGuard::IWalletRemoveObserverGuard(CryptoNote::IWallet& wallet, CryptoNote::IWalletObserver& observer) : +WalletHelper::IWalletRemoveObserverGuard::IWalletRemoveObserverGuard(CryptoNote::IWalletLegacy& wallet, CryptoNote::IWalletLegacyObserver& observer) : m_wallet(wallet), m_observer(observer), m_removed(false) { @@ -97,7 +105,7 @@ void WalletHelper::IWalletRemoveObserverGuard::removeObserver() { m_removed = true; } -void WalletHelper::storeWallet(CryptoNote::IWallet& wallet, const std::string& walletFilename) { +void WalletHelper::storeWallet(CryptoNote::IWalletLegacy& wallet, const std::string& walletFilename) { boost::filesystem::path tempFile = boost::filesystem::unique_path(walletFilename + ".tmp.%%%%-%%%%"); if (boost::filesystem::exists(walletFilename)) { diff --git a/src/wallet/WalletHelper.h b/src/WalletLegacy/WalletHelper.h similarity index 75% rename from src/wallet/WalletHelper.h rename to src/WalletLegacy/WalletHelper.h index 4a4d8705..adcc16db 100755 --- a/src/wallet/WalletHelper.h +++ b/src/WalletLegacy/WalletHelper.h @@ -22,25 +22,24 @@ #include #include "crypto/hash.h" -#include "IWallet.h" - +#include "IWalletLegacy.h" namespace CryptoNote { namespace WalletHelper { -class SaveWalletResultObserver : public CryptoNote::IWalletObserver { +class SaveWalletResultObserver : public CryptoNote::IWalletLegacyObserver { public: std::promise saveResult; virtual void saveCompleted(std::error_code result) override { saveResult.set_value(result); } }; -class InitWalletResultObserver : public CryptoNote::IWalletObserver { +class InitWalletResultObserver : public CryptoNote::IWalletLegacyObserver { public: std::promise initResult; virtual void initCompleted(std::error_code result) override { initResult.set_value(result); } }; -class SendCompleteResultObserver : public CryptoNote::IWalletObserver { +class SendCompleteResultObserver : public CryptoNote::IWalletLegacyObserver { public: virtual void sendTransactionCompleted(CryptoNote::TransactionId transactionId, std::error_code result) override; std::error_code wait(CryptoNote::TransactionId transactionId); @@ -49,21 +48,24 @@ private: std::mutex m_mutex; std::condition_variable m_condition; std::map m_finishedTransactions; + std::error_code m_result; }; class IWalletRemoveObserverGuard { public: - IWalletRemoveObserverGuard(CryptoNote::IWallet& wallet, CryptoNote::IWalletObserver& observer); + IWalletRemoveObserverGuard(CryptoNote::IWalletLegacy& wallet, CryptoNote::IWalletLegacyObserver& observer); ~IWalletRemoveObserverGuard(); void removeObserver(); + private: - CryptoNote::IWallet& m_wallet; - CryptoNote::IWalletObserver& m_observer; + CryptoNote::IWalletLegacy& m_wallet; + CryptoNote::IWalletLegacyObserver& m_observer; bool m_removed; }; void prepareFileNames(const std::string& file_path, std::string& keys_file, std::string& wallet_file); -void storeWallet(CryptoNote::IWallet& wallet, const std::string& walletFilename); +void storeWallet(CryptoNote::IWalletLegacy& wallet, const std::string& walletFilename); -} } +} +} diff --git a/src/wallet/Wallet.cpp b/src/WalletLegacy/WalletLegacy.cpp similarity index 59% rename from src/wallet/Wallet.cpp rename to src/WalletLegacy/WalletLegacy.cpp index 6d047e59..664a8309 100755 --- a/src/wallet/Wallet.cpp +++ b/src/WalletLegacy/WalletLegacy.cpp @@ -15,16 +15,17 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "Wallet.h" +#include "WalletLegacy.h" #include #include -#include "serialization/binary_utils.h" -#include "WalletHelper.h" -#include "WalletSerialization.h" -#include "WalletSerializer.h" -#include "WalletUtils.h" +#include "WalletLegacy/WalletHelper.h" +#include "WalletLegacy/WalletLegacySerialization.h" +#include "WalletLegacy/WalletLegacySerializer.h" +#include "WalletLegacy/WalletUtils.h" + +using namespace Crypto; namespace { @@ -34,12 +35,6 @@ void throwNotDefined() { throw std::runtime_error("The behavior is not defined!"); } -bool verifyKeys(const crypto::secret_key& sec, const crypto::public_key& expected_pub) { - crypto::public_key pub; - bool r = crypto::secret_key_to_public_key(sec, pub); - return r && expected_pub == pub; -} - class ContextCounterHolder { public: @@ -56,7 +51,7 @@ void runAtomic(std::mutex& mutex, F f) { f(); } -class InitWaiter : public CryptoNote::IWalletObserver { +class InitWaiter : public CryptoNote::IWalletLegacyObserver { public: InitWaiter() : future(promise.get_future()) {} @@ -73,7 +68,7 @@ private: }; -class SaveWaiter : public CryptoNote::IWalletObserver { +class SaveWaiter : public CryptoNote::IWalletLegacyObserver { public: SaveWaiter() : future(promise.get_future()) {} @@ -94,7 +89,7 @@ private: namespace CryptoNote { -class SyncStarter : public CryptoNote::IWalletObserver { +class SyncStarter : public CryptoNote::IWalletLegacyObserver { public: SyncStarter(BlockchainSynchronizer& sync) : m_sync(sync) {} virtual ~SyncStarter() {} @@ -108,7 +103,7 @@ public: BlockchainSynchronizer& m_sync; }; -Wallet::Wallet(const CryptoNote::Currency& currency, INode& node) : +WalletLegacy::WalletLegacy(const CryptoNote::Currency& currency, INode& node) : m_state(NOT_INITIALIZED), m_currency(currency), m_node(node), @@ -124,7 +119,7 @@ Wallet::Wallet(const CryptoNote::Currency& currency, INode& node) : addObserver(m_onInitSyncStarter.get()); } -Wallet::~Wallet() { +WalletLegacy::~WalletLegacy() { removeObserver(m_onInitSyncStarter.get()); { @@ -141,15 +136,15 @@ Wallet::~Wallet() { m_sender.release(); } -void Wallet::addObserver(IWalletObserver* observer) { +void WalletLegacy::addObserver(IWalletLegacyObserver* observer) { m_observerManager.add(observer); } -void Wallet::removeObserver(IWalletObserver* observer) { +void WalletLegacy::removeObserver(IWalletLegacyObserver* observer) { m_observerManager.remove(observer); } -void Wallet::initAndGenerate(const std::string& password) { +void WalletLegacy::initAndGenerate(const std::string& password) { { std::unique_lock stateLock(m_cacheMutex); @@ -163,10 +158,10 @@ void Wallet::initAndGenerate(const std::string& password) { initSync(); } - m_observerManager.notify(&IWalletObserver::initCompleted, std::error_code()); + m_observerManager.notify(&IWalletLegacyObserver::initCompleted, std::error_code()); } -void Wallet::initWithKeys(const WalletAccountKeys& accountKeys, const std::string& password) { +void WalletLegacy::initWithKeys(const AccountKeys& accountKeys, const std::string& password) { { std::unique_lock stateLock(m_cacheMutex); @@ -174,35 +169,17 @@ void Wallet::initWithKeys(const WalletAccountKeys& accountKeys, const std::strin throw std::system_error(make_error_code(error::ALREADY_INITIALIZED)); } - account_keys keys; - - std::copy(accountKeys.spendPublicKey.begin(), - accountKeys.spendPublicKey.end(), - reinterpret_cast(&keys.m_account_address.m_spendPublicKey)); - - std::copy(accountKeys.viewPublicKey.begin(), - accountKeys.viewPublicKey.end(), - reinterpret_cast(&keys.m_account_address.m_viewPublicKey)); - - std::copy(accountKeys.spendSecretKey.begin(), - accountKeys.spendSecretKey.end(), - reinterpret_cast(&keys.m_spend_secret_key)); - - std::copy(accountKeys.viewSecretKey.begin(), - accountKeys.viewSecretKey.end(), - reinterpret_cast(&keys.m_view_secret_key)); - - m_account.set_keys(keys); + m_account.setAccountKeys(accountKeys); m_account.set_createtime(ACCOUN_CREATE_TIME_ACCURACY); m_password = password; initSync(); } - m_observerManager.notify(&IWalletObserver::initCompleted, std::error_code()); + m_observerManager.notify(&IWalletLegacyObserver::initCompleted, std::error_code()); } -void Wallet::initAndLoad(std::istream& source, const std::string& password) { +void WalletLegacy::initAndLoad(std::istream& source, const std::string& password) { std::unique_lock stateLock(m_cacheMutex); if (m_state != NOT_INITIALIZED) { @@ -213,13 +190,13 @@ void Wallet::initAndLoad(std::istream& source, const std::string& password) { m_state = LOADING; m_asyncContextCounter.addAsyncContext(); - std::thread loader(&Wallet::doLoad, this, std::ref(source)); + std::thread loader(&WalletLegacy::doLoad, this, std::ref(source)); loader.detach(); } -void Wallet::initSync() { +void WalletLegacy::initSync() { AccountSubscription sub; - sub.keys = reinterpret_cast(m_account.get_keys()); + sub.keys = reinterpret_cast(m_account.getAccountKeys()); sub.transactionSpendableAge = 1; sub.syncStart.height = 0; sub.syncStart.timestamp = m_account.get_createtime() - ACCOUN_CREATE_TIME_ACCURACY; @@ -228,19 +205,19 @@ void Wallet::initSync() { m_transferDetails = &subObject.getContainer(); subObject.addObserver(this); - m_sender.reset(new WalletTransactionSender(m_currency, m_transactionsCache, m_account.get_keys(), *m_transferDetails)); + m_sender.reset(new WalletTransactionSender(m_currency, m_transactionsCache, m_account.getAccountKeys(), *m_transferDetails)); m_state = INITIALIZED; m_blockchainSync.addObserver(this); } -void Wallet::doLoad(std::istream& source) { +void WalletLegacy::doLoad(std::istream& source) { ContextCounterHolder counterHolder(m_asyncContextCounter); try { std::unique_lock lock(m_cacheMutex); std::string cache; - WalletSerializer serializer(m_account, m_transactionsCache); + WalletLegacySerializer serializer(m_account, m_transactionsCache); serializer.deserialize(source, m_password, cache); initSync(); @@ -254,19 +231,19 @@ void Wallet::doLoad(std::istream& source) { // ignore cache loading errors } } catch (std::system_error& e) { - runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::NOT_INITIALIZED;} ); - m_observerManager.notify(&IWalletObserver::initCompleted, e.code()); + runAtomic(m_cacheMutex, [this] () {this->m_state = WalletLegacy::NOT_INITIALIZED;} ); + m_observerManager.notify(&IWalletLegacyObserver::initCompleted, e.code()); return; } catch (std::exception&) { - runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::NOT_INITIALIZED;} ); - m_observerManager.notify(&IWalletObserver::initCompleted, make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR)); + runAtomic(m_cacheMutex, [this] () {this->m_state = WalletLegacy::NOT_INITIALIZED;} ); + m_observerManager.notify(&IWalletLegacyObserver::initCompleted, make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR)); return; } - m_observerManager.notify(&IWalletObserver::initCompleted, std::error_code()); + m_observerManager.notify(&IWalletLegacyObserver::initCompleted, std::error_code()); } -void Wallet::shutdown() { +void WalletLegacy::shutdown() { { std::unique_lock lock(m_cacheMutex); @@ -292,7 +269,7 @@ void Wallet::shutdown() { m_isStopping = false; m_state = NOT_INITIALIZED; - const AccountAddress& accountAddress = reinterpret_cast(m_account.get_keys().m_account_address); + const auto& accountAddress = m_account.getAccountKeys().address; auto subObject = m_transfersSync.getSubscription(accountAddress); assert(subObject != nullptr); subObject->removeObserver(this); @@ -305,19 +282,21 @@ void Wallet::shutdown() { } } -void Wallet::reset() { - InitWaiter initWaiter; - SaveWaiter saveWaiter; - WalletHelper::IWalletRemoveObserverGuard initGuarantee(*this, initWaiter); - WalletHelper::IWalletRemoveObserverGuard saveGuarantee(*this, saveWaiter); - - std::stringstream ss; +void WalletLegacy::reset() { try { - save(ss, false, false); + std::error_code saveError; + std::stringstream ss; + { + SaveWaiter saveWaiter; + WalletHelper::IWalletRemoveObserverGuard saveGuarantee(*this, saveWaiter); + save(ss, false, false); + saveError = saveWaiter.waitSave(); + } - auto saveError = saveWaiter.waitSave(); if (!saveError) { shutdown(); + InitWaiter initWaiter; + WalletHelper::IWalletRemoveObserverGuard initGuarantee(*this, initWaiter); initAndLoad(ss, m_password); initWaiter.waitInit(); } @@ -326,9 +305,9 @@ void Wallet::reset() { } } -void Wallet::save(std::ostream& destination, bool saveDetailed, bool saveCache) { +void WalletLegacy::save(std::ostream& destination, bool saveDetailed, bool saveCache) { if(m_isStopping) { - m_observerManager.notify(&IWalletObserver::saveCompleted, make_error_code(CryptoNote::error::OPERATION_CANCELLED)); + m_observerManager.notify(&IWalletLegacyObserver::saveCompleted, make_error_code(CryptoNote::error::OPERATION_CANCELLED)); return; } @@ -341,18 +320,18 @@ void Wallet::save(std::ostream& destination, bool saveDetailed, bool saveCache) } m_asyncContextCounter.addAsyncContext(); - std::thread saver(&Wallet::doSave, this, std::ref(destination), saveDetailed, saveCache); + std::thread saver(&WalletLegacy::doSave, this, std::ref(destination), saveDetailed, saveCache); saver.detach(); } -void Wallet::doSave(std::ostream& destination, bool saveDetailed, bool saveCache) { +void WalletLegacy::doSave(std::ostream& destination, bool saveDetailed, bool saveCache) { ContextCounterHolder counterHolder(m_asyncContextCounter); try { m_blockchainSync.stop(); std::unique_lock lock(m_cacheMutex); - WalletSerializer serializer(m_account, m_transactionsCache); + WalletLegacySerializer serializer(m_account, m_transactionsCache); std::string cache; if (saveCache) { @@ -367,20 +346,20 @@ void Wallet::doSave(std::ostream& destination, bool saveDetailed, bool saveCache m_blockchainSync.start(); //XXX: start can throw. what to do in this case? } catch (std::system_error& e) { - runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::INITIALIZED;} ); - m_observerManager.notify(&IWalletObserver::saveCompleted, e.code()); + runAtomic(m_cacheMutex, [this] () {this->m_state = WalletLegacy::INITIALIZED;} ); + m_observerManager.notify(&IWalletLegacyObserver::saveCompleted, e.code()); return; } catch (std::exception&) { - runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::INITIALIZED;} ); - m_observerManager.notify(&IWalletObserver::saveCompleted, make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR)); + runAtomic(m_cacheMutex, [this] () {this->m_state = WalletLegacy::INITIALIZED;} ); + m_observerManager.notify(&IWalletLegacyObserver::saveCompleted, make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR)); return; } - m_observerManager.notify(&IWalletObserver::saveCompleted, std::error_code()); + m_observerManager.notify(&IWalletLegacyObserver::saveCompleted, std::error_code()); } -std::error_code Wallet::changePassword(const std::string& oldPassword, const std::string& newPassword) { +std::error_code WalletLegacy::changePassword(const std::string& oldPassword, const std::string& newPassword) { std::unique_lock passLock(m_cacheMutex); throwIfNotInitialised(); @@ -394,14 +373,14 @@ std::error_code Wallet::changePassword(const std::string& oldPassword, const std return std::error_code(); } -std::string Wallet::getAddress() { +std::string WalletLegacy::getAddress() { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); return m_currency.accountAddressAsString(m_account); } -uint64_t Wallet::actualBalance() { +uint64_t WalletLegacy::actualBalance() { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); @@ -409,7 +388,7 @@ uint64_t Wallet::actualBalance() { m_transactionsCache.unconfrimedOutsAmount(); } -uint64_t Wallet::pendingBalance() { +uint64_t WalletLegacy::pendingBalance() { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); @@ -417,53 +396,53 @@ uint64_t Wallet::pendingBalance() { return m_transferDetails->balance(ITransfersContainer::IncludeKeyNotUnlocked) + change; } -size_t Wallet::getTransactionCount() { +size_t WalletLegacy::getTransactionCount() { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); return m_transactionsCache.getTransactionCount(); } -size_t Wallet::getTransferCount() { +size_t WalletLegacy::getTransferCount() { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); return m_transactionsCache.getTransferCount(); } -TransactionId Wallet::findTransactionByTransferId(TransferId transferId) { +TransactionId WalletLegacy::findTransactionByTransferId(TransferId transferId) { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); return m_transactionsCache.findTransactionByTransferId(transferId); } -bool Wallet::getTransaction(TransactionId transactionId, TransactionInfo& transaction) { +bool WalletLegacy::getTransaction(TransactionId transactionId, WalletLegacyTransaction& transaction) { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); return m_transactionsCache.getTransaction(transactionId, transaction); } -bool Wallet::getTransfer(TransferId transferId, Transfer& transfer) { +bool WalletLegacy::getTransfer(TransferId transferId, WalletLegacyTransfer& transfer) { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); return m_transactionsCache.getTransfer(transferId, transfer); } -TransactionId Wallet::sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) { - std::vector transfers; +TransactionId WalletLegacy::sendTransaction(const WalletLegacyTransfer& transfer, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) { + std::vector transfers; transfers.push_back(transfer); throwIfNotInitialised(); return sendTransaction(transfers, fee, extra, mixIn, unlockTimestamp); } -TransactionId Wallet::sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) { +TransactionId WalletLegacy::sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) { TransactionId txId = 0; std::shared_ptr request; - std::deque > events; + std::deque> events; throwIfNotInitialised(); { @@ -475,15 +454,15 @@ TransactionId Wallet::sendTransaction(const std::vector& transfers, ui if (request) { m_asyncContextCounter.addAsyncContext(); - request->perform(m_node, std::bind(&Wallet::sendTransactionCallback, this, std::placeholders::_1, std::placeholders::_2)); + request->perform(m_node, std::bind(&WalletLegacy::sendTransactionCallback, this, std::placeholders::_1, std::placeholders::_2)); } return txId; } -void Wallet::sendTransactionCallback(WalletRequest::Callback callback, std::error_code ec) { +void WalletLegacy::sendTransactionCallback(WalletRequest::Callback callback, std::error_code ec) { ContextCounterHolder counterHolder(m_asyncContextCounter); - std::deque > events; + std::deque > events; boost::optional > nextRequest; { @@ -495,14 +474,14 @@ void Wallet::sendTransactionCallback(WalletRequest::Callback callback, std::erro if (nextRequest) { m_asyncContextCounter.addAsyncContext(); - (*nextRequest)->perform(m_node, std::bind(&Wallet::synchronizationCallback, this, std::placeholders::_1, std::placeholders::_2)); + (*nextRequest)->perform(m_node, std::bind(&WalletLegacy::synchronizationCallback, this, std::placeholders::_1, std::placeholders::_2)); } } -void Wallet::synchronizationCallback(WalletRequest::Callback callback, std::error_code ec) { +void WalletLegacy::synchronizationCallback(WalletRequest::Callback callback, std::error_code ec) { ContextCounterHolder counterHolder(m_asyncContextCounter); - std::deque > events; + std::deque > events; boost::optional > nextRequest; { std::unique_lock lock(m_cacheMutex); @@ -513,25 +492,25 @@ void Wallet::synchronizationCallback(WalletRequest::Callback callback, std::erro if (nextRequest) { m_asyncContextCounter.addAsyncContext(); - (*nextRequest)->perform(m_node, std::bind(&Wallet::synchronizationCallback, this, std::placeholders::_1, std::placeholders::_2)); + (*nextRequest)->perform(m_node, std::bind(&WalletLegacy::synchronizationCallback, this, std::placeholders::_1, std::placeholders::_2)); } } -std::error_code Wallet::cancelTransaction(size_t transactionId) { +std::error_code WalletLegacy::cancelTransaction(size_t transactionId) { return make_error_code(CryptoNote::error::TX_CANCEL_IMPOSSIBLE); } -void Wallet::synchronizationProgressUpdated(uint64_t current, uint64_t total) { +void WalletLegacy::synchronizationProgressUpdated(uint32_t current, uint32_t total) { // forward notification - m_observerManager.notify(&IWalletObserver::synchronizationProgressUpdated, current, total); + m_observerManager.notify(&IWalletLegacyObserver::synchronizationProgressUpdated, current, total); // check if balance has changed and notify client notifyIfBalanceChanged(); } -void Wallet::synchronizationCompleted(std::error_code result) { +void WalletLegacy::synchronizationCompleted(std::error_code result) { if (result != std::make_error_code(std::errc::interrupted)) { - m_observerManager.notify(&IWalletObserver::synchronizationCompleted, result); + m_observerManager.notify(&IWalletLegacyObserver::synchronizationCompleted, result); } if (!result) { @@ -539,8 +518,8 @@ void Wallet::synchronizationCompleted(std::error_code result) { } } -void Wallet::onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) { - std::shared_ptr event; +void WalletLegacy::onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) { + std::shared_ptr event; TransactionInformation txInfo; int64_t txBalance; @@ -554,8 +533,8 @@ void Wallet::onTransactionUpdated(ITransfersSubscription* object, const Hash& tr } } -void Wallet::onTransactionDeleted(ITransfersSubscription* object, const Hash& transactionHash) { - std::shared_ptr event; +void WalletLegacy::onTransactionDeleted(ITransfersSubscription* object, const Hash& transactionHash) { + std::shared_ptr event; { std::unique_lock lock(m_cacheMutex); @@ -567,59 +546,44 @@ void Wallet::onTransactionDeleted(ITransfersSubscription* object, const Hash& tr } } -void Wallet::throwIfNotInitialised() { +void WalletLegacy::throwIfNotInitialised() { if (m_state == NOT_INITIALIZED || m_state == LOADING) { throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); } assert(m_transferDetails); } -void Wallet::notifyClients(std::deque >& events) { +void WalletLegacy::notifyClients(std::deque >& events) { while (!events.empty()) { - std::shared_ptr event = events.front(); + std::shared_ptr event = events.front(); event->notify(m_observerManager); events.pop_front(); } } -void Wallet::notifyIfBalanceChanged() { +void WalletLegacy::notifyIfBalanceChanged() { auto actual = actualBalance(); auto prevActual = m_lastNotifiedActualBalance.exchange(actual); if (prevActual != actual) { - m_observerManager.notify(&IWalletObserver::actualBalanceUpdated, actual); + m_observerManager.notify(&IWalletLegacyObserver::actualBalanceUpdated, actual); } auto pending = pendingBalance(); auto prevPending = m_lastNotifiedPendingBalance.exchange(pending); if (prevPending != pending) { - m_observerManager.notify(&IWalletObserver::pendingBalanceUpdated, pending); + m_observerManager.notify(&IWalletLegacyObserver::pendingBalanceUpdated, pending); } } -void Wallet::getAccountKeys(WalletAccountKeys& keys) { +void WalletLegacy::getAccountKeys(AccountKeys& keys) { if (m_state == NOT_INITIALIZED) { throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); } - const CryptoNote::account_keys& accountKeys = m_account.get_keys(); - std::copy(reinterpret_cast(&accountKeys.m_account_address.m_spendPublicKey), - reinterpret_cast(&accountKeys.m_account_address.m_spendPublicKey) + sizeof(crypto::public_key), - keys.spendPublicKey.begin()); - - std::copy(reinterpret_cast(&accountKeys.m_spend_secret_key), - reinterpret_cast(&accountKeys.m_spend_secret_key) + sizeof(crypto::secret_key), - keys.spendSecretKey.begin()); - - std::copy(reinterpret_cast(&accountKeys.m_account_address.m_viewPublicKey), - reinterpret_cast(&accountKeys.m_account_address.m_viewPublicKey) + sizeof(crypto::public_key), - keys.viewPublicKey.begin()); - - std::copy(reinterpret_cast(&accountKeys.m_view_secret_key), - reinterpret_cast(&accountKeys.m_view_secret_key) + sizeof(crypto::secret_key), - keys.viewSecretKey.begin()); + keys = m_account.getAccountKeys(); } } //namespace CryptoNote diff --git a/src/wallet/Wallet.h b/src/WalletLegacy/WalletLegacy.h old mode 100644 new mode 100755 similarity index 62% rename from src/wallet/Wallet.h rename to src/WalletLegacy/WalletLegacy.h index 60800127..24fbd6b6 --- a/src/wallet/Wallet.h +++ b/src/WalletLegacy/WalletLegacy.h @@ -23,42 +23,42 @@ #include #include -#include "IWallet.h" +#include "IWalletLegacy.h" #include "INode.h" -#include "WalletErrors.h" -#include "WalletAsyncContextCounter.h" +#include "Wallet/WalletErrors.h" +#include "Wallet/WalletAsyncContextCounter.h" #include "Common/ObserverManager.h" -#include "cryptonote_core/tx_extra.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/Currency.h" -#include "WalletUserTransactionsCache.h" -#include "WalletUnconfirmedTransactions.h" +#include "CryptoNoteCore/TransactionExtra.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteCore/Currency.h" +#include "WalletLegacy/WalletUserTransactionsCache.h" +#include "WalletLegacy/WalletUnconfirmedTransactions.h" -#include "WalletTransactionSender.h" -#include "WalletRequest.h" +#include "WalletLegacy/WalletTransactionSender.h" +#include "WalletLegacy/WalletRequest.h" -#include "transfers/BlockchainSynchronizer.h" -#include "transfers/TransfersSynchronizer.h" +#include "Transfers/BlockchainSynchronizer.h" +#include "Transfers/TransfersSynchronizer.h" namespace CryptoNote { class SyncStarter; -class Wallet : - public IWallet, +class WalletLegacy : + public IWalletLegacy, IBlockchainSynchronizerObserver, ITransfersObserver { public: - Wallet(const CryptoNote::Currency& currency, INode& node); - virtual ~Wallet(); + WalletLegacy(const CryptoNote::Currency& currency, INode& node); + virtual ~WalletLegacy(); - virtual void addObserver(IWalletObserver* observer); - virtual void removeObserver(IWalletObserver* observer); + virtual void addObserver(IWalletLegacyObserver* observer); + virtual void removeObserver(IWalletLegacyObserver* observer); virtual void initAndGenerate(const std::string& password); virtual void initAndLoad(std::istream& source, const std::string& password); - virtual void initWithKeys(const WalletAccountKeys& accountKeys, const std::string& password); + virtual void initWithKeys(const AccountKeys& accountKeys, const std::string& password); virtual void shutdown(); virtual void reset(); @@ -76,24 +76,24 @@ public: virtual TransactionId findTransactionByTransferId(TransferId transferId); - virtual bool getTransaction(TransactionId transactionId, TransactionInfo& transaction); - virtual bool getTransfer(TransferId transferId, Transfer& transfer); + virtual bool getTransaction(TransactionId transactionId, WalletLegacyTransaction& transaction); + virtual bool getTransfer(TransferId transferId, WalletLegacyTransfer& transfer); - virtual TransactionId sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0); - virtual TransactionId sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0); + virtual TransactionId sendTransaction(const WalletLegacyTransfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0); + virtual TransactionId sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0); virtual std::error_code cancelTransaction(size_t transactionId); - virtual void getAccountKeys(WalletAccountKeys& keys); + virtual void getAccountKeys(AccountKeys& keys); private: // IBlockchainSynchronizerObserver - virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total) override; + virtual void synchronizationProgressUpdated(uint32_t current, uint32_t total) override; virtual void synchronizationCompleted(std::error_code result) override; // ITransfersObserver - virtual void onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) override; - virtual void onTransactionDeleted(ITransfersSubscription* object, const Hash& transactionHash) override; + virtual void onTransactionUpdated(ITransfersSubscription* object, const Crypto::Hash& transactionHash) override; + virtual void onTransactionDeleted(ITransfersSubscription* object, const Crypto::Hash& transactionHash) override; void initSync(); void throwIfNotInitialised(); @@ -103,7 +103,7 @@ private: void synchronizationCallback(WalletRequest::Callback callback, std::error_code ec); void sendTransactionCallback(WalletRequest::Callback callback, std::error_code ec); - void notifyClients(std::deque >& events); + void notifyClients(std::deque >& events); void notifyIfBalanceChanged(); enum WalletState @@ -116,7 +116,7 @@ private: WalletState m_state; std::mutex m_cacheMutex; - CryptoNote::account_base m_account; + CryptoNote::AccountBase m_account; std::string m_password; const CryptoNote::Currency& m_currency; INode& m_node; @@ -133,7 +133,7 @@ private: std::unique_ptr m_sender; WalletAsyncContextCounter m_asyncContextCounter; - tools::ObserverManager m_observerManager; + Tools::ObserverManager m_observerManager; std::unique_ptr m_onInitSyncStarter; }; diff --git a/src/wallet/WalletEvent.h b/src/WalletLegacy/WalletLegacyEvent.h old mode 100644 new mode 100755 similarity index 50% rename from src/wallet/WalletEvent.h rename to src/WalletLegacy/WalletLegacyEvent.h index faf64e03..b1d97061 --- a/src/wallet/WalletEvent.h +++ b/src/WalletLegacy/WalletLegacyEvent.h @@ -17,44 +17,45 @@ #pragma once -#include "IWallet.h" +#include "IWalletLegacy.h" #include "Common/ObserverManager.h" namespace CryptoNote { -class WalletEvent +class WalletLegacyEvent { public: - virtual ~WalletEvent() {}; + virtual ~WalletLegacyEvent() { + }; - virtual void notify(tools::ObserverManager& observer) = 0; + virtual void notify(Tools::ObserverManager& observer) = 0; }; -class WalletTransactionUpdatedEvent : public WalletEvent +class WalletTransactionUpdatedEvent : public WalletLegacyEvent { public: WalletTransactionUpdatedEvent(TransactionId transactionId) : m_id(transactionId) {}; virtual ~WalletTransactionUpdatedEvent() {}; - virtual void notify(tools::ObserverManager& observer) + virtual void notify(Tools::ObserverManager& observer) { - observer.notify(&IWalletObserver::transactionUpdated, m_id); + observer.notify(&IWalletLegacyObserver::transactionUpdated, m_id); } private: TransactionId m_id; }; -class WalletSendTransactionCompletedEvent : public WalletEvent +class WalletSendTransactionCompletedEvent : public WalletLegacyEvent { public: WalletSendTransactionCompletedEvent(TransactionId transactionId, std::error_code result) : m_id(transactionId), m_error(result) {}; virtual ~WalletSendTransactionCompletedEvent() {}; - virtual void notify(tools::ObserverManager& observer) + virtual void notify(Tools::ObserverManager& observer) { - observer.notify(&IWalletObserver::sendTransactionCompleted, m_id, m_error); + observer.notify(&IWalletLegacyObserver::sendTransactionCompleted, m_id, m_error); } private: @@ -62,72 +63,72 @@ private: std::error_code m_error; }; -class WalletExternalTransactionCreatedEvent : public WalletEvent +class WalletExternalTransactionCreatedEvent : public WalletLegacyEvent { public: WalletExternalTransactionCreatedEvent(TransactionId transactionId) : m_id(transactionId) {}; virtual ~WalletExternalTransactionCreatedEvent() {}; - virtual void notify(tools::ObserverManager& observer) + virtual void notify(Tools::ObserverManager& observer) { - observer.notify(&IWalletObserver::externalTransactionCreated, m_id); + observer.notify(&IWalletLegacyObserver::externalTransactionCreated, m_id); } private: TransactionId m_id; }; -class WalletSynchronizationProgressUpdatedEvent : public WalletEvent +class WalletSynchronizationProgressUpdatedEvent : public WalletLegacyEvent { public: - WalletSynchronizationProgressUpdatedEvent(uint64_t current, uint64_t total) : m_current(current), m_total(total) {}; + WalletSynchronizationProgressUpdatedEvent(uint32_t current, uint32_t total) : m_current(current), m_total(total) {}; virtual ~WalletSynchronizationProgressUpdatedEvent() {}; - virtual void notify(tools::ObserverManager& observer) + virtual void notify(Tools::ObserverManager& observer) { - observer.notify(&IWalletObserver::synchronizationProgressUpdated, m_current, m_total); + observer.notify(&IWalletLegacyObserver::synchronizationProgressUpdated, m_current, m_total); } private: - uint64_t m_current; - uint64_t m_total; + uint32_t m_current; + uint32_t m_total; }; -class WalletSynchronizationCompletedEvent : public WalletEvent { +class WalletSynchronizationCompletedEvent : public WalletLegacyEvent { public: - WalletSynchronizationCompletedEvent(uint64_t current, uint64_t total, std::error_code result) : m_ec(result) {}; + WalletSynchronizationCompletedEvent(uint32_t current, uint32_t total, std::error_code result) : m_ec(result) {}; virtual ~WalletSynchronizationCompletedEvent() {}; - virtual void notify(tools::ObserverManager& observer) { - observer.notify(&IWalletObserver::synchronizationCompleted, m_ec); + virtual void notify(Tools::ObserverManager& observer) { + observer.notify(&IWalletLegacyObserver::synchronizationCompleted, m_ec); } private: std::error_code m_ec; }; -class WalletActualBalanceUpdatedEvent : public WalletEvent +class WalletActualBalanceUpdatedEvent : public WalletLegacyEvent { public: WalletActualBalanceUpdatedEvent(uint64_t balance) : m_balance(balance) {}; virtual ~WalletActualBalanceUpdatedEvent() {}; - virtual void notify(tools::ObserverManager& observer) + virtual void notify(Tools::ObserverManager& observer) { - observer.notify(&IWalletObserver::actualBalanceUpdated, m_balance); + observer.notify(&IWalletLegacyObserver::actualBalanceUpdated, m_balance); } private: uint64_t m_balance; }; -class WalletPendingBalanceUpdatedEvent : public WalletEvent +class WalletPendingBalanceUpdatedEvent : public WalletLegacyEvent { public: WalletPendingBalanceUpdatedEvent(uint64_t balance) : m_balance(balance) {}; virtual ~WalletPendingBalanceUpdatedEvent() {}; - virtual void notify(tools::ObserverManager& observer) + virtual void notify(Tools::ObserverManager& observer) { - observer.notify(&IWalletObserver::pendingBalanceUpdated, m_balance); + observer.notify(&IWalletLegacyObserver::pendingBalanceUpdated, m_balance); } private: uint64_t m_balance; diff --git a/src/wallet/WalletSerialization.cpp b/src/WalletLegacy/WalletLegacySerialization.cpp old mode 100644 new mode 100755 similarity index 67% rename from src/wallet/WalletSerialization.cpp rename to src/WalletLegacy/WalletLegacySerialization.cpp index 6246c2bd..88166a4c --- a/src/wallet/WalletSerialization.cpp +++ b/src/WalletLegacy/WalletLegacySerialization.cpp @@ -15,13 +15,13 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "WalletSerialization.h" -#include "WalletUnconfirmedTransactions.h" -#include "IWallet.h" +#include "WalletLegacySerialization.h" +#include "WalletLegacy/WalletUnconfirmedTransactions.h" +#include "IWalletLegacy.h" -#include "cryptonote_core/cryptonote_serialization.h" -#include "serialization/ISerializer.h" -#include "serialization/SerializationOverloads.h" +#include "CryptoNoteCore/CryptoNoteSerialization.h" +#include "Serialization/ISerializer.h" +#include "Serialization/SerializationOverloads.h" namespace CryptoNote { @@ -37,7 +37,7 @@ void serialize(UnconfirmedTransferDetails& utd, CryptoNote::ISerializer& seriali utd.transactionId = static_cast(txId); } -void serialize(TransactionInfo& txi, CryptoNote::ISerializer& serializer) { +void serialize(WalletLegacyTransaction& txi, CryptoNote::ISerializer& serializer) { uint64_t trId = static_cast(txi.firstTransferId); serializer(trId, "first_transfer_id"); txi.firstTransferId = static_cast(trId); @@ -51,13 +51,31 @@ void serialize(TransactionInfo& txi, CryptoNote::ISerializer& serializer) { serializer(txi.fee, "fee"); serializer(txi.hash, "hash"); serializer(txi.isCoinbase, "is_coinbase"); - serializer(txi.blockHeight, "block_height"); + + if (serializer.type() == ISerializer::INPUT) { + uint64_t height = 0; + serializer(height, "block_height"); + + if (height == std::numeric_limits::max()) { + txi.blockHeight = WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT; + } else { + txi.blockHeight = static_cast(height); + } + } else { + serializer(txi.blockHeight, "block_height"); + } + serializer(txi.timestamp, "timestamp"); serializer(txi.unlockTime, "unlock_time"); serializer(txi.extra, "extra"); + + //this field has been added later in the structure. + //in order to not break backward binary compatibility + // we just set it to zero + txi.sentTime = 0; } -void serialize(Transfer& tr, CryptoNote::ISerializer& serializer) { +void serialize(WalletLegacyTransfer& tr, CryptoNote::ISerializer& serializer) { serializer(tr.address, "address"); serializer(tr.amount, "amount"); } diff --git a/src/wallet/WalletSerialization.h b/src/WalletLegacy/WalletLegacySerialization.h similarity index 81% rename from src/wallet/WalletSerialization.h rename to src/WalletLegacy/WalletLegacySerialization.h index 85a0f240..751f4596 100755 --- a/src/wallet/WalletSerialization.h +++ b/src/WalletLegacy/WalletLegacySerialization.h @@ -21,17 +21,17 @@ #include #include -#include "IWallet.h" +#include "IWalletLegacy.h" namespace CryptoNote { class ISerializer; struct UnconfirmedTransferDetails; -struct TransactionInfo; -struct Transfer; +struct WalletLegacyTransaction; +struct WalletLegacyTransfer; void serialize(UnconfirmedTransferDetails& utd, ISerializer& serializer); -void serialize(TransactionInfo& txi, ISerializer& serializer); -void serialize(Transfer& tr, ISerializer& serializer); +void serialize(WalletLegacyTransaction& txi, ISerializer& serializer); +void serialize(WalletLegacyTransfer& tr, ISerializer& serializer); } diff --git a/src/WalletLegacy/WalletLegacySerializer.cpp b/src/WalletLegacy/WalletLegacySerializer.cpp new file mode 100755 index 00000000..1be62e60 --- /dev/null +++ b/src/WalletLegacy/WalletLegacySerializer.cpp @@ -0,0 +1,186 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "WalletLegacySerializer.h" + +#include + +#include "Common/MemoryInputStream.h" +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "Serialization/BinaryOutputStreamSerializer.h" +#include "Serialization/BinaryInputStreamSerializer.h" +#include "CryptoNoteCore/Account.h" +#include "CryptoNoteCore/CryptoNoteSerialization.h" +#include "WalletLegacy/WalletUserTransactionsCache.h" +#include "Wallet/WalletErrors.h" +#include "WalletLegacy/KeysStorage.h" + +using namespace Common; + +namespace { + +bool verifyKeys(const Crypto::SecretKey& sec, const Crypto::PublicKey& expected_pub) { + Crypto::PublicKey pub; + bool r = Crypto::secret_key_to_public_key(sec, pub); + return r && expected_pub == pub; +} + +void throwIfKeysMissmatch(const Crypto::SecretKey& sec, const Crypto::PublicKey& expected_pub) { + if (!verifyKeys(sec, expected_pub)) + throw std::system_error(make_error_code(CryptoNote::error::WRONG_PASSWORD)); +} + +} + +namespace CryptoNote { + +WalletLegacySerializer::WalletLegacySerializer(CryptoNote::AccountBase& account, WalletUserTransactionsCache& transactionsCache) : + account(account), + transactionsCache(transactionsCache), + walletSerializationVersion(1) +{ +} + +void WalletLegacySerializer::serialize(std::ostream& stream, const std::string& password, bool saveDetailed, const std::string& cache) { + std::stringstream plainArchive; + StdOutputStream plainStream(plainArchive); + CryptoNote::BinaryOutputStreamSerializer serializer(plainStream); + saveKeys(serializer); + + serializer(saveDetailed, "has_details"); + + if (saveDetailed) { + serializer(transactionsCache, "details"); + } + + serializer.binary(const_cast(cache), "cache"); + + std::string plain = plainArchive.str(); + std::string cipher; + + Crypto::chacha8_iv iv = encrypt(plain, password, cipher); + + uint32_t version = walletSerializationVersion; + StdOutputStream output(stream); + CryptoNote::BinaryOutputStreamSerializer s(output); + s.beginObject("wallet"); + s(version, "version"); + s(iv, "iv"); + s(cipher, "data"); + s.endObject(); + + stream.flush(); +} + +void WalletLegacySerializer::saveKeys(CryptoNote::ISerializer& serializer) { + CryptoNote::KeysStorage keys; + CryptoNote::AccountKeys acc = account.getAccountKeys(); + + keys.creationTimestamp = account.get_createtime(); + keys.spendPublicKey = acc.address.spendPublicKey; + keys.spendSecretKey = acc.spendSecretKey; + keys.viewPublicKey = acc.address.viewPublicKey; + keys.viewSecretKey = acc.viewSecretKey; + + keys.serialize(serializer, "keys"); +} + +Crypto::chacha8_iv WalletLegacySerializer::encrypt(const std::string& plain, const std::string& password, std::string& cipher) { + Crypto::chacha8_key key; + Crypto::cn_context context; + Crypto::generate_chacha8_key(context, password, key); + + cipher.resize(plain.size()); + + Crypto::chacha8_iv iv = Crypto::rand(); + Crypto::chacha8(plain.data(), plain.size(), key, iv, &cipher[0]); + + return iv; +} + + +void WalletLegacySerializer::deserialize(std::istream& stream, const std::string& password, std::string& cache) { + StdInputStream stdStream(stream); + CryptoNote::BinaryInputStreamSerializer serializerEncrypted(stdStream); + + serializerEncrypted.beginObject("wallet"); + + uint32_t version; + serializerEncrypted(version, "version"); + + Crypto::chacha8_iv iv; + serializerEncrypted(iv, "iv"); + + std::string cipher; + serializerEncrypted(cipher, "data"); + + serializerEncrypted.endObject(); + + std::string plain; + decrypt(cipher, plain, iv, password); + + MemoryInputStream decryptedStream(plain.data(), plain.size()); + CryptoNote::BinaryInputStreamSerializer serializer(decryptedStream); + + try + { + loadKeys(serializer); + throwIfKeysMissmatch(account.getAccountKeys().viewSecretKey, account.getAccountKeys().address.viewPublicKey); + throwIfKeysMissmatch(account.getAccountKeys().spendSecretKey, account.getAccountKeys().address.spendPublicKey); + } + catch (std::exception&) { + throw std::system_error(make_error_code(CryptoNote::error::WRONG_PASSWORD)); + } + + bool detailsSaved; + + serializer(detailsSaved, "has_details"); + + if (detailsSaved) { + serializer(transactionsCache, "details"); + } + + serializer.binary(cache, "cache"); +} + +void WalletLegacySerializer::decrypt(const std::string& cipher, std::string& plain, Crypto::chacha8_iv iv, const std::string& password) { + Crypto::chacha8_key key; + Crypto::cn_context context; + Crypto::generate_chacha8_key(context, password, key); + + plain.resize(cipher.size()); + + Crypto::chacha8(cipher.data(), cipher.size(), key, iv, &plain[0]); +} + +void WalletLegacySerializer::loadKeys(CryptoNote::ISerializer& serializer) { + CryptoNote::KeysStorage keys; + + keys.serialize(serializer, "keys"); + + CryptoNote::AccountKeys acc; + acc.address.spendPublicKey = keys.spendPublicKey; + acc.spendSecretKey = keys.spendSecretKey; + acc.address.viewPublicKey = keys.viewPublicKey; + acc.viewSecretKey = keys.viewSecretKey; + + account.setAccountKeys(acc); + account.set_createtime(keys.creationTimestamp); +} + +} diff --git a/src/wallet/WalletSerializer.h b/src/WalletLegacy/WalletLegacySerializer.h old mode 100644 new mode 100755 similarity index 82% rename from src/wallet/WalletSerializer.h rename to src/WalletLegacy/WalletLegacySerializer.h index aec3eac9..98e7f9bc --- a/src/wallet/WalletSerializer.h +++ b/src/WalletLegacy/WalletLegacySerializer.h @@ -25,7 +25,7 @@ #include "crypto/chacha8.h" namespace CryptoNote { -class account_base; +class AccountBase; class ISerializer; } @@ -33,9 +33,9 @@ namespace CryptoNote { class WalletUserTransactionsCache; -class WalletSerializer { +class WalletLegacySerializer { public: - WalletSerializer(CryptoNote::account_base& account, WalletUserTransactionsCache& transactionsCache); + WalletLegacySerializer(CryptoNote::AccountBase& account, WalletUserTransactionsCache& transactionsCache); void serialize(std::ostream& stream, const std::string& password, bool saveDetailed, const std::string& cache); void deserialize(std::istream& stream, const std::string& password, std::string& cache); @@ -44,10 +44,10 @@ private: void saveKeys(CryptoNote::ISerializer& serializer); void loadKeys(CryptoNote::ISerializer& serializer); - crypto::chacha8_iv encrypt(const std::string& plain, const std::string& password, std::string& cipher); - void decrypt(const std::string& cipher, std::string& plain, crypto::chacha8_iv iv, const std::string& password); + Crypto::chacha8_iv encrypt(const std::string& plain, const std::string& password, std::string& cipher); + void decrypt(const std::string& cipher, std::string& plain, Crypto::chacha8_iv iv, const std::string& password); - CryptoNote::account_base& account; + CryptoNote::AccountBase& account; WalletUserTransactionsCache& transactionsCache; const uint32_t walletSerializationVersion; }; diff --git a/src/wallet/WalletRequest.h b/src/WalletLegacy/WalletRequest.h similarity index 89% rename from src/wallet/WalletRequest.h rename to src/WalletLegacy/WalletRequest.h index c8726453..b29ee323 100644 --- a/src/wallet/WalletRequest.h +++ b/src/WalletLegacy/WalletRequest.h @@ -19,8 +19,8 @@ #include "INode.h" // #include "WalletSynchronizationContext.h" -#include "WalletSendTransactionContext.h" -#include "WalletEvent.h" +#include "WalletLegacy/WalletSendTransactionContext.h" +#include "WalletLegacy/WalletLegacyEvent.h" #include @@ -33,7 +33,7 @@ namespace CryptoNote { class WalletRequest { public: - typedef std::function >& events, boost::optional >& nextRequest, std::error_code ec)> Callback; + typedef std::function>& events, boost::optional >& nextRequest, std::error_code ec)> Callback; virtual ~WalletRequest() {}; diff --git a/src/wallet/WalletSendTransactionContext.h b/src/WalletLegacy/WalletSendTransactionContext.h old mode 100644 new mode 100755 similarity index 95% rename from src/wallet/WalletSendTransactionContext.h rename to src/WalletLegacy/WalletSendTransactionContext.h index 2fe65d8f..0066c14b --- a/src/wallet/WalletSendTransactionContext.h +++ b/src/WalletLegacy/WalletSendTransactionContext.h @@ -20,8 +20,8 @@ #include #include -#include "cryptonote_core/cryptonote_basic.h" -#include "IWallet.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" +#include "IWalletLegacy.h" #include "ITransfersContainer.h" namespace CryptoNote { diff --git a/src/wallet/WalletTransactionSender.cpp b/src/WalletLegacy/WalletTransactionSender.cpp similarity index 62% rename from src/wallet/WalletTransactionSender.cpp rename to src/WalletLegacy/WalletTransactionSender.cpp index 53eee3b6..72d0c10c 100644 --- a/src/wallet/WalletTransactionSender.cpp +++ b/src/WalletLegacy/WalletTransactionSender.cpp @@ -15,61 +15,60 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "cryptonote_core/account.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "crypto/crypto.h" //for rand() +#include "CryptoNoteCore/Account.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteCore/CryptoNoteTools.h" -#include "WalletTransactionSender.h" -#include "WalletUtils.h" +#include "WalletLegacy/WalletTransactionSender.h" +#include "WalletLegacy/WalletUtils.h" -#include "cryptonote_core/cryptonote_basic_impl.h" +#include "CryptoNoteCore/CryptoNoteBasicImpl.h" #include #include +using namespace Crypto; + namespace { using namespace CryptoNote; -uint64_t countNeededMoney(uint64_t fee, const std::vector& transfers) { +uint64_t countNeededMoney(uint64_t fee, const std::vector& transfers) { uint64_t needed_money = fee; for (auto& transfer: transfers) { - CryptoNote::throwIf(transfer.amount == 0, CryptoNote::error::ZERO_DESTINATION); - CryptoNote::throwIf(transfer.amount < 0, CryptoNote::error::WRONG_AMOUNT); + throwIf(transfer.amount == 0, error::ZERO_DESTINATION); + throwIf(transfer.amount < 0, error::WRONG_AMOUNT); needed_money += transfer.amount; - CryptoNote::throwIf(static_cast(needed_money) < transfer.amount, CryptoNote::error::SUM_OVERFLOW); + throwIf(static_cast(needed_money) < transfer.amount, error::SUM_OVERFLOW); } return needed_money; } -void createChangeDestinations(const CryptoNote::AccountPublicAddress& address, uint64_t neededMoney, uint64_t foundMoney, CryptoNote::tx_destination_entry& changeDts) { +void createChangeDestinations(const AccountPublicAddress& address, uint64_t neededMoney, uint64_t foundMoney, TransactionDestinationEntry& changeDts) { if (neededMoney < foundMoney) { changeDts.addr = address; changeDts.amount = foundMoney - neededMoney; } } -void constructTx(const CryptoNote::account_keys keys, const std::vector& sources, const std::vector& splittedDests, - const std::string& extra, uint64_t unlockTimestamp, uint64_t sizeLimit, CryptoNote::Transaction& tx) { +void constructTx(const AccountKeys keys, const std::vector& sources, const std::vector& splittedDests, + const std::string& extra, uint64_t unlockTimestamp, uint64_t sizeLimit, Transaction& tx) { std::vector extraVec; extraVec.reserve(extra.size()); std::for_each(extra.begin(), extra.end(), [&extraVec] (const char el) { extraVec.push_back(el);}); Logging::LoggerGroup nullLog; - bool r = CryptoNote::construct_tx(keys, sources, splittedDests, extraVec, tx, unlockTimestamp, nullLog); + bool r = constructTransaction(keys, sources, splittedDests, extraVec, tx, unlockTimestamp, nullLog); - CryptoNote::throwIf(!r, CryptoNote::error::INTERNAL_WALLET_ERROR); - CryptoNote::throwIf(CryptoNote::get_object_blobsize(tx) >= sizeLimit, CryptoNote::error::TRANSACTION_SIZE_TOO_BIG); + throwIf(!r, error::INTERNAL_WALLET_ERROR); + throwIf(getObjectBinarySize(tx) >= sizeLimit, error::TRANSACTION_SIZE_TOO_BIG); } -void fillTransactionHash(const CryptoNote::Transaction& tx, CryptoNote::TransactionHash& hash) { - crypto::hash h = CryptoNote::get_transaction_hash(tx); - memcpy(hash.data(), reinterpret_cast(&h), hash.size()); -} - -std::shared_ptr makeCompleteEvent(WalletUserTransactionsCache& transactionCache, size_t transactionId, std::error_code ec) { +std::shared_ptr makeCompleteEvent(WalletUserTransactionsCache& transactionCache, size_t transactionId, std::error_code ec) { transactionCache.updateTransactionSendingState(transactionId, ec); return std::make_shared(transactionId, ec); } @@ -78,7 +77,7 @@ std::shared_ptr makeCompleteEvent(WalletUserTransactionsCache& tran namespace CryptoNote { -WalletTransactionSender::WalletTransactionSender(const CryptoNote::Currency& currency, WalletUserTransactionsCache& transactionsCache, CryptoNote::account_keys keys, ITransfersContainer& transfersContainer) : +WalletTransactionSender::WalletTransactionSender(const Currency& currency, WalletUserTransactionsCache& transactionsCache, AccountKeys keys, ITransfersContainer& transfersContainer) : m_currency(currency), m_transactionsCache(transactionsCache), m_isStoping(false), @@ -91,31 +90,31 @@ void WalletTransactionSender::stop() { } bool WalletTransactionSender::validateDestinationAddress(const std::string& address) { - CryptoNote::AccountPublicAddress ignore; + AccountPublicAddress ignore; return m_currency.parseAccountAddressString(address, ignore); } -void WalletTransactionSender::validateTransfersAddresses(const std::vector& transfers) { - for (const Transfer& tr: transfers) { +void WalletTransactionSender::validateTransfersAddresses(const std::vector& transfers) { + for (const WalletLegacyTransfer& tr : transfers) { if (!validateDestinationAddress(tr.address)) { - throw std::system_error(make_error_code(CryptoNote::error::BAD_ADDRESS)); + throw std::system_error(make_error_code(error::BAD_ADDRESS)); } } } -std::shared_ptr WalletTransactionSender::makeSendRequest(TransactionId& transactionId, std::deque >& events, - const std::vector& transfers, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) { +std::shared_ptr WalletTransactionSender::makeSendRequest(TransactionId& transactionId, std::deque>& events, + const std::vector& transfers, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) { using namespace CryptoNote; - throwIf(transfers.empty(), CryptoNote::error::ZERO_DESTINATION); + throwIf(transfers.empty(), error::ZERO_DESTINATION); validateTransfersAddresses(transfers); uint64_t neededMoney = countNeededMoney(fee, transfers); std::shared_ptr context = std::make_shared(); context->foundMoney = selectTransfersToSend(neededMoney, 0 == mixIn, context->dustPolicy.dustThreshold, context->selectedTransfers); - throwIf(context->foundMoney < neededMoney, CryptoNote::error::WRONG_AMOUNT); + throwIf(context->foundMoney < neededMoney, error::WRONG_AMOUNT); transactionId = m_transactionsCache.addNewTransaction(neededMoney, fee, extra, transfers, unlockTimestamp); context->transactionId = transactionId; @@ -141,11 +140,11 @@ std::shared_ptr WalletTransactionSender::makeGetRandomOutsRequest this, context, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); } -void WalletTransactionSender::sendTransactionRandomOutsByAmount(std::shared_ptr context, std::deque >& events, +void WalletTransactionSender::sendTransactionRandomOutsByAmount(std::shared_ptr context, std::deque>& events, boost::optional >& nextRequest, std::error_code ec) { if (m_isStoping) { - ec = make_error_code(CryptoNote::error::TX_CANCELLED); + ec = make_error_code(error::TX_CANCELLED); } if (ec) { @@ -154,10 +153,10 @@ void WalletTransactionSender::sendTransactionRandomOutsByAmount(std::shared_ptr< } auto scanty_it = std::find_if(context->outs.begin(), context->outs.end(), - [&] (CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& out) {return out.outs.size() < context->mixIn;}); + [&] (COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& out) {return out.outs.size() < context->mixIn;}); if (scanty_it != context->outs.end()) { - events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(CryptoNote::error::MIXIN_COUNT_TOO_BIG))); + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(error::MIXIN_COUNT_TOO_BIG))); return; } @@ -166,31 +165,31 @@ void WalletTransactionSender::sendTransactionRandomOutsByAmount(std::shared_ptr< nextRequest = req; } -std::shared_ptr WalletTransactionSender::doSendTransaction(std::shared_ptr context, std::deque >& events) { +std::shared_ptr WalletTransactionSender::doSendTransaction(std::shared_ptr context, std::deque>& events) { if (m_isStoping) { - events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(CryptoNote::error::TX_CANCELLED))); + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(error::TX_CANCELLED))); return std::shared_ptr(); } try { - TransactionInfo& transaction = m_transactionsCache.getTransaction(context->transactionId); + WalletLegacyTransaction& transaction = m_transactionsCache.getTransaction(context->transactionId); - std::vector sources; + std::vector sources; prepareInputs(context->selectedTransfers, context->outs, sources, context->mixIn); - CryptoNote::tx_destination_entry changeDts; + TransactionDestinationEntry changeDts; changeDts.amount = 0; uint64_t totalAmount = -transaction.totalAmount; - createChangeDestinations(m_keys.m_account_address, totalAmount, context->foundMoney, changeDts); + createChangeDestinations(m_keys.address, totalAmount, context->foundMoney, changeDts); - std::vector splittedDests; + std::vector splittedDests; splitDestinations(transaction.firstTransferId, transaction.transferCount, changeDts, context->dustPolicy, splittedDests); - CryptoNote::Transaction tx; + Transaction tx; constructTx(m_keys, sources, splittedDests, transaction.extra, transaction.unlockTime, m_upperTransactionSizeLimit, tx); - fillTransactionHash(tx, transaction.hash); + getObjectHash(tx, transaction.hash); m_transactionsCache.updateTransaction(context->transactionId, tx, totalAmount, context->selectedTransfers); @@ -203,14 +202,14 @@ std::shared_ptr WalletTransactionSender::doSendTransaction(std::s events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, ec.code())); } catch(std::exception&) { - events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR))); + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(error::INTERNAL_WALLET_ERROR))); } return std::shared_ptr(); } -void WalletTransactionSender::relayTransactionCallback(std::shared_ptr context, std::deque >& events, - boost::optional >& nextRequest, std::error_code ec) { +void WalletTransactionSender::relayTransactionCallback(std::shared_ptr context, std::deque>& events, + boost::optional >& nextRequest, std::error_code ec) { if (m_isStoping) { return; } @@ -219,65 +218,66 @@ void WalletTransactionSender::relayTransactionCallback(std::shared_ptr& splittedDests) { +void WalletTransactionSender::splitDestinations(TransferId firstTransferId, size_t transfersCount, const TransactionDestinationEntry& changeDts, + const TxDustPolicy& dustPolicy, std::vector& splittedDests) { uint64_t dust = 0; digitSplitStrategy(firstTransferId, transfersCount, changeDts, dustPolicy.dustThreshold, splittedDests, dust); - throwIf(dustPolicy.dustThreshold < dust, CryptoNote::error::INTERNAL_WALLET_ERROR); + throwIf(dustPolicy.dustThreshold < dust, error::INTERNAL_WALLET_ERROR); if (0 != dust && !dustPolicy.addToFee) { - splittedDests.push_back(CryptoNote::tx_destination_entry(dust, dustPolicy.addrForDust)); + splittedDests.push_back(TransactionDestinationEntry(dust, dustPolicy.addrForDust)); } } void WalletTransactionSender::digitSplitStrategy(TransferId firstTransferId, size_t transfersCount, - const CryptoNote::tx_destination_entry& change_dst, uint64_t dust_threshold, - std::vector& splitted_dsts, uint64_t& dust) { + const TransactionDestinationEntry& change_dst, uint64_t dust_threshold, + std::vector& splitted_dsts, uint64_t& dust) { splitted_dsts.clear(); dust = 0; for (TransferId idx = firstTransferId; idx < firstTransferId + transfersCount; ++idx) { - Transfer& de = m_transactionsCache.getTransfer(idx); + WalletLegacyTransfer& de = m_transactionsCache.getTransfer(idx); - CryptoNote::AccountPublicAddress addr; + AccountPublicAddress addr; if (!m_currency.parseAccountAddressString(de.address, addr)) { - throw std::system_error(make_error_code(CryptoNote::error::BAD_ADDRESS)); + throw std::system_error(make_error_code(error::BAD_ADDRESS)); } - CryptoNote::decompose_amount_into_digits(de.amount, dust_threshold, - [&](uint64_t chunk) { splitted_dsts.push_back(CryptoNote::tx_destination_entry(chunk, addr)); }, - [&](uint64_t a_dust) { splitted_dsts.push_back(CryptoNote::tx_destination_entry(a_dust, addr)); } ); + decompose_amount_into_digits(de.amount, dust_threshold, + [&](uint64_t chunk) { splitted_dsts.push_back(TransactionDestinationEntry(chunk, addr)); }, + [&](uint64_t a_dust) { splitted_dsts.push_back(TransactionDestinationEntry(a_dust, addr)); }); } - CryptoNote::decompose_amount_into_digits(change_dst.amount, dust_threshold, - [&](uint64_t chunk) { splitted_dsts.push_back(CryptoNote::tx_destination_entry(chunk, change_dst.addr)); }, + decompose_amount_into_digits(change_dst.amount, dust_threshold, + [&](uint64_t chunk) { splitted_dsts.push_back(TransactionDestinationEntry(chunk, change_dst.addr)); }, [&](uint64_t a_dust) { dust = a_dust; } ); } void WalletTransactionSender::prepareInputs( const std::list& selectedTransfers, - std::vector& outs, - std::vector& sources, uint64_t mixIn) { + std::vector& outs, + std::vector& sources, uint64_t mixIn) { size_t i = 0; for (const auto& td: selectedTransfers) { sources.resize(sources.size()+1); - CryptoNote::tx_source_entry& src = sources.back(); + TransactionSourceEntry& src = sources.back(); src.amount = td.amount; //paste mixin transaction if(outs.size()) { - outs[i].outs.sort([](const CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& a, const CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& b){return a.global_amount_index < b.global_amount_index;}); + std::sort(outs[i].outs.begin(), outs[i].outs.end(), + [](const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& a, const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& b){return a.global_amount_index < b.global_amount_index;}); for (auto& daemon_oe: outs[i].outs) { if(td.globalOutputIndex == daemon_oe.global_amount_index) continue; - CryptoNote::tx_source_entry::output_entry oe; - oe.first = daemon_oe.global_amount_index; + TransactionSourceEntry::OutputEntry oe; + oe.first = static_cast(daemon_oe.global_amount_index); oe.second = daemon_oe.out_key; src.outputs.push_back(oe); if(src.outputs.size() >= mixIn) @@ -286,22 +286,22 @@ void WalletTransactionSender::prepareInputs( } //paste real transaction to the random index - auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const CryptoNote::tx_source_entry::output_entry& a) { return a.first >= td.globalOutputIndex; }); + auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const TransactionSourceEntry::OutputEntry& a) { return a.first >= td.globalOutputIndex; }); - CryptoNote::tx_source_entry::output_entry real_oe; + TransactionSourceEntry::OutputEntry real_oe; real_oe.first = td.globalOutputIndex; - real_oe.second = reinterpret_cast(td.outputKey); + real_oe.second = td.outputKey; auto interted_it = src.outputs.insert(it_to_insert, real_oe); - src.real_out_tx_key = reinterpret_cast(td.transactionPublicKey); - src.real_output = interted_it - src.outputs.begin(); - src.real_output_in_tx_index = td.outputInTransaction; + src.realTransactionPublicKey = td.transactionPublicKey; + src.realOutput = interted_it - src.outputs.begin(); + src.realOutputIndexInTransaction = td.outputInTransaction; ++i; } } -void WalletTransactionSender::notifyBalanceChanged(std::deque >& events) { +void WalletTransactionSender::notifyBalanceChanged(std::deque>& events) { uint64_t unconfirmedOutsAmount = m_transactionsCache.unconfrimedOutsAmount(); uint64_t change = unconfirmedOutsAmount - m_transactionsCache.unconfirmedTransactionsAmount(); @@ -355,7 +355,7 @@ uint64_t WalletTransactionSender::selectTransfersToSend(uint64_t neededMoney, bo } } - std::default_random_engine randomGenerator(crypto::rand()); + std::default_random_engine randomGenerator(Crypto::rand()); bool selectOneDust = addDust && !unusedDust.empty(); uint64_t foundMoney = 0; diff --git a/src/wallet/WalletTransactionSender.h b/src/WalletLegacy/WalletTransactionSender.h old mode 100644 new mode 100755 similarity index 56% rename from src/wallet/WalletTransactionSender.h rename to src/WalletLegacy/WalletTransactionSender.h index 4ea01a59..97fdae4f --- a/src/wallet/WalletTransactionSender.h +++ b/src/WalletLegacy/WalletTransactionSender.h @@ -17,14 +17,14 @@ #pragma once -#include "cryptonote_core/account.h" -#include "cryptonote_core/Currency.h" +#include "CryptoNoteCore/Account.h" +#include "CryptoNoteCore/Currency.h" #include "INode.h" -#include "WalletSendTransactionContext.h" -#include "WalletUserTransactionsCache.h" -#include "WalletUnconfirmedTransactions.h" -#include "WalletRequest.h" +#include "WalletLegacy/WalletSendTransactionContext.h" +#include "WalletLegacy/WalletUserTransactionsCache.h" +#include "WalletLegacy/WalletUnconfirmedTransactions.h" +#include "WalletLegacy/WalletRequest.h" #include "ITransfersContainer.h" @@ -33,36 +33,35 @@ namespace CryptoNote { class WalletTransactionSender { public: - WalletTransactionSender(const CryptoNote::Currency& currency, WalletUserTransactionsCache& transactionsCache, CryptoNote::account_keys keys, ITransfersContainer& transfersContainer); + WalletTransactionSender(const Currency& currency, WalletUserTransactionsCache& transactionsCache, AccountKeys keys, ITransfersContainer& transfersContainer); - void init(CryptoNote::account_keys keys, ITransfersContainer& transfersContainer); void stop(); - std::shared_ptr makeSendRequest(TransactionId& transactionId, std::deque >& events, const std::vector& transfers, - uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0); + std::shared_ptr makeSendRequest(TransactionId& transactionId, std::deque>& events, + const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0); private: std::shared_ptr makeGetRandomOutsRequest(std::shared_ptr context); - std::shared_ptr doSendTransaction(std::shared_ptr context, std::deque >& events); - void prepareInputs(const std::list& selectedTransfers, std::vector& outs, - std::vector& sources, uint64_t mixIn); - void splitDestinations(TransferId firstTransferId, size_t transfersCount, const CryptoNote::tx_destination_entry& changeDts, - const TxDustPolicy& dustPolicy, std::vector& splittedDests); - void digitSplitStrategy(TransferId firstTransferId, size_t transfersCount, const CryptoNote::tx_destination_entry& change_dst, uint64_t dust_threshold, - std::vector& splitted_dsts, uint64_t& dust); - void sendTransactionRandomOutsByAmount(std::shared_ptr context, std::deque >& events, + std::shared_ptr doSendTransaction(std::shared_ptr context, std::deque>& events); + void prepareInputs(const std::list& selectedTransfers, std::vector& outs, + std::vector& sources, uint64_t mixIn); + void splitDestinations(TransferId firstTransferId, size_t transfersCount, const TransactionDestinationEntry& changeDts, + const TxDustPolicy& dustPolicy, std::vector& splittedDests); + void digitSplitStrategy(TransferId firstTransferId, size_t transfersCount, const TransactionDestinationEntry& change_dst, uint64_t dust_threshold, + std::vector& splitted_dsts, uint64_t& dust); + void sendTransactionRandomOutsByAmount(std::shared_ptr context, std::deque>& events, boost::optional >& nextRequest, std::error_code ec); - void relayTransactionCallback(std::shared_ptr context, std::deque >& events, + void relayTransactionCallback(std::shared_ptr context, std::deque>& events, boost::optional >& nextRequest, std::error_code ec); - void notifyBalanceChanged(std::deque >& events); + void notifyBalanceChanged(std::deque>& events); - void validateTransfersAddresses(const std::vector& transfers); + void validateTransfersAddresses(const std::vector& transfers); bool validateDestinationAddress(const std::string& address); uint64_t selectTransfersToSend(uint64_t neededMoney, bool addDust, uint64_t dust, std::list& selectedTransfers); - const CryptoNote::Currency& m_currency; - CryptoNote::account_keys m_keys; + const Currency& m_currency; + AccountKeys m_keys; WalletUserTransactionsCache& m_transactionsCache; uint64_t m_upperTransactionSizeLimit; diff --git a/src/wallet/WalletUnconfirmedTransactions.cpp b/src/WalletLegacy/WalletUnconfirmedTransactions.cpp old mode 100644 new mode 100755 similarity index 77% rename from src/wallet/WalletUnconfirmedTransactions.cpp rename to src/WalletLegacy/WalletUnconfirmedTransactions.cpp index da528c7d..ebd9a60a --- a/src/wallet/WalletUnconfirmedTransactions.cpp +++ b/src/WalletLegacy/WalletUnconfirmedTransactions.cpp @@ -16,11 +16,13 @@ // along with Bytecoin. If not, see . #include "WalletUnconfirmedTransactions.h" -#include "WalletSerialization.h" +#include "WalletLegacy/WalletLegacySerialization.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "serialization/ISerializer.h" -#include "serialization/SerializationOverloads.h" +#include "CryptoNoteCore/CryptoNoteTools.h" +#include "Serialization/ISerializer.h" +#include "Serialization/SerializationOverloads.h" + +using namespace Crypto; namespace CryptoNote { @@ -28,15 +30,15 @@ inline TransactionOutputId getOutputId(const TransactionOutputInformation& out) return std::make_pair(out.transactionPublicKey, out.outputInTransaction); } -bool WalletUnconfirmedTransactions::serialize(CryptoNote::ISerializer& s) { +bool WalletUnconfirmedTransactions::serialize(ISerializer& s) { s(m_unconfirmedTxs, "transactions"); - if (s.type() == CryptoNote::ISerializer::INPUT) { + if (s.type() == ISerializer::INPUT) { collectUsedOutputs(); } return true; } -bool WalletUnconfirmedTransactions::findTransactionId(const TransactionHash& hash, TransactionId& id) { +bool WalletUnconfirmedTransactions::findTransactionId(const Hash& hash, TransactionId& id) { auto it = m_unconfirmedTxs.find(hash); if (it == m_unconfirmedTxs.end()) { return false; @@ -46,7 +48,7 @@ bool WalletUnconfirmedTransactions::findTransactionId(const TransactionHash& has return true; } -void WalletUnconfirmedTransactions::erase(const TransactionHash& hash) { +void WalletUnconfirmedTransactions::erase(const Hash& hash) { auto it = m_unconfirmedTxs.find(hash); if (it == m_unconfirmedTxs.end()) { return; @@ -58,13 +60,10 @@ void WalletUnconfirmedTransactions::erase(const TransactionHash& hash) { m_unconfirmedTxs.erase(it); } -void WalletUnconfirmedTransactions::add(const CryptoNote::Transaction& tx, TransactionId transactionId, +void WalletUnconfirmedTransactions::add(const Transaction& tx, TransactionId transactionId, uint64_t amount, const std::list& usedOutputs) { - auto cryptoHash = CryptoNote::get_transaction_hash(tx); - TransactionHash hash = reinterpret_cast(cryptoHash); - - UnconfirmedTransferDetails& utd = m_unconfirmedTxs[hash]; + UnconfirmedTransferDetails& utd = m_unconfirmedTxs[getObjectHash(tx)]; utd.amount = amount; utd.sentTime = time(nullptr); @@ -84,7 +83,7 @@ void WalletUnconfirmedTransactions::add(const CryptoNote::Transaction& tx, Trans utd.outsAmount = outsAmount; } -void WalletUnconfirmedTransactions::updateTransactionId(const TransactionHash& hash, TransactionId id) { +void WalletUnconfirmedTransactions::updateTransactionId(const Hash& hash, TransactionId id) { auto it = m_unconfirmedTxs.find(hash); if (it != m_unconfirmedTxs.end()) { it->second.transactionId = id; diff --git a/src/wallet/WalletUnconfirmedTransactions.h b/src/WalletLegacy/WalletUnconfirmedTransactions.h old mode 100644 new mode 100755 similarity index 67% rename from src/wallet/WalletUnconfirmedTransactions.h rename to src/WalletLegacy/WalletUnconfirmedTransactions.h index 0ccdf1c2..9fbb2929 --- a/src/wallet/WalletUnconfirmedTransactions.h +++ b/src/WalletLegacy/WalletUnconfirmedTransactions.h @@ -17,29 +17,41 @@ #pragma once -#include "IWallet.h" +#include "IWalletLegacy.h" #include "ITransfersContainer.h" #include -#include +#include #include #include -#include "crypto/hash.h" -#include "cryptonote_core/cryptonote_basic.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" +#include "crypto/crypto.h" namespace CryptoNote { class ISerializer; + +typedef std::pair TransactionOutputId; +} + +namespace std { + +template<> +struct hash { + size_t operator()(const CryptoNote::TransactionOutputId &_v) const { + return hash()(_v.first) ^ _v.second; + } +}; + } namespace CryptoNote { -typedef std::pair TransactionOutputId; struct UnconfirmedTransferDetails { UnconfirmedTransferDetails() : - amount(0), sentTime(0), transactionId(INVALID_TRANSACTION_ID) {} + amount(0), sentTime(0), transactionId(WALLET_LEGACY_INVALID_TRANSACTION_ID) {} CryptoNote::Transaction tx; uint64_t amount; @@ -55,11 +67,11 @@ public: bool serialize(CryptoNote::ISerializer& s); - bool findTransactionId(const TransactionHash& hash, TransactionId& id); - void erase(const TransactionHash& hash); + bool findTransactionId(const Crypto::Hash& hash, TransactionId& id); + void erase(const Crypto::Hash& hash); void add(const CryptoNote::Transaction& tx, TransactionId transactionId, uint64_t amount, const std::list& usedOutputs); - void updateTransactionId(const TransactionHash& hash, TransactionId id); + void updateTransactionId(const Crypto::Hash& hash, TransactionId id); uint64_t countUnconfirmedOutsAmount() const; uint64_t countUnconfirmedTransactionsAmount() const; @@ -70,8 +82,8 @@ private: void collectUsedOutputs(); - typedef std::unordered_map> UnconfirmedTxsContainer; - typedef std::set UsedOutputsContainer; + typedef std::unordered_map> UnconfirmedTxsContainer; + typedef std::unordered_set UsedOutputsContainer; UnconfirmedTxsContainer m_unconfirmedTxs; UsedOutputsContainer m_usedOutputs; diff --git a/src/wallet/WalletUserTransactionsCache.cpp b/src/WalletLegacy/WalletUserTransactionsCache.cpp old mode 100644 new mode 100755 similarity index 70% rename from src/wallet/WalletUserTransactionsCache.cpp rename to src/WalletLegacy/WalletUserTransactionsCache.cpp index 27729880..021e02f8 --- a/src/wallet/WalletUserTransactionsCache.cpp +++ b/src/WalletLegacy/WalletUserTransactionsCache.cpp @@ -15,18 +15,19 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "WalletErrors.h" -#include "WalletUserTransactionsCache.h" -#include "WalletSerialization.h" -#include "WalletUtils.h" +#include "IWalletLegacy.h" +#include "Wallet/WalletErrors.h" +#include "WalletLegacy/WalletUserTransactionsCache.h" +#include "WalletLegacy/WalletLegacySerialization.h" +#include "WalletLegacy/WalletUtils.h" -#include "serialization/ISerializer.h" -#include "serialization/SerializationOverloads.h" +#include "Serialization/ISerializer.h" +#include "Serialization/SerializationOverloads.h" #include +using namespace Crypto; + namespace CryptoNote { - - bool WalletUserTransactionsCache::serialize(CryptoNote::ISerializer& s) { if (s.type() == CryptoNote::ISerializer::INPUT) { s(m_transactions, "transactions"); @@ -63,9 +64,9 @@ size_t WalletUserTransactionsCache::getTransferCount() const { } TransactionId WalletUserTransactionsCache::addNewTransaction( - uint64_t amount, uint64_t fee, const std::string& extra, const std::vector& transfers, uint64_t unlockTime) { + uint64_t amount, uint64_t fee, const std::string& extra, const std::vector& transfers, uint64_t unlockTime) { - TransactionInfo transaction; + WalletLegacyTransaction transaction; transaction.firstTransferId = insertTransfers(transfers); transaction.transferCount = transfers.size(); @@ -75,8 +76,8 @@ TransactionId WalletUserTransactionsCache::addNewTransaction( transaction.isCoinbase = false; transaction.timestamp = 0; transaction.extra = extra; - transaction.blockHeight = UNCONFIRMED_TRANSACTION_HEIGHT; - transaction.state = TransactionState::Sending; + transaction.blockHeight = WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT; + transaction.state = WalletLegacyTransactionState::Sending; transaction.unlockTime = unlockTime; return insertTransaction(std::move(transaction)); @@ -93,19 +94,18 @@ void WalletUserTransactionsCache::updateTransaction( void WalletUserTransactionsCache::updateTransactionSendingState(TransactionId transactionId, std::error_code ec) { auto& txInfo = m_transactions.at(transactionId); if (ec) { - txInfo.state = ec.value() == error::TX_CANCELLED ? TransactionState::Cancelled : TransactionState::Failed; + txInfo.state = ec.value() == error::TX_CANCELLED ? WalletLegacyTransactionState::Cancelled : WalletLegacyTransactionState::Failed; m_unconfirmedTransactions.erase(txInfo.hash); } else { txInfo.sentTime = time(nullptr); // update sending time - txInfo.state = TransactionState::Active; + txInfo.state = WalletLegacyTransactionState::Active; } } -std::shared_ptr WalletUserTransactionsCache::onTransactionUpdated(const TransactionInformation& txInfo, - int64_t txBalance) { - std::shared_ptr event; +std::shared_ptr WalletUserTransactionsCache::onTransactionUpdated(const TransactionInformation& txInfo, int64_t txBalance) { + std::shared_ptr event; - TransactionId id = CryptoNote::INVALID_TRANSACTION_ID; + TransactionId id = CryptoNote::WALLET_LEGACY_INVALID_TRANSACTION_ID; if (!m_unconfirmedTransactions.findTransactionId(txInfo.transactionHash, id)) { id = findTransactionByHash(txInfo.transactionHash); @@ -115,9 +115,9 @@ std::shared_ptr WalletUserTransactionsCache::onTransactionUpdated(c bool isCoinbase = txInfo.totalAmountIn == 0; - if (id == CryptoNote::INVALID_TRANSACTION_ID) { - TransactionInfo transaction; - transaction.firstTransferId = INVALID_TRANSFER_ID; + if (id == CryptoNote::WALLET_LEGACY_INVALID_TRANSACTION_ID) { + WalletLegacyTransaction transaction; + transaction.firstTransferId = WALLET_LEGACY_INVALID_TRANSFER_ID; transaction.transferCount = 0; transaction.totalAmount = txBalance; transaction.fee = isCoinbase ? 0 : txInfo.totalAmountIn - txInfo.totalAmountOut; @@ -127,17 +127,17 @@ std::shared_ptr WalletUserTransactionsCache::onTransactionUpdated(c transaction.isCoinbase = isCoinbase; transaction.timestamp = txInfo.timestamp; transaction.extra.assign(txInfo.extra.begin(), txInfo.extra.end()); - transaction.state = TransactionState::Active; + transaction.state = WalletLegacyTransactionState::Active; transaction.unlockTime = txInfo.unlockTime; id = insertTransaction(std::move(transaction)); // notification event event = std::make_shared(id); } else { - TransactionInfo& tr = getTransaction(id); + WalletLegacyTransaction& tr = getTransaction(id); tr.blockHeight = txInfo.blockHeight; tr.timestamp = txInfo.timestamp; - tr.state = TransactionState::Active; + tr.state = WalletLegacyTransactionState::Active; // notification event event = std::make_shared(id); } @@ -145,8 +145,8 @@ std::shared_ptr WalletUserTransactionsCache::onTransactionUpdated(c return event; } -std::shared_ptr WalletUserTransactionsCache::onTransactionDeleted(const TransactionHash& transactionHash) { - TransactionId id = CryptoNote::INVALID_TRANSACTION_ID; +std::shared_ptr WalletUserTransactionsCache::onTransactionDeleted(const Hash& transactionHash) { + TransactionId id = CryptoNote::WALLET_LEGACY_INVALID_TRANSACTION_ID; if (m_unconfirmedTransactions.findTransactionId(transactionHash, id)) { m_unconfirmedTransactions.erase(transactionHash); // LOG_ERROR("Unconfirmed transaction is deleted: id = " << id << ", hash = " << transactionHash); @@ -155,12 +155,12 @@ std::shared_ptr WalletUserTransactionsCache::onTransactionDeleted(c id = findTransactionByHash(transactionHash); } - std::shared_ptr event; - if (id != CryptoNote::INVALID_TRANSACTION_ID) { - TransactionInfo& tr = getTransaction(id); - tr.blockHeight = UNCONFIRMED_TRANSACTION_HEIGHT; + std::shared_ptr event; + if (id != CryptoNote::WALLET_LEGACY_INVALID_TRANSACTION_ID) { + WalletLegacyTransaction& tr = getTransaction(id); + tr.blockHeight = WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT; tr.timestamp = 0; - tr.state = TransactionState::Deleted; + tr.state = WalletLegacyTransactionState::Deleted; event = std::make_shared(id); } else { @@ -175,9 +175,9 @@ TransactionId WalletUserTransactionsCache::findTransactionByTransferId(TransferI { TransactionId id; for (id = 0; id < m_transactions.size(); ++id) { - const TransactionInfo& tx = m_transactions[id]; + const WalletLegacyTransaction& tx = m_transactions[id]; - if (tx.firstTransferId == INVALID_TRANSFER_ID || tx.transferCount == 0) + if (tx.firstTransferId == WALLET_LEGACY_INVALID_TRANSFER_ID || tx.transferCount == 0) continue; if (transferId >= tx.firstTransferId && transferId < (tx.firstTransferId + tx.transferCount)) @@ -185,12 +185,12 @@ TransactionId WalletUserTransactionsCache::findTransactionByTransferId(TransferI } if (id == m_transactions.size()) - return INVALID_TRANSACTION_ID; + return WALLET_LEGACY_INVALID_TRANSACTION_ID; return id; } -bool WalletUserTransactionsCache::getTransaction(TransactionId transactionId, TransactionInfo& transaction) const +bool WalletUserTransactionsCache::getTransaction(TransactionId transactionId, WalletLegacyTransaction& transaction) const { if (transactionId >= m_transactions.size()) return false; @@ -200,7 +200,7 @@ bool WalletUserTransactionsCache::getTransaction(TransactionId transactionId, Tr return true; } -bool WalletUserTransactionsCache::getTransfer(TransferId transferId, Transfer& transfer) const +bool WalletUserTransactionsCache::getTransfer(TransferId transferId, WalletLegacyTransfer& transfer) const { if (transferId >= m_transfers.size()) return false; @@ -210,16 +210,16 @@ bool WalletUserTransactionsCache::getTransfer(TransferId transferId, Transfer& t return true; } -TransactionId WalletUserTransactionsCache::insertTransaction(TransactionInfo&& Transaction) { +TransactionId WalletUserTransactionsCache::insertTransaction(WalletLegacyTransaction&& Transaction) { m_transactions.emplace_back(std::move(Transaction)); return m_transactions.size() - 1; } -TransactionId WalletUserTransactionsCache::findTransactionByHash(const TransactionHash& hash) { - auto it = std::find_if(m_transactions.begin(), m_transactions.end(), [&hash] (const TransactionInfo& tx) { return tx.hash == hash; }); +TransactionId WalletUserTransactionsCache::findTransactionByHash(const Hash& hash) { + auto it = std::find_if(m_transactions.begin(), m_transactions.end(), [&hash](const WalletLegacyTransaction& tx) { return tx.hash == hash; }); if (it == m_transactions.end()) - return CryptoNote::INVALID_TRANSACTION_ID; + return CryptoNote::WALLET_LEGACY_INVALID_TRANSACTION_ID; return std::distance(m_transactions.begin(), it); } @@ -228,7 +228,7 @@ bool WalletUserTransactionsCache::isUsed(const TransactionOutputInformation& out return m_unconfirmedTransactions.isUsed(out); } -TransactionInfo& WalletUserTransactionsCache::getTransaction(TransactionId transactionId) { +WalletLegacyTransaction& WalletUserTransactionsCache::getTransaction(TransactionId transactionId) { return m_transactions.at(transactionId); } @@ -237,23 +237,23 @@ void WalletUserTransactionsCache::getGoodItems(UserTransactions& transactions, U for (size_t txId = 0; txId < m_transactions.size(); ++txId) { bool isGood = - m_transactions[txId].state != TransactionState::Cancelled && - m_transactions[txId].state != TransactionState::Failed; + m_transactions[txId].state != WalletLegacyTransactionState::Cancelled && + m_transactions[txId].state != WalletLegacyTransactionState::Failed; if (isGood) { getGoodTransaction(txId, offset, transactions, transfers); } else { - const TransactionInfo& t = m_transactions[txId]; - offset += t.firstTransferId != INVALID_TRANSFER_ID ? t.transferCount : 0; + const WalletLegacyTransaction& t = m_transactions[txId]; + offset += t.firstTransferId != WALLET_LEGACY_INVALID_TRANSFER_ID ? t.transferCount : 0; } } } void WalletUserTransactionsCache::getGoodTransaction(TransactionId txId, size_t offset, UserTransactions& transactions, UserTransfers& transfers) { transactions.push_back(m_transactions[txId]); - TransactionInfo& tx = transactions.back(); + WalletLegacyTransaction& tx = transactions.back(); - if (tx.firstTransferId == INVALID_TRANSFER_ID) { + if (tx.firstTransferId == WALLET_LEGACY_INVALID_TRANSFER_ID) { return; } @@ -266,29 +266,29 @@ void WalletUserTransactionsCache::getGoodTransaction(TransactionId txId, size_t } void WalletUserTransactionsCache::getTransfersByTx(TransactionId id, UserTransfers& transfers) { - const TransactionInfo& tx = m_transactions[id]; + const WalletLegacyTransaction& tx = m_transactions[id]; - if (tx.firstTransferId != INVALID_TRANSFER_ID) { + if (tx.firstTransferId != WALLET_LEGACY_INVALID_TRANSFER_ID) { UserTransfers::const_iterator first = m_transfers.begin() + tx.firstTransferId; UserTransfers::const_iterator last = first + tx.transferCount; std::copy(first, last, std::back_inserter(transfers)); } } -TransferId WalletUserTransactionsCache::insertTransfers(const std::vector& transfers) { +TransferId WalletUserTransactionsCache::insertTransfers(const std::vector& transfers) { std::copy(transfers.begin(), transfers.end(), std::back_inserter(m_transfers)); return m_transfers.size() - transfers.size(); } void WalletUserTransactionsCache::updateUnconfirmedTransactions() { for (TransactionId id = 0; id < m_transactions.size(); ++id) { - if (m_transactions[id].blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { + if (m_transactions[id].blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { m_unconfirmedTransactions.updateTransactionId(m_transactions[id].hash, id); } } } -Transfer& WalletUserTransactionsCache::getTransfer(TransferId transferId) { +WalletLegacyTransfer& WalletUserTransactionsCache::getTransfer(TransferId transferId) { return m_transfers.at(transferId); } diff --git a/src/wallet/WalletUserTransactionsCache.h b/src/WalletLegacy/WalletUserTransactionsCache.h old mode 100644 new mode 100755 similarity index 66% rename from src/wallet/WalletUserTransactionsCache.h rename to src/WalletLegacy/WalletUserTransactionsCache.h index 10faeca8..89754b7b --- a/src/wallet/WalletUserTransactionsCache.h +++ b/src/WalletLegacy/WalletUserTransactionsCache.h @@ -18,11 +18,11 @@ #pragma once #include "crypto/hash.h" -#include "IWallet.h" +#include "IWalletLegacy.h" #include "ITransfersContainer.h" -#include "WalletEvent.h" -#include "WalletUnconfirmedTransactions.h" +#include "WalletLegacy/WalletLegacyEvent.h" +#include "WalletLegacy/WalletUnconfirmedTransactions.h" namespace CryptoNote { class ISerializer; @@ -42,32 +42,32 @@ public: size_t getTransactionCount() const; size_t getTransferCount() const; - TransactionId addNewTransaction(uint64_t amount, uint64_t fee, const std::string& extra, const std::vector& transfers, uint64_t unlockTime); + TransactionId addNewTransaction(uint64_t amount, uint64_t fee, const std::string& extra, const std::vector& transfers, uint64_t unlockTime); void updateTransaction(TransactionId transactionId, const CryptoNote::Transaction& tx, uint64_t amount, const std::list& usedOutputs); void updateTransactionSendingState(TransactionId transactionId, std::error_code ec); - std::shared_ptr onTransactionUpdated(const TransactionInformation& txInfo, int64_t txBalance); - std::shared_ptr onTransactionDeleted(const TransactionHash& transactionHash); + std::shared_ptr onTransactionUpdated(const TransactionInformation& txInfo, int64_t txBalance); + std::shared_ptr onTransactionDeleted(const Crypto::Hash& transactionHash); TransactionId findTransactionByTransferId(TransferId transferId) const; - bool getTransaction(TransactionId transactionId, TransactionInfo& transaction) const; - TransactionInfo& getTransaction(TransactionId transactionId); - bool getTransfer(TransferId transferId, Transfer& transfer) const; - Transfer& getTransfer(TransferId transferId); + bool getTransaction(TransactionId transactionId, WalletLegacyTransaction& transaction) const; + WalletLegacyTransaction& getTransaction(TransactionId transactionId); + bool getTransfer(TransferId transferId, WalletLegacyTransfer& transfer) const; + WalletLegacyTransfer& getTransfer(TransferId transferId); bool isUsed(const TransactionOutputInformation& out) const; void reset(); private: - TransactionId findTransactionByHash(const TransactionHash& hash); - TransactionId insertTransaction(TransactionInfo&& Transaction); - TransferId insertTransfers(const std::vector& transfers); + TransactionId findTransactionByHash(const Crypto::Hash& hash); + TransactionId insertTransaction(WalletLegacyTransaction&& Transaction); + TransferId insertTransfers(const std::vector& transfers); void updateUnconfirmedTransactions(); - typedef std::vector UserTransfers; - typedef std::vector UserTransactions; + typedef std::vector UserTransfers; + typedef std::vector UserTransactions; void getGoodItems(UserTransactions& transactions, UserTransfers& transfers); void getGoodTransaction(TransactionId txId, size_t offset, UserTransactions& transactions, UserTransfers& transfers); diff --git a/src/wallet/WalletUtils.h b/src/WalletLegacy/WalletUtils.h old mode 100644 new mode 100755 similarity index 88% rename from src/wallet/WalletUtils.h rename to src/WalletLegacy/WalletUtils.h index 65c9dfb4..abdb04c9 --- a/src/wallet/WalletUtils.h +++ b/src/WalletLegacy/WalletUtils.h @@ -21,8 +21,8 @@ #include #include -#include "IWallet.h" -#include "WalletErrors.h" +#include "IWalletLegacy.h" +#include "Wallet/WalletErrors.h" namespace CryptoNote { @@ -32,11 +32,11 @@ inline void throwIf(bool expr, CryptoNote::error::WalletErrorCodes ec) throw std::system_error(make_error_code(ec)); } -inline std::ostream& operator <<(std::ostream& ostr, const TransactionHash& hash) { +inline std::ostream& operator <<(std::ostream& ostr, const Crypto::Hash& hash) { std::ios_base::fmtflags flags = ostr.setf(std::ios_base::hex, std::ios_base::basefield); char fill = ostr.fill('0'); - for (auto b : hash) { + for (auto b : hash.data) { ostr << std::setw(2) << static_cast(b); } diff --git a/src/crypto/chacha8.c b/src/crypto/chacha8.c old mode 100644 new mode 100755 diff --git a/src/crypto/chacha8.h b/src/crypto/chacha8.h index 04ec5d7b..fca5df70 100644 --- a/src/crypto/chacha8.h +++ b/src/crypto/chacha8.h @@ -8,10 +8,11 @@ #if defined(__cplusplus) #include +#include #include "hash.h" -namespace crypto { +namespace Crypto { extern "C" { #endif void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher); @@ -36,14 +37,14 @@ namespace crypto { static_assert(sizeof(chacha8_key) == CHACHA8_KEY_SIZE && sizeof(chacha8_iv) == CHACHA8_IV_SIZE, "Invalid structure size"); - inline void chacha8(const void* data, std::size_t length, const chacha8_key& key, const chacha8_iv& iv, char* cipher) { + inline void chacha8(const void* data, size_t length, const chacha8_key& key, const chacha8_iv& iv, char* cipher) { chacha8(data, length, reinterpret_cast(&key), reinterpret_cast(&iv), cipher); } - inline void generate_chacha8_key(crypto::cn_context &context, std::string password, chacha8_key& key) { - static_assert(sizeof(chacha8_key) <= sizeof(hash), "Size of hash must be at least that of chacha8_key"); - crypto::hash pwd_hash; - crypto::cn_slow_hash(context, password.data(), password.size(), pwd_hash); + inline void generate_chacha8_key(Crypto::cn_context &context, const std::string& password, chacha8_key& key) { + static_assert(sizeof(chacha8_key) <= sizeof(Hash), "Size of hash must be at least that of chacha8_key"); + Hash pwd_hash; + cn_slow_hash(context, password.data(), password.size(), pwd_hash); memcpy(&key, &pwd_hash, sizeof(key)); memset(&pwd_hash, 0, sizeof(pwd_hash)); } diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp old mode 100644 new mode 100755 index efa724e1..7b2103c8 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -24,17 +24,16 @@ #include #include -#include "Common/varint.h" +#include "Common/Varint.h" #include "crypto.h" #include "hash.h" -namespace crypto { +namespace Crypto { using std::abort; using std::int32_t; using std::lock_guard; using std::mutex; - using std::size_t; extern "C" { #include "crypto-ops.h" @@ -43,200 +42,286 @@ namespace crypto { mutex random_lock; - static inline unsigned char *operator &(ec_point &point) { - return &reinterpret_cast(point); - } - - static inline const unsigned char *operator &(const ec_point &point) { - return &reinterpret_cast(point); - } - - static inline unsigned char *operator &(ec_scalar &scalar) { - return &reinterpret_cast(scalar); - } - - static inline const unsigned char *operator &(const ec_scalar &scalar) { - return &reinterpret_cast(scalar); - } - - static inline void random_scalar(ec_scalar &res) { + static inline void random_scalar(EllipticCurveScalar &res) { unsigned char tmp[64]; generate_random_bytes(64, tmp); sc_reduce(tmp); memcpy(&res, tmp, 32); } - static inline void hash_to_scalar(const void *data, size_t length, ec_scalar &res) { - cn_fast_hash(data, length, reinterpret_cast(res)); - sc_reduce32(&res); + static inline void hash_to_scalar(const void *data, size_t length, EllipticCurveScalar &res) { + cn_fast_hash(data, length, reinterpret_cast(res)); + sc_reduce32(reinterpret_cast(&res)); } - void crypto_ops::generate_keys(public_key &pub, secret_key &sec) { + void crypto_ops::generate_keys(PublicKey &pub, SecretKey &sec) { lock_guard lock(random_lock); ge_p3 point; - random_scalar(sec); - ge_scalarmult_base(&point, &sec); - ge_p3_tobytes(&pub, &point); + random_scalar(reinterpret_cast(sec)); + ge_scalarmult_base(&point, reinterpret_cast(&sec)); + ge_p3_tobytes(reinterpret_cast(&pub), &point); } - bool crypto_ops::check_key(const public_key &key) { + bool crypto_ops::check_key(const PublicKey &key) { ge_p3 point; - return ge_frombytes_vartime(&point, &key) == 0; + return ge_frombytes_vartime(&point, reinterpret_cast(&key)) == 0; } - bool crypto_ops::secret_key_to_public_key(const secret_key &sec, public_key &pub) { + bool crypto_ops::secret_key_to_public_key(const SecretKey &sec, PublicKey &pub) { ge_p3 point; - if (sc_check(&sec) != 0) { + if (sc_check(reinterpret_cast(&sec)) != 0) { return false; } - ge_scalarmult_base(&point, &sec); - ge_p3_tobytes(&pub, &point); + ge_scalarmult_base(&point, reinterpret_cast(&sec)); + ge_p3_tobytes(reinterpret_cast(&pub), &point); return true; } - bool crypto_ops::generate_key_derivation(const public_key &key1, const secret_key &key2, key_derivation &derivation) { + bool crypto_ops::generate_key_derivation(const PublicKey &key1, const SecretKey &key2, KeyDerivation &derivation) { ge_p3 point; ge_p2 point2; ge_p1p1 point3; - assert(sc_check(&key2) == 0); - if (ge_frombytes_vartime(&point, &key1) != 0) { + assert(sc_check(reinterpret_cast(&key2)) == 0); + if (ge_frombytes_vartime(&point, reinterpret_cast(&key1)) != 0) { return false; } - ge_scalarmult(&point2, &key2, &point); + ge_scalarmult(&point2, reinterpret_cast(&key2), &point); ge_mul8(&point3, &point2); ge_p1p1_to_p2(&point2, &point3); - ge_tobytes(&derivation, &point2); + ge_tobytes(reinterpret_cast(&derivation), &point2); return true; } - static void derivation_to_scalar(const key_derivation &derivation, size_t output_index, ec_scalar &res) { + static void derivation_to_scalar(const KeyDerivation &derivation, size_t output_index, EllipticCurveScalar &res) { struct { - key_derivation derivation; + KeyDerivation derivation; char output_index[(sizeof(size_t) * 8 + 6) / 7]; } buf; char *end = buf.output_index; buf.derivation = derivation; - tools::write_varint(end, output_index); + Tools::write_varint(end, output_index); assert(end <= buf.output_index + sizeof buf.output_index); hash_to_scalar(&buf, end - reinterpret_cast(&buf), res); } - bool crypto_ops::derive_public_key(const key_derivation &derivation, size_t output_index, - const public_key &base, public_key &derived_key) { - ec_scalar scalar; + static void derivation_to_scalar(const KeyDerivation &derivation, size_t output_index, const uint8_t* suffix, size_t suffixLength, EllipticCurveScalar &res) { + assert(suffixLength <= 32); + struct { + KeyDerivation derivation; + char output_index[(sizeof(size_t) * 8 + 6) / 7 + 32]; + } buf; + char *end = buf.output_index; + buf.derivation = derivation; + Tools::write_varint(end, output_index); + assert(end <= buf.output_index + sizeof buf.output_index); + size_t bufSize = end - reinterpret_cast(&buf); + memcpy(end, suffix, suffixLength); + hash_to_scalar(&buf, bufSize + suffixLength, res); + } + + bool crypto_ops::derive_public_key(const KeyDerivation &derivation, size_t output_index, + const PublicKey &base, PublicKey &derived_key) { + EllipticCurveScalar scalar; ge_p3 point1; ge_p3 point2; ge_cached point3; ge_p1p1 point4; ge_p2 point5; - if (ge_frombytes_vartime(&point1, &base) != 0) { + if (ge_frombytes_vartime(&point1, reinterpret_cast(&base)) != 0) { return false; } derivation_to_scalar(derivation, output_index, scalar); - ge_scalarmult_base(&point2, &scalar); + ge_scalarmult_base(&point2, reinterpret_cast(&scalar)); ge_p3_to_cached(&point3, &point2); ge_add(&point4, &point1, &point3); ge_p1p1_to_p2(&point5, &point4); - ge_tobytes(&derived_key, &point5); + ge_tobytes(reinterpret_cast(&derived_key), &point5); return true; } - void crypto_ops::derive_secret_key(const key_derivation &derivation, size_t output_index, - const secret_key &base, secret_key &derived_key) { - ec_scalar scalar; - assert(sc_check(&base) == 0); - derivation_to_scalar(derivation, output_index, scalar); - sc_add(&derived_key, &base, &scalar); - } - - bool crypto_ops::underive_public_key(const key_derivation &derivation, size_t output_index, - const public_key &derived_key, public_key &base) { - ec_scalar scalar; + bool crypto_ops::derive_public_key(const KeyDerivation &derivation, size_t output_index, + const PublicKey &base, const uint8_t* suffix, size_t suffixLength, PublicKey &derived_key) { + EllipticCurveScalar scalar; ge_p3 point1; ge_p3 point2; ge_cached point3; ge_p1p1 point4; ge_p2 point5; - if (ge_frombytes_vartime(&point1, &derived_key) != 0) { + if (ge_frombytes_vartime(&point1, reinterpret_cast(&base)) != 0) { return false; } - derivation_to_scalar(derivation, output_index, scalar); - ge_scalarmult_base(&point2, &scalar); + derivation_to_scalar(derivation, output_index, suffix, suffixLength, scalar); + ge_scalarmult_base(&point2, reinterpret_cast(&scalar)); ge_p3_to_cached(&point3, &point2); - ge_sub(&point4, &point1, &point3); + ge_add(&point4, &point1, &point3); ge_p1p1_to_p2(&point5, &point4); - ge_tobytes(&base, &point5); + ge_tobytes(reinterpret_cast(&derived_key), &point5); return true; } + bool crypto_ops::underive_public_key_and_get_scalar(const KeyDerivation &derivation, size_t output_index, + const PublicKey &derived_key, PublicKey &base, EllipticCurveScalar &hashed_derivation) { + ge_p3 point1; + ge_p3 point2; + ge_cached point3; + ge_p1p1 point4; + ge_p2 point5; + if (ge_frombytes_vartime(&point1, reinterpret_cast(&derived_key)) != 0) { + return false; + } + derivation_to_scalar(derivation, output_index, hashed_derivation); + ge_scalarmult_base(&point2, reinterpret_cast(&hashed_derivation)); + ge_p3_to_cached(&point3, &point2); + ge_sub(&point4, &point1, &point3); + ge_p1p1_to_p2(&point5, &point4); + ge_tobytes(reinterpret_cast(&base), &point5); + return true; + } + + void crypto_ops::derive_secret_key(const KeyDerivation &derivation, size_t output_index, + const SecretKey &base, SecretKey &derived_key) { + EllipticCurveScalar scalar; + assert(sc_check(reinterpret_cast(&base)) == 0); + derivation_to_scalar(derivation, output_index, scalar); + sc_add(reinterpret_cast(&derived_key), reinterpret_cast(&base), reinterpret_cast(&scalar)); + } + + void crypto_ops::derive_secret_key(const KeyDerivation &derivation, size_t output_index, + const SecretKey &base, const uint8_t* suffix, size_t suffixLength, SecretKey &derived_key) { + EllipticCurveScalar scalar; + assert(sc_check(reinterpret_cast(&base)) == 0); + derivation_to_scalar(derivation, output_index, suffix, suffixLength, scalar); + sc_add(reinterpret_cast(&derived_key), reinterpret_cast(&base), reinterpret_cast(&scalar)); + } + + + bool crypto_ops::underive_public_key(const KeyDerivation &derivation, size_t output_index, + const PublicKey &derived_key, PublicKey &base) { + EllipticCurveScalar scalar; + ge_p3 point1; + ge_p3 point2; + ge_cached point3; + ge_p1p1 point4; + ge_p2 point5; + if (ge_frombytes_vartime(&point1, reinterpret_cast(&derived_key)) != 0) { + return false; + } + derivation_to_scalar(derivation, output_index, scalar); + ge_scalarmult_base(&point2, reinterpret_cast(&scalar)); + ge_p3_to_cached(&point3, &point2); + ge_sub(&point4, &point1, &point3); + ge_p1p1_to_p2(&point5, &point4); + ge_tobytes(reinterpret_cast(&base), &point5); + return true; + } + + bool crypto_ops::underive_public_key(const KeyDerivation &derivation, size_t output_index, + const PublicKey &derived_key, const uint8_t* suffix, size_t suffixLength, PublicKey &base) { + EllipticCurveScalar scalar; + ge_p3 point1; + ge_p3 point2; + ge_cached point3; + ge_p1p1 point4; + ge_p2 point5; + if (ge_frombytes_vartime(&point1, reinterpret_cast(&derived_key)) != 0) { + return false; + } + + derivation_to_scalar(derivation, output_index, suffix, suffixLength, scalar); + ge_scalarmult_base(&point2, reinterpret_cast(&scalar)); + ge_p3_to_cached(&point3, &point2); + ge_sub(&point4, &point1, &point3); + ge_p1p1_to_p2(&point5, &point4); + ge_tobytes(reinterpret_cast(&base), &point5); + return true; + } + + struct s_comm { - hash h; - ec_point key; - ec_point comm; + Hash h; + EllipticCurvePoint key; + EllipticCurvePoint comm; }; - void crypto_ops::generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig) { + void crypto_ops::generate_signature(const Hash &prefix_hash, const PublicKey &pub, const SecretKey &sec, Signature &sig) { lock_guard lock(random_lock); ge_p3 tmp3; - ec_scalar k; + EllipticCurveScalar k; s_comm buf; #if !defined(NDEBUG) { ge_p3 t; - public_key t2; - assert(sc_check(&sec) == 0); - ge_scalarmult_base(&t, &sec); - ge_p3_tobytes(&t2, &t); + PublicKey t2; + assert(sc_check(reinterpret_cast(&sec)) == 0); + ge_scalarmult_base(&t, reinterpret_cast(&sec)); + ge_p3_tobytes(reinterpret_cast(&t2), &t); assert(pub == t2); } #endif buf.h = prefix_hash; - buf.key = pub; + buf.key = reinterpret_cast(pub); random_scalar(k); - ge_scalarmult_base(&tmp3, &k); - ge_p3_tobytes(&buf.comm, &tmp3); - hash_to_scalar(&buf, sizeof(s_comm), sig.c); - sc_mulsub(&sig.r, &sig.c, &sec, &k); + ge_scalarmult_base(&tmp3, reinterpret_cast(&k)); + ge_p3_tobytes(reinterpret_cast(&buf.comm), &tmp3); + hash_to_scalar(&buf, sizeof(s_comm), reinterpret_cast(sig)); + sc_mulsub(reinterpret_cast(&sig) + 32, reinterpret_cast(&sig), reinterpret_cast(&sec), reinterpret_cast(&k)); } - bool crypto_ops::check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig) { + bool crypto_ops::check_signature(const Hash &prefix_hash, const PublicKey &pub, const Signature &sig) { ge_p2 tmp2; ge_p3 tmp3; - ec_scalar c; + EllipticCurveScalar c; s_comm buf; assert(check_key(pub)); buf.h = prefix_hash; - buf.key = pub; - if (ge_frombytes_vartime(&tmp3, &pub) != 0) { + buf.key = reinterpret_cast(pub); + if (ge_frombytes_vartime(&tmp3, reinterpret_cast(&pub)) != 0) { abort(); } - if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0) { + if (sc_check(reinterpret_cast(&sig)) != 0 || sc_check(reinterpret_cast(&sig) + 32) != 0) { return false; } - ge_double_scalarmult_base_vartime(&tmp2, &sig.c, &tmp3, &sig.r); - ge_tobytes(&buf.comm, &tmp2); + ge_double_scalarmult_base_vartime(&tmp2, reinterpret_cast(&sig), &tmp3, reinterpret_cast(&sig) + 32); + ge_tobytes(reinterpret_cast(&buf.comm), &tmp2); hash_to_scalar(&buf, sizeof(s_comm), c); - sc_sub(&c, &c, &sig.c); - return sc_isnonzero(&c) == 0; + sc_sub(reinterpret_cast(&c), reinterpret_cast(&c), reinterpret_cast(&sig)); + return sc_isnonzero(reinterpret_cast(&c)) == 0; } - static void hash_to_ec(const public_key &key, ge_p3 &res) { - hash h; + static void hash_to_ec(const PublicKey &key, ge_p3 &res) { + Hash h; ge_p2 point; ge_p1p1 point2; - cn_fast_hash(std::addressof(key), sizeof(public_key), h); + cn_fast_hash(std::addressof(key), sizeof(PublicKey), h); ge_fromfe_frombytes_vartime(&point, reinterpret_cast(&h)); ge_mul8(&point2, &point); ge_p1p1_to_p3(&res, &point2); } - void crypto_ops::generate_key_image(const public_key &pub, const secret_key &sec, key_image &image) { + void crypto_ops::hash_data_to_ec(const uint8_t* data, std::size_t len, PublicKey& key) { + Hash h; + ge_p2 point; + ge_p1p1 point2; + cn_fast_hash(data, len, h); + ge_fromfe_frombytes_vartime(&point, reinterpret_cast(&h)); + ge_mul8(&point2, &point); + ge_p1p1_to_p2(&point, &point2); + ge_tobytes(reinterpret_cast(&key), &point); + } + + void crypto_ops::generate_key_image(const PublicKey &pub, const SecretKey &sec, KeyImage &image) { ge_p3 point; ge_p2 point2; - assert(sc_check(&sec) == 0); + assert(sc_check(reinterpret_cast(&sec)) == 0); hash_to_ec(pub, point); - ge_scalarmult(&point2, &sec, &point); - ge_tobytes(&image, &point2); + ge_scalarmult(&point2, reinterpret_cast(&sec), &point); + ge_tobytes(reinterpret_cast(&image), &point2); + } + + void crypto_ops::generate_incomplete_key_image(const PublicKey &pub, EllipticCurvePoint &incomplete_key_image) { + ge_p3 point; + hash_to_ec(pub, point); + ge_p3_tobytes(reinterpret_cast(&incomplete_key_image), &point); } #ifdef _MSC_VER @@ -244,9 +329,9 @@ namespace crypto { #endif struct rs_comm { - hash h; + Hash h; struct { - ec_point a, b; + EllipticCurvePoint a, b; } ab[]; }; @@ -254,25 +339,25 @@ namespace crypto { return sizeof(rs_comm) + pubs_count * sizeof(rs_comm().ab[0]); } - void crypto_ops::generate_ring_signature(const hash &prefix_hash, const key_image &image, - const public_key *const *pubs, size_t pubs_count, - const secret_key &sec, size_t sec_index, - signature *sig) { + void crypto_ops::generate_ring_signature(const Hash &prefix_hash, const KeyImage &image, + const PublicKey *const *pubs, size_t pubs_count, + const SecretKey &sec, size_t sec_index, + Signature *sig) { lock_guard lock(random_lock); size_t i; ge_p3 image_unp; ge_dsmp image_pre; - ec_scalar sum, k, h; + EllipticCurveScalar sum, k, h; rs_comm *const buf = reinterpret_cast(alloca(rs_comm_size(pubs_count))); assert(sec_index < pubs_count); #if !defined(NDEBUG) { ge_p3 t; - public_key t2; - key_image t3; - assert(sc_check(&sec) == 0); - ge_scalarmult_base(&t, &sec); - ge_p3_tobytes(&t2, &t); + PublicKey t2; + KeyImage t3; + assert(sc_check(reinterpret_cast(&sec)) == 0); + ge_scalarmult_base(&t, reinterpret_cast(&sec)); + ge_p3_tobytes(reinterpret_cast(&t2), &t); assert(*pubs[sec_index] == t2); generate_key_image(*pubs[sec_index], sec, t3); assert(image == t3); @@ -281,78 +366,78 @@ namespace crypto { } } #endif - if (ge_frombytes_vartime(&image_unp, &image) != 0) { + if (ge_frombytes_vartime(&image_unp, reinterpret_cast(&image)) != 0) { abort(); } ge_dsm_precomp(image_pre, &image_unp); - sc_0(&sum); + sc_0(reinterpret_cast(&sum)); buf->h = prefix_hash; for (i = 0; i < pubs_count; i++) { ge_p2 tmp2; ge_p3 tmp3; if (i == sec_index) { random_scalar(k); - ge_scalarmult_base(&tmp3, &k); - ge_p3_tobytes(&buf->ab[i].a, &tmp3); + ge_scalarmult_base(&tmp3, reinterpret_cast(&k)); + ge_p3_tobytes(reinterpret_cast(&buf->ab[i].a), &tmp3); hash_to_ec(*pubs[i], tmp3); - ge_scalarmult(&tmp2, &k, &tmp3); - ge_tobytes(&buf->ab[i].b, &tmp2); + ge_scalarmult(&tmp2, reinterpret_cast(&k), &tmp3); + ge_tobytes(reinterpret_cast(&buf->ab[i].b), &tmp2); } else { - random_scalar(sig[i].c); - random_scalar(sig[i].r); - if (ge_frombytes_vartime(&tmp3, &*pubs[i]) != 0) { + random_scalar(reinterpret_cast(sig[i])); + random_scalar(*reinterpret_cast(reinterpret_cast(&sig[i]) + 32)); + if (ge_frombytes_vartime(&tmp3, reinterpret_cast(&*pubs[i])) != 0) { abort(); } - ge_double_scalarmult_base_vartime(&tmp2, &sig[i].c, &tmp3, &sig[i].r); - ge_tobytes(&buf->ab[i].a, &tmp2); + ge_double_scalarmult_base_vartime(&tmp2, reinterpret_cast(&sig[i]), &tmp3, reinterpret_cast(&sig[i]) + 32); + ge_tobytes(reinterpret_cast(&buf->ab[i].a), &tmp2); hash_to_ec(*pubs[i], tmp3); - ge_double_scalarmult_precomp_vartime(&tmp2, &sig[i].r, &tmp3, &sig[i].c, image_pre); - ge_tobytes(&buf->ab[i].b, &tmp2); - sc_add(&sum, &sum, &sig[i].c); + ge_double_scalarmult_precomp_vartime(&tmp2, reinterpret_cast(&sig[i]) + 32, &tmp3, reinterpret_cast(&sig[i]), image_pre); + ge_tobytes(reinterpret_cast(&buf->ab[i].b), &tmp2); + sc_add(reinterpret_cast(&sum), reinterpret_cast(&sum), reinterpret_cast(&sig[i])); } } hash_to_scalar(buf, rs_comm_size(pubs_count), h); - sc_sub(&sig[sec_index].c, &h, &sum); - sc_mulsub(&sig[sec_index].r, &sig[sec_index].c, &sec, &k); + sc_sub(reinterpret_cast(&sig[sec_index]), reinterpret_cast(&h), reinterpret_cast(&sum)); + sc_mulsub(reinterpret_cast(&sig[sec_index]) + 32, reinterpret_cast(&sig[sec_index]), reinterpret_cast(&sec), reinterpret_cast(&k)); } - bool crypto_ops::check_ring_signature(const hash &prefix_hash, const key_image &image, - const public_key *const *pubs, size_t pubs_count, - const signature *sig) { + bool crypto_ops::check_ring_signature(const Hash &prefix_hash, const KeyImage &image, + const PublicKey *const *pubs, size_t pubs_count, + const Signature *sig) { size_t i; ge_p3 image_unp; ge_dsmp image_pre; - ec_scalar sum, h; + EllipticCurveScalar sum, h; rs_comm *const buf = reinterpret_cast(alloca(rs_comm_size(pubs_count))); #if !defined(NDEBUG) for (i = 0; i < pubs_count; i++) { assert(check_key(*pubs[i])); } #endif - if (ge_frombytes_vartime(&image_unp, &image) != 0) { + if (ge_frombytes_vartime(&image_unp, reinterpret_cast(&image)) != 0) { return false; } ge_dsm_precomp(image_pre, &image_unp); - sc_0(&sum); + sc_0(reinterpret_cast(&sum)); buf->h = prefix_hash; for (i = 0; i < pubs_count; i++) { ge_p2 tmp2; ge_p3 tmp3; - if (sc_check(&sig[i].c) != 0 || sc_check(&sig[i].r) != 0) { + if (sc_check(reinterpret_cast(&sig[i])) != 0 || sc_check(reinterpret_cast(&sig[i]) + 32) != 0) { return false; } - if (ge_frombytes_vartime(&tmp3, &*pubs[i]) != 0) { + if (ge_frombytes_vartime(&tmp3, reinterpret_cast(&*pubs[i])) != 0) { abort(); } - ge_double_scalarmult_base_vartime(&tmp2, &sig[i].c, &tmp3, &sig[i].r); - ge_tobytes(&buf->ab[i].a, &tmp2); + ge_double_scalarmult_base_vartime(&tmp2, reinterpret_cast(&sig[i]), &tmp3, reinterpret_cast(&sig[i]) + 32); + ge_tobytes(reinterpret_cast(&buf->ab[i].a), &tmp2); hash_to_ec(*pubs[i], tmp3); - ge_double_scalarmult_precomp_vartime(&tmp2, &sig[i].r, &tmp3, &sig[i].c, image_pre); - ge_tobytes(&buf->ab[i].b, &tmp2); - sc_add(&sum, &sum, &sig[i].c); + ge_double_scalarmult_precomp_vartime(&tmp2, reinterpret_cast(&sig[i]) + 32, &tmp3, reinterpret_cast(&sig[i]), image_pre); + ge_tobytes(reinterpret_cast(&buf->ab[i].b), &tmp2); + sc_add(reinterpret_cast(&sum), reinterpret_cast(&sum), reinterpret_cast(&sig[i])); } hash_to_scalar(buf, rs_comm_size(pubs_count), h); - sc_sub(&h, &h, &sum); - return sc_isnonzero(&h) == 0; + sc_sub(reinterpret_cast(&h), reinterpret_cast(&h), reinterpret_cast(&sum)); + return sc_isnonzero(reinterpret_cast(&h)) == 0; } } diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h old mode 100644 new mode 100755 index 9bc0bcd1..6426a856 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -20,15 +20,15 @@ #include #include #include +#include #include -#include "Common/pod-class.h" +#include + #include "generic-ops.h" #include "hash.h" -namespace crypto { - - using std::size_t; +namespace Crypto { extern "C" { #include "random.h" @@ -36,41 +36,13 @@ namespace crypto { extern std::mutex random_lock; -#pragma pack(push, 1) - POD_CLASS ec_point { - char data[32]; - }; +struct EllipticCurvePoint { + uint8_t data[32]; +}; - POD_CLASS ec_scalar { - char data[32]; - }; - - POD_CLASS public_key: ec_point { - friend class crypto_ops; - }; - - POD_CLASS secret_key: ec_scalar { - friend class crypto_ops; - }; - - POD_CLASS key_derivation: ec_point { - friend class crypto_ops; - }; - - POD_CLASS key_image: ec_point { - friend class crypto_ops; - }; - - POD_CLASS signature { - ec_scalar c, r; - friend class crypto_ops; - }; -#pragma pack(pop) - - static_assert(sizeof(ec_point) == 32 && sizeof(ec_scalar) == 32 && - sizeof(public_key) == 32 && sizeof(secret_key) == 32 && - sizeof(key_derivation) == 32 && sizeof(key_image) == 32 && - sizeof(signature) == 64, "Invalid structure size"); +struct EllipticCurveScalar { + uint8_t data[32]; +}; class crypto_ops { crypto_ops(); @@ -78,34 +50,48 @@ namespace crypto { void operator=(const crypto_ops &); ~crypto_ops(); - static void generate_keys(public_key &, secret_key &); - friend void generate_keys(public_key &, secret_key &); - static bool check_key(const public_key &); - friend bool check_key(const public_key &); - static bool secret_key_to_public_key(const secret_key &, public_key &); - friend bool secret_key_to_public_key(const secret_key &, public_key &); - static bool generate_key_derivation(const public_key &, const secret_key &, key_derivation &); - friend bool generate_key_derivation(const public_key &, const secret_key &, key_derivation &); - static bool derive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &); - friend bool derive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &); - static void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &); - friend void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &); - static bool underive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &); - friend bool underive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &); - static void generate_signature(const hash &, const public_key &, const secret_key &, signature &); - friend void generate_signature(const hash &, const public_key &, const secret_key &, signature &); - static bool check_signature(const hash &, const public_key &, const signature &); - friend bool check_signature(const hash &, const public_key &, const signature &); - static void generate_key_image(const public_key &, const secret_key &, key_image &); - friend void generate_key_image(const public_key &, const secret_key &, key_image &); - static void generate_ring_signature(const hash &, const key_image &, - const public_key *const *, std::size_t, const secret_key &, std::size_t, signature *); - friend void generate_ring_signature(const hash &, const key_image &, - const public_key *const *, std::size_t, const secret_key &, std::size_t, signature *); - static bool check_ring_signature(const hash &, const key_image &, - const public_key *const *, std::size_t, const signature *); - friend bool check_ring_signature(const hash &, const key_image &, - const public_key *const *, std::size_t, const signature *); + static void generate_keys(PublicKey &, SecretKey &); + friend void generate_keys(PublicKey &, SecretKey &); + static bool check_key(const PublicKey &); + friend bool check_key(const PublicKey &); + static bool secret_key_to_public_key(const SecretKey &, PublicKey &); + friend bool secret_key_to_public_key(const SecretKey &, PublicKey &); + static bool generate_key_derivation(const PublicKey &, const SecretKey &, KeyDerivation &); + friend bool generate_key_derivation(const PublicKey &, const SecretKey &, KeyDerivation &); + static bool derive_public_key(const KeyDerivation &, size_t, const PublicKey &, PublicKey &); + friend bool derive_public_key(const KeyDerivation &, size_t, const PublicKey &, PublicKey &); + friend bool derive_public_key(const KeyDerivation &, size_t, const PublicKey &, const uint8_t*, size_t, PublicKey &); + static bool derive_public_key(const KeyDerivation &, size_t, const PublicKey &, const uint8_t*, size_t, PublicKey &); + //hack for pg + static bool underive_public_key_and_get_scalar(const KeyDerivation &, std::size_t, const PublicKey &, PublicKey &, EllipticCurveScalar &); + friend bool underive_public_key_and_get_scalar(const KeyDerivation &, std::size_t, const PublicKey &, PublicKey &, EllipticCurveScalar &); + static void generate_incomplete_key_image(const PublicKey &, EllipticCurvePoint &); + friend void generate_incomplete_key_image(const PublicKey &, EllipticCurvePoint &); + // + static void derive_secret_key(const KeyDerivation &, size_t, const SecretKey &, SecretKey &); + friend void derive_secret_key(const KeyDerivation &, size_t, const SecretKey &, SecretKey &); + static void derive_secret_key(const KeyDerivation &, size_t, const SecretKey &, const uint8_t*, size_t, SecretKey &); + friend void derive_secret_key(const KeyDerivation &, size_t, const SecretKey &, const uint8_t*, size_t, SecretKey &); + static bool underive_public_key(const KeyDerivation &, size_t, const PublicKey &, PublicKey &); + friend bool underive_public_key(const KeyDerivation &, size_t, const PublicKey &, PublicKey &); + static bool underive_public_key(const KeyDerivation &, size_t, const PublicKey &, const uint8_t*, size_t, PublicKey &); + friend bool underive_public_key(const KeyDerivation &, size_t, const PublicKey &, const uint8_t*, size_t, PublicKey &); + static void generate_signature(const Hash &, const PublicKey &, const SecretKey &, Signature &); + friend void generate_signature(const Hash &, const PublicKey &, const SecretKey &, Signature &); + static bool check_signature(const Hash &, const PublicKey &, const Signature &); + friend bool check_signature(const Hash &, const PublicKey &, const Signature &); + static void generate_key_image(const PublicKey &, const SecretKey &, KeyImage &); + friend void generate_key_image(const PublicKey &, const SecretKey &, KeyImage &); + static void hash_data_to_ec(const uint8_t*, std::size_t, PublicKey&); + friend void hash_data_to_ec(const uint8_t*, std::size_t, PublicKey&); + static void generate_ring_signature(const Hash &, const KeyImage &, + const PublicKey *const *, size_t, const SecretKey &, size_t, Signature *); + friend void generate_ring_signature(const Hash &, const KeyImage &, + const PublicKey *const *, size_t, const SecretKey &, size_t, Signature *); + static bool check_ring_signature(const Hash &, const KeyImage &, + const PublicKey *const *, size_t, const Signature *); + friend bool check_ring_signature(const Hash &, const KeyImage &, + const PublicKey *const *, size_t, const Signature *); }; /* Generate a value filled with random bytes. @@ -118,7 +104,7 @@ namespace crypto { return res; } - /* Random number engine based on crypto::rand() + /* Random number engine based on Crypto::rand() */ template class random_engine { @@ -149,19 +135,19 @@ namespace crypto { /* Generate a new key pair */ - inline void generate_keys(public_key &pub, secret_key &sec) { + inline void generate_keys(PublicKey &pub, SecretKey &sec) { crypto_ops::generate_keys(pub, sec); } /* Check a public key. Returns true if it is valid, false otherwise. */ - inline bool check_key(const public_key &key) { + inline bool check_key(const PublicKey &key) { return crypto_ops::check_key(key); } /* Checks a private key and computes the corresponding public key. */ - inline bool secret_key_to_public_key(const secret_key &sec, public_key &pub) { + inline bool secret_key_to_public_key(const SecretKey &sec, PublicKey &pub) { return crypto_ops::secret_key_to_public_key(sec, pub); } @@ -171,31 +157,55 @@ namespace crypto { * * The sender uses key derivation, the output index, and the receivers' "spend" key to derive an ephemeral public key. * * The receiver can either derive the public key (to check that the transaction is addressed to him) or the private key (to spend the money). */ - inline bool generate_key_derivation(const public_key &key1, const secret_key &key2, key_derivation &derivation) { + inline bool generate_key_derivation(const PublicKey &key1, const SecretKey &key2, KeyDerivation &derivation) { return crypto_ops::generate_key_derivation(key1, key2, derivation); } - inline bool derive_public_key(const key_derivation &derivation, std::size_t output_index, - const public_key &base, public_key &derived_key) { + + inline bool derive_public_key(const KeyDerivation &derivation, size_t output_index, + const PublicKey &base, const uint8_t* prefix, size_t prefixLength, PublicKey &derived_key) { + return crypto_ops::derive_public_key(derivation, output_index, base, prefix, prefixLength, derived_key); + } + + inline bool derive_public_key(const KeyDerivation &derivation, size_t output_index, + const PublicKey &base, PublicKey &derived_key) { return crypto_ops::derive_public_key(derivation, output_index, base, derived_key); } - inline void derive_secret_key(const key_derivation &derivation, std::size_t output_index, - const secret_key &base, secret_key &derived_key) { + + + inline bool underive_public_key_and_get_scalar(const KeyDerivation &derivation, std::size_t output_index, + const PublicKey &derived_key, PublicKey &base, EllipticCurveScalar &hashed_derivation) { + return crypto_ops::underive_public_key_and_get_scalar(derivation, output_index, derived_key, base, hashed_derivation); + } + + inline void derive_secret_key(const KeyDerivation &derivation, std::size_t output_index, + const SecretKey &base, const uint8_t* prefix, size_t prefixLength, SecretKey &derived_key) { + crypto_ops::derive_secret_key(derivation, output_index, base, prefix, prefixLength, derived_key); + } + + inline void derive_secret_key(const KeyDerivation &derivation, std::size_t output_index, + const SecretKey &base, SecretKey &derived_key) { crypto_ops::derive_secret_key(derivation, output_index, base, derived_key); } + /* Inverse function of derive_public_key. It can be used by the receiver to find which "spend" key was used to generate a transaction. This may be useful if the receiver used multiple addresses which only differ in "spend" key. */ - inline bool underive_public_key(const key_derivation &derivation, std::size_t output_index, - const public_key &derived_key, public_key &base) { + inline bool underive_public_key(const KeyDerivation &derivation, size_t output_index, + const PublicKey &derived_key, const uint8_t* prefix, size_t prefixLength, PublicKey &base) { + return crypto_ops::underive_public_key(derivation, output_index, derived_key, prefix, prefixLength, base); + } + + inline bool underive_public_key(const KeyDerivation &derivation, size_t output_index, + const PublicKey &derived_key, PublicKey &base) { return crypto_ops::underive_public_key(derivation, output_index, derived_key, base); } /* Generation and checking of a standard signature. */ - inline void generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig) { + inline void generate_signature(const Hash &prefix_hash, const PublicKey &pub, const SecretKey &sec, Signature &sig) { crypto_ops::generate_signature(prefix_hash, pub, sec, sig); } - inline bool check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig) { + inline bool check_signature(const Hash &prefix_hash, const PublicKey &pub, const Signature &sig) { return crypto_ops::check_signature(prefix_hash, pub, sig); } @@ -205,36 +215,43 @@ namespace crypto { * * Then he selects a bunch of outputs, including the one he spends, and uses them to generate a ring signature. * To check the signature, it is necessary to collect all the keys that were used to generate it. To detect double spends, it is necessary to check that each key image is used at most once. */ - inline void generate_key_image(const public_key &pub, const secret_key &sec, key_image &image) { + inline void generate_key_image(const PublicKey &pub, const SecretKey &sec, KeyImage &image) { crypto_ops::generate_key_image(pub, sec, image); } - inline void generate_ring_signature(const hash &prefix_hash, const key_image &image, - const public_key *const *pubs, std::size_t pubs_count, - const secret_key &sec, std::size_t sec_index, - signature *sig) { + + inline void hash_data_to_ec(const uint8_t* data, std::size_t len, PublicKey& key) { + crypto_ops::hash_data_to_ec(data, len, key); + } + + inline void generate_ring_signature(const Hash &prefix_hash, const KeyImage &image, + const PublicKey *const *pubs, std::size_t pubs_count, + const SecretKey &sec, std::size_t sec_index, + Signature *sig) { crypto_ops::generate_ring_signature(prefix_hash, image, pubs, pubs_count, sec, sec_index, sig); } - inline bool check_ring_signature(const hash &prefix_hash, const key_image &image, - const public_key *const *pubs, std::size_t pubs_count, - const signature *sig) { + inline bool check_ring_signature(const Hash &prefix_hash, const KeyImage &image, + const PublicKey *const *pubs, size_t pubs_count, + const Signature *sig) { return crypto_ops::check_ring_signature(prefix_hash, image, pubs, pubs_count, sig); } - /* Variants with vector parameters. + /* Variants with vector parameters. */ - inline void generate_ring_signature(const hash &prefix_hash, const key_image &image, - const std::vector &pubs, - const secret_key &sec, std::size_t sec_index, - signature *sig) { + inline void generate_ring_signature(const Hash &prefix_hash, const KeyImage &image, + const std::vector &pubs, + const SecretKey &sec, size_t sec_index, + Signature *sig) { generate_ring_signature(prefix_hash, image, pubs.data(), pubs.size(), sec, sec_index, sig); } - inline bool check_ring_signature(const hash &prefix_hash, const key_image &image, - const std::vector &pubs, - const signature *sig) { + inline bool check_ring_signature(const Hash &prefix_hash, const KeyImage &image, + const std::vector &pubs, + const Signature *sig) { return check_ring_signature(prefix_hash, image, pubs.data(), pubs.size(), sig); } + } -CRYPTO_MAKE_HASHABLE(public_key) -CRYPTO_MAKE_HASHABLE(key_image) -CRYPTO_MAKE_COMPARABLE(signature) +CRYPTO_MAKE_HASHABLE(PublicKey) +CRYPTO_MAKE_HASHABLE(KeyImage) +CRYPTO_MAKE_COMPARABLE(Signature) +CRYPTO_MAKE_COMPARABLE(SecretKey) diff --git a/src/crypto/generic-ops.h b/src/crypto/generic-ops.h index 4b4b16d9..91d35297 100644 --- a/src/crypto/generic-ops.h +++ b/src/crypto/generic-ops.h @@ -22,7 +22,7 @@ #include #define CRYPTO_MAKE_COMPARABLE(type) \ -namespace crypto { \ +namespace Crypto { \ inline bool operator==(const type &_v1, const type &_v2) { \ return std::memcmp(&_v1, &_v2, sizeof(type)) == 0; \ } \ @@ -33,17 +33,17 @@ namespace crypto { \ #define CRYPTO_MAKE_HASHABLE(type) \ CRYPTO_MAKE_COMPARABLE(type) \ -namespace crypto { \ - static_assert(sizeof(std::size_t) <= sizeof(type), "Size of " #type " must be at least that of size_t"); \ - inline std::size_t hash_value(const type &_v) { \ - return reinterpret_cast(_v); \ +namespace Crypto { \ + static_assert(sizeof(size_t) <= sizeof(type), "Size of " #type " must be at least that of size_t"); \ + inline size_t hash_value(const type &_v) { \ + return reinterpret_cast(_v); \ } \ } \ namespace std { \ template<> \ - struct hash { \ - std::size_t operator()(const crypto::type &_v) const { \ - return reinterpret_cast(_v); \ + struct hash { \ + size_t operator()(const Crypto::type &_v) const { \ + return reinterpret_cast(_v); \ } \ }; \ } diff --git a/src/crypto/groestl.c b/src/crypto/groestl.c index 00bf987c..e194d7ea 100644 --- a/src/crypto/groestl.c +++ b/src/crypto/groestl.c @@ -204,7 +204,7 @@ static void OutputTransformation(hashState *ctx) { /* initialise context */ static void Init(hashState* ctx) { - int i = 0; + unsigned int i = 0; /* allocate memory for state and data buffer */ for(;i<(SIZE512/sizeof(uint32_t));i++) diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h old mode 100644 new mode 100755 diff --git a/src/crypto/hash.h b/src/crypto/hash.h index 05e2e459..08410a2c 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -19,33 +19,25 @@ #include -#include "Common/pod-class.h" +#include #include "generic-ops.h" -namespace crypto { +namespace Crypto { extern "C" { #include "hash-ops.h" } -#pragma pack(push, 1) - POD_CLASS hash { - char data[HASH_SIZE]; - }; -#pragma pack(pop) - - static_assert(sizeof(hash) == HASH_SIZE, "Invalid structure size"); - /* Cryptonight hash functions */ - inline void cn_fast_hash(const void *data, std::size_t length, hash &hash) { + inline void cn_fast_hash(const void *data, size_t length, Hash &hash) { cn_fast_hash(data, length, reinterpret_cast(&hash)); } - inline hash cn_fast_hash(const void *data, std::size_t length) { - hash h; + inline Hash cn_fast_hash(const void *data, size_t length) { + Hash h; cn_fast_hash(data, length, reinterpret_cast(&h)); return h; } @@ -63,25 +55,25 @@ namespace crypto { private: void *data; - friend inline void cn_slow_hash(cn_context &, const void *, std::size_t, hash &); + friend inline void cn_slow_hash(cn_context &, const void *, size_t, Hash &); }; - inline void cn_slow_hash(cn_context &context, const void *data, std::size_t length, hash &hash) { + inline void cn_slow_hash(cn_context &context, const void *data, size_t length, Hash &hash) { (*cn_slow_hash_f)(context.data, data, length, reinterpret_cast(&hash)); } - inline void tree_hash(const hash *hashes, std::size_t count, hash &root_hash) { + inline void tree_hash(const Hash *hashes, size_t count, Hash &root_hash) { tree_hash(reinterpret_cast(hashes), count, reinterpret_cast(&root_hash)); } - inline void tree_branch(const hash *hashes, std::size_t count, hash *branch) { + inline void tree_branch(const Hash *hashes, size_t count, Hash *branch) { tree_branch(reinterpret_cast(hashes), count, reinterpret_cast(branch)); } - inline void tree_hash_from_branch(const hash *branch, std::size_t depth, const hash &leaf, const void *path, hash &root_hash) { + inline void tree_hash_from_branch(const Hash *branch, size_t depth, const Hash &leaf, const void *path, Hash &root_hash) { tree_hash_from_branch(reinterpret_cast(branch), depth, reinterpret_cast(&leaf), path, reinterpret_cast(&root_hash)); } } -CRYPTO_MAKE_HASHABLE(hash) +CRYPTO_MAKE_HASHABLE(Hash) diff --git a/src/crypto/skein.c b/src/crypto/skein.c index 9c8ac288..65e4525c 100644 --- a/src/crypto/skein.c +++ b/src/crypto/skein.c @@ -77,7 +77,7 @@ typedef struct /* 1024-bit Skein hash context stru } Skein1024_Ctxt_t; /* Skein APIs for (incremental) "straight hashing" */ -#if SKEIN_256_NIST_MAX_HASH_BITS +#if SKEIN_256_NIST_MAX_HASHBITS static int Skein_256_Init (Skein_256_Ctxt_t *ctx, size_t hashBitLen); #endif static int Skein_512_Init (Skein_512_Ctxt_t *ctx, size_t hashBitLen); @@ -1941,7 +1941,7 @@ static HashReturn Final (hashState *state, BitSequence *hashval); /* select the context size and init the context */ static HashReturn Init(hashState *state, int hashbitlen) { -#if SKEIN_256_NIST_MAX_HASH_BITS +#if SKEIN_256_NIST_MAX_HASHBITS if (hashbitlen <= SKEIN_256_NIST_MAX_HASHBITS) { Skein_Assert(hashbitlen > 0,BAD_HASHLEN); diff --git a/src/crypto/skein_port.h b/src/crypto/skein_port.h old mode 100644 new mode 100755 diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c old mode 100644 new mode 100755 diff --git a/src/crypto/slow-hash.cpp b/src/crypto/slow-hash.cpp index 33fa46b7..58fc44cc 100644 --- a/src/crypto/slow-hash.cpp +++ b/src/crypto/slow-hash.cpp @@ -27,7 +27,7 @@ using std::bad_alloc; -namespace crypto { +namespace Crypto { enum { MAP_SIZE = SLOW_HASH_CONTEXT_SIZE + ((-SLOW_HASH_CONTEXT_SIZE) & 0xfff) diff --git a/src/cryptonote_core/BlockIndex.cpp b/src/cryptonote_core/BlockIndex.cpp deleted file mode 100644 index 12e973bd..00000000 --- a/src/cryptonote_core/BlockIndex.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "BlockIndex.h" -#include - -namespace CryptoNote -{ - crypto::hash BlockIndex::getBlockId(uint64_t height) const { - if (height >= m_container.size()) - return boost::value_initialized(); - return m_container[static_cast(height)]; - } - - bool BlockIndex::getBlockIds(uint64_t startHeight, size_t maxCount, std::list& items) const { - if (startHeight >= m_container.size()) - return false; - - for (size_t i = startHeight; i < (startHeight + maxCount) && i < m_container.size(); ++i) { - items.push_back(m_container[i]); - } - - return true; - } - - - bool BlockIndex::findSupplement(const std::list& ids, uint64_t& offset) const { - - for (const auto& id : ids) { - if (getBlockHeight(id, offset)) - return true; - } - - return false; - } - - bool BlockIndex::getShortChainHistory(std::list& ids) const { - size_t i = 0; - size_t current_multiplier = 1; - size_t sz = size(); - - if (!sz) - return true; - - size_t current_back_offset = 1; - bool genesis_included = false; - - while (current_back_offset < sz) { - ids.push_back(m_container[sz - current_back_offset]); - if (sz - current_back_offset == 0) - genesis_included = true; - if (i < 10) { - ++current_back_offset; - } else { - current_back_offset += current_multiplier *= 2; - } - ++i; - } - - if (!genesis_included) - ids.push_back(m_container[0]); - - return true; - } - - crypto::hash BlockIndex::getTailId() const { - if (m_container.empty()) - return boost::value_initialized(); - return m_container.back(); - } - - -} diff --git a/src/cryptonote_core/ICore.h b/src/cryptonote_core/ICore.h deleted file mode 100755 index 5ab16254..00000000 --- a/src/cryptonote_core/ICore.h +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include -#include -#include -#include - -#include "crypto/hash.h" -#include "cryptonote_core/difficulty.h" -#include "cryptonote_protocol/blobdatatype.h" - -namespace CryptoNote { - -struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request; -struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response; -struct NOTIFY_RESPONSE_CHAIN_ENTRY_request; -struct NOTIFY_RESPONSE_GET_OBJECTS_request; -struct NOTIFY_REQUEST_GET_OBJECTS_request; - -class Currency; -class ICoreObserver; -struct Block; -struct block_verification_context; -struct BlockFullInfo; -struct core_stat_info; -struct i_cryptonote_protocol; -struct Transaction; -struct TransactionInputMultisignature; -struct TransactionInputToKey; -struct tx_verification_context; - -class ICore { -public: - virtual ~ICore() {} - - virtual bool addObserver(ICoreObserver* observer) = 0; - virtual bool removeObserver(ICoreObserver* observer) = 0; - - virtual bool have_block(const crypto::hash& id) = 0; - virtual bool get_short_chain_history(std::list& ids) = 0; - virtual bool get_stat_info(CryptoNote::core_stat_info& st_inf) = 0; - virtual bool on_idle() = 0; - virtual void pause_mining() = 0; - virtual void update_block_template_and_resume_mining() = 0; - virtual bool handle_incoming_block_blob(const CryptoNote::blobdata& block_blob, CryptoNote::block_verification_context& bvc, bool control_miner, bool relay_block) = 0; - virtual bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp) = 0; - virtual void on_synchronized() = 0; - virtual bool is_ready() = 0; - - virtual bool get_blockchain_top(uint64_t& height, crypto::hash& top_id) = 0; - virtual bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, - uint64_t& total_height, uint64_t& start_height, size_t max_count) = 0; - virtual bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY_request& resp) = 0; - virtual bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res) = 0; - virtual bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) = 0; - virtual i_cryptonote_protocol* get_protocol() = 0; - virtual bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block) = 0; - virtual std::vector getPoolTransactions() = 0; - virtual bool getPoolChanges(const crypto::hash& tailBlockId, const std::vector& knownTxsIds, - std::vector& addedTxs, std::vector& deletedTxsIds) = 0; - virtual void getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, - std::vector& deletedTxsIds) = 0; - virtual bool queryBlocks(const std::list& block_ids, uint64_t timestamp, - uint64_t& start_height, uint64_t& current_height, uint64_t& full_offset, std::list& entries) = 0; - - virtual crypto::hash getBlockIdByHeight(uint64_t height) = 0; - virtual bool getBlockByHash(const crypto::hash &h, Block &blk) = 0; - virtual void getTransactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs, bool checkTxPool = false) = 0; - virtual bool getBackwardBlocksSizes(uint64_t fromHeight, std::vector& sizes, size_t count) = 0; - virtual bool getBlockSize(const crypto::hash& hash, size_t& size) = 0; - virtual bool getAlreadyGeneratedCoins(const crypto::hash& hash, uint64_t& generatedCoins) = 0; - virtual bool getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, - bool penalizeFee, uint64_t& reward, int64_t& emissionChange) = 0; - virtual bool scanOutputkeysForIndices(const TransactionInputToKey& txInToKey, std::list>& outputReferences) = 0; - virtual bool getBlockDifficulty(uint64_t height, difficulty_type& difficulty) = 0; - virtual bool getBlockContainingTx(const crypto::hash& txId, crypto::hash& blockId, uint64_t& blockHeight) = 0; - virtual bool getMultisigOutputReference(const TransactionInputMultisignature& txInMultisig, std::pair& outputReference) = 0; -}; - -} //namespace CryptoNote diff --git a/src/cryptonote_core/Transaction.cpp b/src/cryptonote_core/Transaction.cpp deleted file mode 100644 index c7dada32..00000000 --- a/src/cryptonote_core/Transaction.cpp +++ /dev/null @@ -1,644 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "ITransaction.h" -#include "TransactionExtra.h" - -#include "cryptonote_format_utils.h" -#include "account.h" - -#include -#include -#include - -namespace { - - using namespace CryptoNote; - - void derivePublicKey(const AccountAddress& to, const crypto::secret_key& txKey, size_t outputIndex, crypto::public_key& ephemeralKey) { - crypto::key_derivation derivation; - crypto::generate_key_derivation(*reinterpret_cast(&to.viewPublicKey), txKey, derivation); - crypto::derive_public_key(derivation, outputIndex, *reinterpret_cast(&to.spendPublicKey), ephemeralKey); - } - - bool checkInputsKeyimagesDiff(const CryptoNote::Transaction& tx) { - std::unordered_set ki; - for (const auto& in : tx.vin) { - if (in.type() == typeid(TransactionInputToKey)) { - if (!ki.insert(boost::get(in).keyImage).second) - return false; - } - } - return true; - } - - - // TransactionInput helper functions - - size_t getRequiredSignaturesCount(const TransactionInput& in) { - if (in.type() == typeid(TransactionInputToKey)) { - return boost::get(in).keyOffsets.size(); - } - if (in.type() == typeid(TransactionInputMultisignature)) { - return boost::get(in).signatures; - } - return 0; - } - - uint64_t getTransactionInputAmount(const TransactionInput& in) { - if (in.type() == typeid(TransactionInputToKey)) { - return boost::get(in).amount; - } - if (in.type() == typeid(TransactionInputMultisignature)) { - return boost::get(in).amount; - } - return 0; - } - - TransactionTypes::InputType getTransactionInputType(const TransactionInput& in) { - if (in.type() == typeid(TransactionInputToKey)) { - return TransactionTypes::InputType::Key; - } - if (in.type() == typeid(TransactionInputMultisignature)) { - return TransactionTypes::InputType::Multisignature; - } - if (in.type() == typeid(TransactionInputGenerate)) { - return TransactionTypes::InputType::Generating; - } - return TransactionTypes::InputType::Invalid; - } - - const TransactionInput& getInputChecked(const CryptoNote::Transaction& transaction, size_t index) { - if (transaction.vin.size() <= index) { - throw std::runtime_error("Transaction input index out of range"); - } - return transaction.vin[index]; - } - - const TransactionInput& getInputChecked(const CryptoNote::Transaction& transaction, size_t index, TransactionTypes::InputType type) { - const auto& input = getInputChecked(transaction, index); - if (getTransactionInputType(input) != type) { - throw std::runtime_error("Unexpected transaction input type"); - } - return input; - } - - // TransactionOutput helper functions - - TransactionTypes::OutputType getTransactionOutputType(const TransactionOutputTarget& out) { - if (out.type() == typeid(TransactionOutputToKey)) { - return TransactionTypes::OutputType::Key; - } - if (out.type() == typeid(TransactionOutputMultisignature)) { - return TransactionTypes::OutputType::Multisignature; - } - return TransactionTypes::OutputType::Invalid; - } - - const TransactionOutput& getOutputChecked(const CryptoNote::Transaction& transaction, size_t index) { - if (transaction.vout.size() <= index) { - throw std::runtime_error("Transaction output index out of range"); - } - return transaction.vout[index]; - } - - const TransactionOutput& getOutputChecked(const CryptoNote::Transaction& transaction, size_t index, TransactionTypes::OutputType type) { - const auto& output = getOutputChecked(transaction, index); - if (getTransactionOutputType(output.target) != type) { - throw std::runtime_error("Unexpected transaction output target type"); - } - return output; - } -} - - -namespace CryptoNote { - - using namespace TransactionTypes; - - //////////////////////////////////////////////////////////////////////// - // class Transaction declaration - //////////////////////////////////////////////////////////////////////// - - class TransactionImpl : public ITransaction { - public: - TransactionImpl(); - TransactionImpl(const Blob& txblob); - TransactionImpl(const CryptoNote::Transaction& tx); - - // ITransactionReader - virtual Hash getTransactionHash() const override; - virtual Hash getTransactionPrefixHash() const override; - virtual PublicKey getTransactionPublicKey() const override; - virtual uint64_t getUnlockTime() const override; - virtual bool getPaymentId(Hash& hash) const override; - virtual bool getExtraNonce(std::string& nonce) const override; - virtual Blob getExtra() const override; - - // inputs - virtual size_t getInputCount() const override; - virtual uint64_t getInputTotalAmount() const override; - virtual TransactionTypes::InputType getInputType(size_t index) const override; - virtual void getInput(size_t index, TransactionTypes::InputKey& input) const override; - virtual void getInput(size_t index, TransactionTypes::InputMultisignature& input) const override; - - // outputs - virtual size_t getOutputCount() const override; - virtual uint64_t getOutputTotalAmount() const override; - virtual TransactionTypes::OutputType getOutputType(size_t index) const override; - virtual void getOutput(size_t index, TransactionTypes::OutputKey& output) const override; - virtual void getOutput(size_t index, TransactionTypes::OutputMultisignature& output) const override; - - virtual size_t getRequiredSignaturesCount(size_t index) const override; - virtual bool findOutputsToAccount(const AccountAddress& addr, const SecretKey& viewSecretKey, std::vector& outs, uint64_t& outputAmount) const override; - - // various checks - virtual bool validateInputs() const override; - virtual bool validateOutputs() const override; - virtual bool validateSignatures() const override; - - // get serialized transaction - virtual Blob getTransactionData() const override; - - // ITransactionWriter - - virtual void setUnlockTime(uint64_t unlockTime) override; - virtual void setPaymentId(const Hash& hash) override; - virtual void setExtraNonce(const std::string& nonce) override; - virtual void appendExtra(const Blob& extraData) override; - - // Inputs/Outputs - virtual size_t addInput(const TransactionTypes::InputKey& input) override; - virtual size_t addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, TransactionTypes::KeyPair& ephKeys) override; - virtual size_t addInput(const TransactionTypes::InputMultisignature& input) override; - - virtual size_t addOutput(uint64_t amount, const AccountAddress& to) override; - virtual size_t addOutput(uint64_t amount, const std::vector& to, uint32_t requiredSignatures) override; - - virtual void signInputKey(size_t input, const TransactionTypes::InputKeyInfo& info, const TransactionTypes::KeyPair& ephKeys) override; - virtual void signInputMultisignature(size_t input, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) override; - - // secret key - virtual bool getTransactionSecretKey(SecretKey& key) const override; - virtual void setTransactionSecretKey(const SecretKey& key) override; - - private: - - void invalidateHash(); - - std::vector& getSignatures(size_t input); - - const crypto::secret_key& txSecretKey() const { - if (!secretKey) { - throw std::runtime_error("Operation requires transaction secret key"); - } - return *secretKey; - } - - void checkIfSigning() const { - if (!transaction.signatures.empty()) { - throw std::runtime_error("Cannot perform requested operation, since it will invalidate transaction signatures"); - } - } - - CryptoNote::Transaction transaction; - boost::optional secretKey; - mutable boost::optional transactionHash; - TransactionExtra extra; - }; - - - //////////////////////////////////////////////////////////////////////// - // class Transaction implementation - //////////////////////////////////////////////////////////////////////// - - std::unique_ptr createTransaction() { - return std::unique_ptr(new TransactionImpl()); - } - - std::unique_ptr createTransaction(const Blob& transactionBlob) { - return std::unique_ptr(new TransactionImpl(transactionBlob)); - } - - std::unique_ptr createTransaction(const CryptoNote::Transaction& tx) { - return std::unique_ptr(new TransactionImpl(tx)); - } - - TransactionImpl::TransactionImpl() { - CryptoNote::KeyPair txKeys(CryptoNote::KeyPair::generate()); - - tx_extra_pub_key pk = { txKeys.pub }; - extra.set(pk); - - transaction.version = CURRENT_TRANSACTION_VERSION; - transaction.unlockTime = 0; - transaction.extra = extra.serialize(); - - secretKey = txKeys.sec; - } - - TransactionImpl::TransactionImpl(const Blob& data) { - CryptoNote::blobdata blob(reinterpret_cast(data.data()), data.size()); - if (!parse_and_validate_tx_from_blob(blob, transaction)) { - throw std::runtime_error("Invalid transaction data"); - } - - extra.parse(transaction.extra); - transactionHash = get_blob_hash(blob); // avoid serialization if we already have blob - } - - TransactionImpl::TransactionImpl(const CryptoNote::Transaction& tx) : transaction(tx) { - extra.parse(transaction.extra); - } - - void TransactionImpl::invalidateHash() { - if (transactionHash.is_initialized()) { - transactionHash = decltype(transactionHash)(); - } - } - - Hash TransactionImpl::getTransactionHash() const { - if (!transactionHash.is_initialized()) { - transactionHash = get_transaction_hash(transaction); - } - - return reinterpret_cast(transactionHash.get()); - } - - Hash TransactionImpl::getTransactionPrefixHash() const { - auto hash = get_transaction_prefix_hash(transaction); - return reinterpret_cast(hash); - } - - PublicKey TransactionImpl::getTransactionPublicKey() const { - crypto::public_key pk(null_pkey); - extra.getPublicKey(pk); - return reinterpret_cast(pk); - } - - uint64_t TransactionImpl::getUnlockTime() const { - return transaction.unlockTime; - } - - void TransactionImpl::setUnlockTime(uint64_t unlockTime) { - checkIfSigning(); - transaction.unlockTime = unlockTime; - invalidateHash(); - } - - bool TransactionImpl::getTransactionSecretKey(SecretKey& key) const { - if (!secretKey) { - return false; - } - key = reinterpret_cast(secretKey.get()); - return true; - } - - void TransactionImpl::setTransactionSecretKey(const SecretKey& key) { - const auto& sk = reinterpret_cast(key); - crypto::public_key pk; - crypto::public_key txPubKey; - - crypto::secret_key_to_public_key(sk, pk); - extra.getPublicKey(txPubKey); - - if (txPubKey != pk) { - throw std::runtime_error("Secret transaction key does not match public key"); - } - - secretKey = reinterpret_cast(key); - } - - size_t TransactionImpl::addInput(const InputKey& input) { - checkIfSigning(); - TransactionInputToKey inKey = { input.amount, input.keyOffsets, *reinterpret_cast(&input.keyImage) }; - transaction.vin.emplace_back(inKey); - invalidateHash(); - return transaction.vin.size() - 1; - } - - size_t TransactionImpl::addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, TransactionTypes::KeyPair& ephKeys) { - checkIfSigning(); - InputKey input; - input.amount = info.amount; - - generate_key_image_helper( - reinterpret_cast(senderKeys), - reinterpret_cast(info.realOutput.transactionPublicKey), - info.realOutput.outputInTransaction, - reinterpret_cast(ephKeys), - reinterpret_cast(input.keyImage)); - - // fill outputs array and use relative offsets - for (const auto& out : info.outputs) { - input.keyOffsets.push_back(out.outputIndex); - } - - input.keyOffsets = absolute_output_offsets_to_relative(input.keyOffsets); - return addInput(input); - } - - size_t TransactionImpl::addInput(const InputMultisignature& input) { - checkIfSigning(); - - TransactionInputMultisignature inMsig; - inMsig.amount = input.amount; - inMsig.outputIndex = input.outputIndex; - inMsig.signatures = input.signatures; - transaction.vin.push_back(inMsig); - invalidateHash(); - - return transaction.vin.size() - 1; - } - - size_t TransactionImpl::addOutput(uint64_t amount, const AccountAddress& to) { - checkIfSigning(); - - TransactionOutputToKey outKey; - derivePublicKey(to, txSecretKey(), transaction.vout.size(), outKey.key); - TransactionOutput out = { amount, outKey }; - transaction.vout.emplace_back(out); - invalidateHash(); - - return transaction.vout.size() - 1; - } - - size_t TransactionImpl::addOutput(uint64_t amount, const std::vector& to, uint32_t requiredSignatures) { - checkIfSigning(); - - const auto& txKey = txSecretKey(); - size_t outputIndex = transaction.vout.size(); - TransactionOutputMultisignature outMsig; - outMsig.requiredSignatures = requiredSignatures; - outMsig.keys.resize(to.size()); - - for (size_t i = 0; i < to.size(); ++i) { - derivePublicKey(to[i], txKey, outputIndex, outMsig.keys[i]); - } - - TransactionOutput out = { amount, outMsig }; - transaction.vout.emplace_back(out); - invalidateHash(); - - return outputIndex; - } - - void TransactionImpl::signInputKey(size_t index, const TransactionTypes::InputKeyInfo& info, const TransactionTypes::KeyPair& ephKeys) { - const auto& input = boost::get(getInputChecked(transaction, index, InputType::Key)); - Hash prefixHash = getTransactionPrefixHash(); - - std::vector signatures; - std::vector keysPtrs; - - for (const auto& o : info.outputs) { - keysPtrs.push_back(reinterpret_cast(&o.targetKey)); - } - - signatures.resize(keysPtrs.size()); - - generate_ring_signature( - reinterpret_cast(prefixHash), - reinterpret_cast(input.keyImage), - keysPtrs, - reinterpret_cast(ephKeys.secretKey), - info.realOutput.transactionIndex, - signatures.data()); - - getSignatures(index) = signatures; - invalidateHash(); - } - - void TransactionImpl::signInputMultisignature(size_t index, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) { - crypto::key_derivation derivation; - crypto::public_key ephemeralPublicKey; - crypto::secret_key ephemeralSecretKey; - - crypto::generate_key_derivation( - reinterpret_cast(sourceTransactionKey), - reinterpret_cast(accountKeys.viewSecretKey), - derivation); - - crypto::derive_public_key(derivation, outputIndex, - reinterpret_cast(accountKeys.address.spendPublicKey), ephemeralPublicKey); - crypto::derive_secret_key(derivation, outputIndex, - reinterpret_cast(accountKeys.spendSecretKey), ephemeralSecretKey); - - crypto::signature signature; - auto txPrefixHash = getTransactionPrefixHash(); - - crypto::generate_signature(reinterpret_cast(txPrefixHash), - ephemeralPublicKey, ephemeralSecretKey, signature); - - getSignatures(index).push_back(signature); - invalidateHash(); - } - - std::vector& TransactionImpl::getSignatures(size_t input) { - // update signatures container size if needed - if (transaction.signatures.size() < transaction.vin.size()) { - transaction.signatures.resize(transaction.vin.size()); - } - // check range - if (input >= transaction.signatures.size()) { - throw std::runtime_error("Invalid input index"); - } - - return transaction.signatures[input]; - } - - std::vector TransactionImpl::getTransactionData() const { - return stringToVector(t_serializable_object_to_blob(transaction)); - } - - void TransactionImpl::setPaymentId(const Hash& hash) { - checkIfSigning(); - blobdata paymentIdBlob; - set_payment_id_to_tx_extra_nonce(paymentIdBlob, reinterpret_cast(hash)); - setExtraNonce(paymentIdBlob); - } - - bool TransactionImpl::getPaymentId(Hash& hash) const { - blobdata nonce; - if (getExtraNonce(nonce)) { - crypto::hash paymentId; - if (get_payment_id_from_tx_extra_nonce(nonce, paymentId)) { - hash = reinterpret_cast(paymentId); - return true; - } - } - return false; - } - - void TransactionImpl::setExtraNonce(const std::string& nonce) { - checkIfSigning(); - tx_extra_nonce extraNonce = { nonce }; - extra.set(extraNonce); - transaction.extra = extra.serialize(); - invalidateHash(); - } - - void TransactionImpl::appendExtra(const Blob& extraData) { - checkIfSigning(); - transaction.extra.insert( - transaction.extra.end(), extraData.begin(), extraData.end()); - } - - bool TransactionImpl::getExtraNonce(std::string& nonce) const { - tx_extra_nonce extraNonce; - if (extra.get(extraNonce)) { - nonce = extraNonce.nonce; - return true; - } - return false; - } - - Blob TransactionImpl::getExtra() const { - return transaction.extra; - } - - size_t TransactionImpl::getInputCount() const { - return transaction.vin.size(); - } - - uint64_t TransactionImpl::getInputTotalAmount() const { - return std::accumulate(transaction.vin.begin(), transaction.vin.end(), 0ULL, [](uint64_t val, const TransactionInput& in) { - return val + getTransactionInputAmount(in); }); - } - - TransactionTypes::InputType TransactionImpl::getInputType(size_t index) const { - return getTransactionInputType(getInputChecked(transaction, index)); - } - - void TransactionImpl::getInput(size_t index, InputKey& input) const { - const auto& k = boost::get(getInputChecked(transaction, index, InputType::Key)); - input.amount = k.amount; - input.keyImage = reinterpret_cast(k.keyImage); - input.keyOffsets = k.keyOffsets; - } - - void TransactionImpl::getInput(size_t index, InputMultisignature& input) const { - const auto& m = boost::get(getInputChecked(transaction, index, InputType::Multisignature)); - input.amount = m.amount; - input.outputIndex = m.outputIndex; - input.signatures = m.signatures; - } - - size_t TransactionImpl::getOutputCount() const { - return transaction.vout.size(); - } - - uint64_t TransactionImpl::getOutputTotalAmount() const { - return std::accumulate(transaction.vout.begin(), transaction.vout.end(), 0ULL, [](uint64_t val, const TransactionOutput& out) { - return val + out.amount; }); - } - - TransactionTypes::OutputType TransactionImpl::getOutputType(size_t index) const { - return getTransactionOutputType(getOutputChecked(transaction, index).target); - } - - void TransactionImpl::getOutput(size_t index, OutputKey& output) const { - const auto& out = getOutputChecked(transaction, index, OutputType::Key); - const auto& k = boost::get(out.target); - output.amount = out.amount; - output.key = reinterpret_cast(k.key); - } - - void TransactionImpl::getOutput(size_t index, OutputMultisignature& output) const { - const auto& out = getOutputChecked(transaction, index, OutputType::Multisignature); - const auto& m = boost::get(out.target); - output.amount = out.amount; - output.keys = reinterpret_cast&>(m.keys); - output.requiredSignatures = m.requiredSignatures; - } - - bool isOutToKey(const crypto::public_key& spendPublicKey, const crypto::public_key& outKey, const crypto::key_derivation& derivation, size_t keyIndex) { - crypto::public_key pk; - derive_public_key(derivation, keyIndex, spendPublicKey, pk); - return pk == outKey; - } - - bool TransactionImpl::findOutputsToAccount(const AccountAddress& addr, const SecretKey& viewSecretKey, std::vector& out, uint64_t& amount) const { - account_keys keys; - keys.m_account_address = reinterpret_cast(addr); - // only view secret key is used, spend key is not needed - keys.m_view_secret_key = reinterpret_cast(viewSecretKey); - - auto pk = getTransactionPublicKey(); - crypto::public_key txPubKey = reinterpret_cast(pk); - - amount = 0; - size_t keyIndex = 0; - uint32_t outputIndex = 0; - - crypto::key_derivation derivation; - generate_key_derivation(txPubKey, keys.m_view_secret_key, derivation); - - for (const TransactionOutput& o : transaction.vout) { - assert(o.target.type() == typeid(TransactionOutputToKey) || o.target.type() == typeid(TransactionOutputMultisignature)); - if (o.target.type() == typeid(TransactionOutputToKey)) { - if (is_out_to_acc(keys, boost::get(o.target), derivation, keyIndex)) { - out.push_back(outputIndex); - amount += o.amount; - } - ++keyIndex; - } else if (o.target.type() == typeid(TransactionOutputMultisignature)) { - const auto& target = boost::get(o.target); - for (const auto& key : target.keys) { - if (isOutToKey(keys.m_account_address.m_spendPublicKey, key, derivation, static_cast(outputIndex))) { - out.push_back(outputIndex); - } - ++keyIndex; - } - } - ++outputIndex; - } - - return true; - } - - size_t TransactionImpl::getRequiredSignaturesCount(size_t index) const { - return ::getRequiredSignaturesCount(getInputChecked(transaction, index)); - } - - bool TransactionImpl::validateInputs() const { - return - check_inputs_types_supported(transaction) && - check_inputs_overflow(transaction) && - checkInputsKeyimagesDiff(transaction) && - checkMultisignatureInputsDiff(transaction); - } - - bool TransactionImpl::validateOutputs() const { - return - check_outs_valid(transaction) && - check_outs_overflow(transaction); - } - - bool TransactionImpl::validateSignatures() const { - if (transaction.signatures.size() < transaction.vin.size()) { - return false; - } - - for (size_t i = 0; i < transaction.vin.size(); ++i) { - if (getRequiredSignaturesCount(i) > transaction.signatures[i].size()) { - return false; - } - } - - return true; - } -} diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h deleted file mode 100644 index 6b0781de..00000000 --- a/src/cryptonote_core/blockchain_storage.h +++ /dev/null @@ -1,352 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include - -#include "google/sparse_hash_set" -#include "google/sparse_hash_map" - -#include "Common/ObserverManager.h" -#include "Common/util.h" -#include "cryptonote_core/BlockIndex.h" -#include "cryptonote_core/checkpoints.h" -#include "cryptonote_core/Currency.h" -#include "cryptonote_core/IBlockchainStorageObserver.h" -#include "cryptonote_core/ITransactionValidator.h" -#include "cryptonote_core/SwappedVector.h" -#include "cryptonote_core/UpgradeDetector.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/tx_pool.h" - - -#include - -#undef ERROR - -namespace CryptoNote { - struct NOTIFY_RESPONSE_CHAIN_ENTRY_request; - struct NOTIFY_REQUEST_GET_OBJECTS_request; - struct NOTIFY_RESPONSE_GET_OBJECTS_request; - struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request; - struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response; - struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount; - - using CryptoNote::BlockInfo; - class blockchain_storage : public CryptoNote::ITransactionValidator { - public: - blockchain_storage(const Currency& currency, tx_memory_pool& tx_pool, Logging::ILogger& logger); - - bool addObserver(IBlockchainStorageObserver* observer); - bool removeObserver(IBlockchainStorageObserver* observer); - - // ITransactionValidator - virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock); - virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed); - virtual bool haveSpentKeyImages(const CryptoNote::Transaction& tx); - - bool init() { return init(tools::get_default_data_dir(), true); } - bool init(const std::string& config_folder, bool load_existing); - bool deinit(); - - bool getLowerBound(uint64_t timestamp, uint64_t startOffset, uint64_t& height); - bool getBlockIds(uint64_t startHeight, size_t maxCount, std::list& items); - - void set_checkpoints(checkpoints&& chk_pts) { m_checkpoints = chk_pts; } - bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs); - bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks); - bool get_alternative_blocks(std::list& blocks); - size_t get_alternative_blocks_count(); - crypto::hash get_block_id_by_height(uint64_t height); - bool get_block_by_hash(const crypto::hash &h, Block &blk); - - template void serialize(archive_t & ar, const unsigned int version); - - bool have_tx(const crypto::hash &id); - bool have_tx_keyimges_as_spent(const Transaction &tx); - - uint64_t get_current_blockchain_height(); - crypto::hash get_tail_id(); - crypto::hash get_tail_id(uint64_t& height); - difficulty_type get_difficulty_for_next_block(); - uint64_t getCoinsInCirculation(); - uint8_t get_block_major_version_for_height(uint64_t height) const; - bool add_new_block(const Block& bl_, block_verification_context& bvc); - bool reset_and_set_genesis_block(const Block& b); - bool create_block_template(Block& b, const AccountPublicAddress& miner_address, difficulty_type& di, uint32_t& height, const blobdata& ex_nonce); - bool have_block(const crypto::hash& id); - size_t get_total_transactions(); - bool get_short_chain_history(std::list& ids); - bool find_blockchain_supplement(const std::list& qblock_ids, uint64_t& starter_offset); // !!!! - bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY_request& resp); - bool find_blockchain_supplement(const std::list& qblock_ids, std::list>>& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); - bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp); - bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res); - bool get_backward_blocks_sizes(size_t from_height, std::vector& sz, size_t count); - bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs); - bool check_tx_inputs(const Transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, BlockInfo* tail = 0); - uint64_t get_current_comulative_blocksize_limit(); - bool is_storing_blockchain(){return m_is_blockchain_storing;} - uint64_t block_difficulty(size_t i); - bool getPoolChanges(const crypto::hash& tailBlockId, const std::vector& knownTxsIds, - std::vector& addedTxs, std::vector& deletedTxsIds); - void getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, - std::vector& deletedTxsIds); - bool getBlockContainingTx(const crypto::hash& txId, crypto::hash& blockId, uint64_t& blockHeight); - bool getAlreadyGeneratedCoins(const crypto::hash& hash, uint64_t& generatedCoins); - bool getBlockSize(const crypto::hash& hash, size_t& size); - bool getMultisigOutputReference(const TransactionInputMultisignature& txInMultisig, std::pair& outputReference); - - template bool scan_outputkeys_for_indexes(const TransactionInputToKey& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height = NULL); - - template - bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) { - std::lock_guard lk(m_blockchain_lock); - - for (const auto& bl_id : block_ids) { - uint64_t height = 0; - if (!m_blockIndex.getBlockHeight(bl_id, height)) { - missed_bs.push_back(bl_id); - } else { - if (!(height < m_blocks.size())) { logger(Logging::ERROR, Logging::BRIGHT_RED) << "Internal error: bl_id=" << Common::podToHex(bl_id) - << " have index record with offset=" << height << ", bigger then m_blocks.size()=" << m_blocks.size(); return false; } - blocks.push_back(m_blocks[height].bl); - } - } - - return true; - } - - template - void getBlockchainTransactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) { - std::lock_guard bcLock(m_blockchain_lock); - - for (const auto& tx_id : txs_ids) { - auto it = m_transactionMap.find(tx_id); - if (it == m_transactionMap.end()) { - missed_txs.push_back(tx_id); - } else { - txs.push_back(transactionByIndex(it->second).tx); - } - } - } - - template - void get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs, bool checkTxPool = false) { - if (checkTxPool){ - std::lock_guard txLock(m_tx_pool); - - getBlockchainTransactions(txs_ids, txs, missed_txs); - - auto poolTxIds = std::move(missed_txs); - missed_txs.clear(); - m_tx_pool.getTransactions(poolTxIds, txs, missed_txs); - - } else { - getBlockchainTransactions(txs_ids, txs, missed_txs); - } - } - - //debug functions - void print_blockchain(uint64_t start_index, uint64_t end_index); - void print_blockchain_index(); - void print_blockchain_outs(const std::string& file); - - private: - struct TransactionEntry { - Transaction tx; - std::vector m_global_output_indexes; - - template void serialize(archive_t & ar, unsigned int version); - - BEGIN_SERIALIZE_OBJECT() - FIELD(tx) - FIELD(m_global_output_indexes) - END_SERIALIZE() - }; - - struct BlockEntry { - Block bl; - uint32_t height; - uint64_t block_cumulative_size; - difficulty_type cumulative_difficulty; - uint64_t already_generated_coins; - std::vector transactions; - - template void serialize(Archive& archive, unsigned int version); - - BEGIN_SERIALIZE_OBJECT() - FIELD(bl) - VARINT_FIELD(height) - VARINT_FIELD(block_cumulative_size) - VARINT_FIELD(cumulative_difficulty) - VARINT_FIELD(already_generated_coins) - FIELD(transactions) - END_SERIALIZE() - }; - - struct TransactionIndex { - uint32_t block; - uint16_t transaction; - - template void serialize(Archive& archive, unsigned int version); - }; - - struct MultisignatureOutputUsage { - TransactionIndex transactionIndex; - uint16_t outputIndex; - bool isUsed; - - template void serialize(Archive& archive, unsigned int version); - }; - - typedef google::sparse_hash_set key_images_container; - typedef std::unordered_map blocks_ext_by_hash; - typedef google::sparse_hash_map>> outputs_container; //crypto::hash - tx hash, size_t - index of out in transaction - typedef std::map> MultisignatureOutputsContainer; - - const Currency& m_currency; - tx_memory_pool& m_tx_pool; - std::recursive_mutex m_blockchain_lock; // TODO: add here reader/writer lock - crypto::cn_context m_cn_context; - tools::ObserverManager m_observerManager; - - key_images_container m_spent_keys; - size_t m_current_block_cumul_sz_limit; - blocks_ext_by_hash m_alternative_chains; // crypto::hash -> block_extended_info - outputs_container m_outputs; - - std::string m_config_folder; - checkpoints m_checkpoints; - std::atomic m_is_in_checkpoint_zone; - std::atomic m_is_blockchain_storing; - - typedef SwappedVector Blocks; - typedef std::unordered_map BlockMap; - typedef std::unordered_map TransactionMap; - typedef BasicUpgradeDetector UpgradeDetector; - - friend class BlockCacheSerializer; - - Blocks m_blocks; - CryptoNote::BlockIndex m_blockIndex; - TransactionMap m_transactionMap; - MultisignatureOutputsContainer m_multisignatureOutputs; - UpgradeDetector m_upgradeDetector; - - Logging::LoggerRef logger; - - bool storeCache(); - bool switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain); - bool handle_alternative_block(const Block& b, const crypto::hash& id, block_verification_context& bvc); - difficulty_type get_next_difficulty_for_alternative_chain(const std::list& alt_chain, BlockEntry& bei); - bool prevalidate_miner_transaction(const Block& b, uint64_t height); - bool validate_miner_transaction(const Block& b, uint64_t height, size_t cumulativeBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, uint64_t& reward, int64_t& emissionChange); - bool validate_transaction(const Block& b, uint64_t height, const Transaction& tx); - bool rollback_blockchain_switching(std::list& original_chain, size_t rollback_height); - bool get_last_n_blocks_sizes(std::vector& sz, size_t count); - bool add_out_to_get_random_outs(std::vector>& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount& result_outs, uint64_t amount, size_t i); - bool is_tx_spendtime_unlocked(uint64_t unlock_time); - size_t find_end_of_allowed_index(const std::vector>& amount_outs); - bool check_block_timestamp_main(const Block& b); - bool check_block_timestamp(std::vector timestamps, const Block& b); - uint64_t get_adjusted_time(); - bool complete_timestamps_vector(uint64_t start_height, std::vector& timestamps); - bool checkBlockVersion(const Block& b, const crypto::hash& blockHash); - bool checkParentBlockSize(const Block& b, const crypto::hash& blockHash); - bool checkCumulativeBlockSize(const crypto::hash& blockId, size_t cumulativeBlockSize, uint64_t height); - bool getBlockCumulativeSize(const Block& block, size_t& cumulativeSize); - bool update_next_comulative_size_limit(); - bool check_tx_input(const TransactionInputToKey& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t* pmax_related_block_height = NULL); - bool check_tx_inputs(const Transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height = NULL); - bool check_tx_inputs(const Transaction& tx, uint64_t* pmax_used_block_height = NULL); - bool have_tx_keyimg_as_spent(const crypto::key_image &key_im); - const TransactionEntry& transactionByIndex(TransactionIndex index); - bool pushBlock(const Block& blockData, block_verification_context& bvc); - bool pushBlock(BlockEntry& block); - void popBlock(const crypto::hash& blockHash); - bool pushTransaction(BlockEntry& block, const crypto::hash& transactionHash, TransactionIndex transactionIndex); - void popTransaction(const Transaction& transaction, const crypto::hash& transactionHash); - void popTransactions(const BlockEntry& block, const crypto::hash& minerTransactionHash); - bool validateInput(const TransactionInputMultisignature& input, const crypto::hash& transactionHash, const crypto::hash& transactionPrefixHash, const std::vector& transactionSignatures); - - friend class LockedBlockchainStorage; - }; - - class LockedBlockchainStorage: boost::noncopyable { - public: - - LockedBlockchainStorage(blockchain_storage& bc) - : m_bc(bc), m_lock(bc.m_blockchain_lock) {} - - blockchain_storage* operator -> () { - return &m_bc; - } - - private: - - blockchain_storage& m_bc; - std::lock_guard m_lock; - }; - - template bool blockchain_storage::scan_outputkeys_for_indexes(const TransactionInputToKey& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height) { - std::lock_guard lk(m_blockchain_lock); - auto it = m_outputs.find(tx_in_to_key.amount); - if (it == m_outputs.end() || !tx_in_to_key.keyOffsets.size()) - return false; - - std::vector absolute_offsets = relative_output_offsets_to_absolute(tx_in_to_key.keyOffsets); - std::vector>& amount_outs_vec = it->second; - size_t count = 0; - for (uint64_t i : absolute_offsets) { - if(i >= amount_outs_vec.size() ) { - logger(Logging::INFO) << "Wrong index in transaction inputs: " << i << ", expected maximum " << amount_outs_vec.size() - 1; - return false; - } - - //auto tx_it = m_transactionMap.find(amount_outs_vec[i].first); - //if (!(tx_it != m_transactionMap.end())) { logger(ERROR, BRIGHT_RED) << "Wrong transaction id in output indexes: " << Common::podToHex(amount_outs_vec[i].first); return false; } - - const TransactionEntry& tx = transactionByIndex(amount_outs_vec[i].first); - - if (!(amount_outs_vec[i].second < tx.tx.vout.size())) { - logger(Logging::ERROR, Logging::BRIGHT_RED) - << "Wrong index in transaction outputs: " - << amount_outs_vec[i].second << ", expected less then " - << tx.tx.vout.size(); - return false; - } - - if (!vis.handle_output(tx.tx, tx.tx.vout[amount_outs_vec[i].second], amount_outs_vec[i].second)) { - logger(Logging::INFO) << "Failed to handle_output for output no = " << count << ", with absolute offset " << i; - return false; - } - - if(count++ == absolute_offsets.size()-1 && pmax_related_block_height) { - if (*pmax_related_block_height < amount_outs_vec[i].first.block) { - *pmax_related_block_height = amount_outs_vec[i].first.block; - } - } - } - - return true; - } -} - -#include "cryptonote_boost_serialization.h" -#include "blockchain_storage_boost_serialization.h" diff --git a/src/cryptonote_core/blockchain_storage_boost_serialization.h b/src/cryptonote_core/blockchain_storage_boost_serialization.h deleted file mode 100644 index af526117..00000000 --- a/src/cryptonote_core/blockchain_storage_boost_serialization.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once -/* -namespace boost -{ - namespace serialization - { - - - template - void serialize(archive_t & ar, CryptoNote::blockchain_storage::transaction_chain_entry& te, const unsigned int version) - { - ar & te.tx; - ar & te.m_keeper_block_height; - ar & te.m_blob_size; - ar & te.m_global_output_indexes; - } - - template - void serialize(archive_t & ar, CryptoNote::blockchain_storage::block_extended_info& ei, const unsigned int version) - { - ar & ei.bl; - ar & ei.height; - ar & ei.cumulative_difficulty; - ar & ei.block_cumulative_size; - ar & ei.already_generated_coins; - } - - } -} -*/ diff --git a/src/cryptonote_core/cryptonote_basic.cpp b/src/cryptonote_core/cryptonote_basic.cpp deleted file mode 100755 index 81d12347..00000000 --- a/src/cryptonote_core/cryptonote_basic.cpp +++ /dev/null @@ -1,321 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "cryptonote_basic.h" -#include "../Common/StreamTools.h" -#include "../Common/StringTools.h" - -using Common::IInputStream; -using Common::IOutputStream; -using Common::read; -using Common::readVarint; -using Common::toString; - -namespace CryptoNote { - -void TransactionInputGenerate::serialize(IOutputStream& out) const { - writeVarint(out, height); -} - -TransactionInputGenerate TransactionInputGenerate::deserialize(IInputStream& in) { - TransactionInputGenerate input; - readVarint(in, input.height); - return input; -} - -void TransactionInputToKey::serialize(IOutputStream& out) const { - writeVarint(out, amount); - writeVarint(out, keyOffsets.size()); - for (uint64_t outputIndex : keyOffsets) { - writeVarint(out, outputIndex); - } - - write(out, &keyImage, sizeof(keyImage)); -} - -TransactionInputToKey TransactionInputToKey::deserialize(IInputStream& in) { - TransactionInputToKey input; - readVarint(in, input.amount); - input.keyOffsets.resize(readVarint(in)); - for (uint64_t& outputIndex : input.keyOffsets) { - readVarint(in, outputIndex); - } - - read(in, &input.keyImage, sizeof(input.keyImage)); - return input; -} - -void TransactionInputMultisignature::serialize(IOutputStream& out) const { - writeVarint(out, amount); - writeVarint(out, signatures); - writeVarint(out, outputIndex); -} - -TransactionInputMultisignature TransactionInputMultisignature::deserialize(IInputStream& in) { - TransactionInputMultisignature input; - readVarint(in, input.amount); - readVarint(in, input.signatures); - readVarint(in, input.outputIndex); - return input; -} - -void TransactionOutputToKey::serialize(IOutputStream& out) const { - write(out, &key, sizeof(key)); -} - -TransactionOutputToKey TransactionOutputToKey::deserialize(IInputStream& in) { - TransactionOutputToKey output; - read(in, &output.key, sizeof(output.key)); - return output; -} - -void TransactionOutputMultisignature::serialize(IOutputStream& out) const { - writeVarint(out, keys.size()); - for (const crypto::public_key& key : keys) { - write(out, &key, sizeof(key)); - } - - writeVarint(out, requiredSignatures); -} - -TransactionOutputMultisignature TransactionOutputMultisignature::deserialize(IInputStream& in) { - TransactionOutputMultisignature output; - output.keys.resize(readVarint(in)); - for (crypto::public_key& key : output.keys) { - read(in, &key, sizeof(key)); - } - - readVarint(in, output.requiredSignatures); - if (output.requiredSignatures > output.keys.size()) { - throw std::runtime_error("TransactionOutputMultisignature::deserialize"); - } - - return output; -} - -void TransactionOutput::serialize(IOutputStream& out) const { - writeVarint(out, amount); - if (target.type() == typeid(TransactionOutputToKey)) { - write(out, static_cast(2)); - boost::get(target).serialize(out); - } else { - write(out, static_cast(3)); - boost::get(target).serialize(out); - } -} - -TransactionOutput TransactionOutput::deserialize(IInputStream& in) { - TransactionOutput output; - readVarint(in, output.amount); - uint8_t targetType = read(in); - if (targetType == 2) { - output.target = TransactionOutputToKey::deserialize(in); - } else if (targetType == 3) { - output.target = TransactionOutputMultisignature::deserialize(in); - } else { - throw std::runtime_error("TransactionOutput::deserialize"); - } - - return output; -} - -void Transaction::serialize(IOutputStream& out) const { - writeVarint(out, version); - writeVarint(out, unlockTime); - writeVarint(out, vin.size()); - for (const TransactionInput& input : vin) { - if (input.type() == typeid(TransactionInputGenerate)) { - write(out, static_cast(255)); - boost::get(input).serialize(out); - } else if (input.type() == typeid(TransactionInputToKey)) { - write(out, static_cast(2)); - boost::get(input).serialize(out); - } else { - write(out, static_cast(3)); - boost::get(input).serialize(out); - } - } - - writeVarint(out, vout.size()); - for (const TransactionOutput& output : vout) { - output.serialize(out); - } - - writeVarint(out, extra.size()); - write(out, extra); - std::size_t signatureCount = 0; - for (const std::vector& inputSignatures : signatures) { - signatureCount += inputSignatures.size(); - } - - for (const std::vector& inputSignatures : signatures) { - for (const crypto::signature& signature : inputSignatures) { - write(out, &signature, sizeof(signature)); - } - } -} - -Transaction Transaction::deserialize(IInputStream& in) { - Transaction transaction; - readVarint(in, transaction.version); - if (transaction.version != CURRENT_TRANSACTION_VERSION) { - throw std::runtime_error("Transaction::deserialize"); - } - - readVarint(in, transaction.unlockTime); - transaction.vin.resize(readVarint(in)); - for (TransactionInput& input : transaction.vin) { - uint8_t inputType = read(in); - if (inputType == 255) { - input = TransactionInputGenerate::deserialize(in); - } else if (inputType == 2) { - input = TransactionInputToKey::deserialize(in); - } else if (inputType == 3) { - input = TransactionInputMultisignature::deserialize(in); - } else { - throw std::runtime_error("Transaction::deserialize"); - } - } - - transaction.vout.resize(readVarint(in)); - for (TransactionOutput& output : transaction.vout) { - output = TransactionOutput::deserialize(in); - } - - transaction.extra.resize(readVarint(in)); - read(in, transaction.extra.data(), transaction.extra.size()); - transaction.signatures.resize(transaction.vin.size()); - for (std::size_t i = 0; i < transaction.vin.size(); ++i) { - std::size_t signatureCount; - if (transaction.vin[i].type() == typeid(TransactionInputGenerate)) { - signatureCount = 0; - } else if (transaction.vin[i].type() == typeid(TransactionInputToKey)) { - signatureCount = boost::get(transaction.vin[i]).keyOffsets.size(); - } else { - signatureCount = boost::get(transaction.vin[i]).signatures; - } - - transaction.signatures[i].resize(signatureCount); - for (crypto::signature& signature : transaction.signatures[i]) { - read(in, &signature, sizeof(signature)); - } - } - - return transaction; -} - -void Block::serialize(IOutputStream& out) const { - writeVarint(out, majorVersion); - writeVarint(out, minorVersion); - if (majorVersion == BLOCK_MAJOR_VERSION_1) { - writeVarint(out, timestamp); - write(out, &prevId, sizeof(prevId)); - write(out, nonce); - } else { - write(out, &prevId, sizeof(prevId)); - writeVarint(out, parentBlock.majorVersion); - writeVarint(out, parentBlock.minorVersion); - writeVarint(out, timestamp); - write(out, &parentBlock.prevId, sizeof(parentBlock.prevId)); - write(out, nonce); - writeVarint(out, parentBlock.numberOfTransactions); - for (const crypto::hash& hash : parentBlock.minerTxBranch) { - write(out, &hash, sizeof(hash)); - } - - parentBlock.minerTx.serialize(out); - for (const crypto::hash& hash : parentBlock.blockchainBranch) { - write(out, &hash, sizeof(hash)); - } - } - - minerTx.serialize(out); - writeVarint(out, txHashes.size()); - for (const crypto::hash& hash : txHashes) { - write(out, &hash, sizeof(hash)); - } -} - -Block Block::deserialize(IInputStream& in) { - Block block; - readVarint(in, block.majorVersion); - if (block.majorVersion == BLOCK_MAJOR_VERSION_1) { - readVarint(in, block.minorVersion); - if (block.minorVersion != BLOCK_MINOR_VERSION_0 && block.minorVersion != BLOCK_MINOR_VERSION_1) { - throw std::runtime_error("Invalid block minor version (" + toString(static_cast(block.minorVersion)) + ") for major version 1"); - } - - readVarint(in, block.timestamp); - read(in, &block.prevId, sizeof(block.prevId)); - read(in, block.nonce); - } else if (block.majorVersion == BLOCK_MAJOR_VERSION_2) { - readVarint(in, block.minorVersion); - if (block.minorVersion != BLOCK_MINOR_VERSION_0) { - throw std::runtime_error("Invalid block minor version (" + toString(static_cast(block.minorVersion)) + ") for major version 2"); - } - - read(in, &block.prevId, sizeof(block.prevId)); - readVarint(in, block.parentBlock.majorVersion); - if (block.parentBlock.majorVersion != BLOCK_MAJOR_VERSION_1) { - throw std::runtime_error("Invalid parent block major version (" + toString(static_cast(block.parentBlock.majorVersion)) + ')'); - } - - readVarint(in, block.parentBlock.minorVersion); - if (block.parentBlock.minorVersion != BLOCK_MINOR_VERSION_0) { - throw std::runtime_error("Invalid parent block minor version (" + toString(static_cast(block.parentBlock.minorVersion)) + ')'); - } - - - readVarint(in, block.timestamp); - read(in, &block.parentBlock.prevId, sizeof(block.parentBlock.prevId)); - read(in, block.nonce); - readVarint(in, block.parentBlock.numberOfTransactions); - - block.parentBlock.minerTxBranch.resize(crypto::tree_depth(block.parentBlock.numberOfTransactions)); - for (crypto::hash& hash : block.parentBlock.minerTxBranch) { - read(in, &hash, sizeof(hash)); - } - - block.parentBlock.minerTx = Transaction::deserialize(in); - tx_extra_merge_mining_tag mergedMiningTag; - if (!get_mm_tag_from_extra(block.parentBlock.minerTx.extra, mergedMiningTag)) { - throw std::runtime_error("Cannot get merged mining tag"); - } - - if (mergedMiningTag.depth > 8 * sizeof(crypto::hash)) { - throw std::runtime_error("Invalid merged mining tag depth (" + toString(mergedMiningTag.depth) + ')'); - } - - - block.parentBlock.blockchainBranch.resize(mergedMiningTag.depth); - for (crypto::hash& hash : block.parentBlock.blockchainBranch) { - read(in, &hash, sizeof(hash)); - } - } else { - throw std::runtime_error("Invalid block major version (" + toString(static_cast(block.majorVersion)) + ')'); - } - - block.minerTx = Transaction::deserialize(in); - block.txHashes.resize(readVarint(in)); - for (crypto::hash& hash : block.txHashes) { - read(in, &hash, sizeof(hash)); - } - - return block; -} - -} diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h deleted file mode 100644 index 17d533c5..00000000 --- a/src/cryptonote_core/cryptonote_basic.h +++ /dev/null @@ -1,451 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include -#include - -#include -#include - -#include "crypto/crypto.h" -#include "crypto/hash.h" -#include "cryptonote_core/tx_extra.h" -#include "serialization/binary_archive.h" -#include "serialization/crypto.h" -#include "serialization/json_archive.h" -#include "serialization/serialization.h" -#include "serialization/variant.h" -#include "cryptonote_config.h" - -namespace Common { -class IInputStream; -class IOutputStream; -} - -namespace CryptoNote { - class account_base; - struct account_keys; - struct Block; - struct Transaction; - struct tx_extra_merge_mining_tag; - - // Implemented in cryptonote_format_utils.cpp - bool get_transaction_hash(const Transaction& t, crypto::hash& res); - bool get_mm_tag_from_extra(const std::vector& tx, tx_extra_merge_mining_tag& mm_tag); - - const static crypto::hash null_hash = boost::value_initialized(); - const static crypto::public_key null_pkey = boost::value_initialized(); - - /* inputs */ - - struct TransactionInputGenerate { - uint32_t height; - - BEGIN_SERIALIZE_OBJECT() - VARINT_FIELD(height); - END_SERIALIZE() - - void serialize(Common::IOutputStream& out) const; - static TransactionInputGenerate deserialize(Common::IInputStream& in); - }; - - struct TransactionInputToKey { - uint64_t amount; - std::vector keyOffsets; - crypto::key_image keyImage; // double spending protection - - BEGIN_SERIALIZE_OBJECT() - VARINT_FIELD(amount); - FIELD(keyOffsets); - FIELD(keyImage); - END_SERIALIZE() - - void serialize(Common::IOutputStream& out) const; - static TransactionInputToKey deserialize(Common::IInputStream& in); - }; - - struct TransactionInputMultisignature { - uint64_t amount; - uint32_t signatures; - uint64_t outputIndex; - - BEGIN_SERIALIZE_OBJECT() - VARINT_FIELD(amount); - VARINT_FIELD(signatures); - VARINT_FIELD(outputIndex); - END_SERIALIZE() - - void serialize(Common::IOutputStream& out) const; - static TransactionInputMultisignature deserialize(Common::IInputStream& in); - }; - - /* outputs */ - - struct TransactionOutputToKey { - TransactionOutputToKey() { } - TransactionOutputToKey(const crypto::public_key &_key) : key(_key) { } - crypto::public_key key; - - void serialize(Common::IOutputStream& out) const; - static TransactionOutputToKey deserialize(Common::IInputStream& in); - }; - - struct TransactionOutputMultisignature { - std::vector keys; - uint32_t requiredSignatures; - - BEGIN_SERIALIZE_OBJECT() - FIELD(keys); - VARINT_FIELD(requiredSignatures); - END_SERIALIZE() - - void serialize(Common::IOutputStream& out) const; - static TransactionOutputMultisignature deserialize(Common::IInputStream& in); - }; - - struct TransactionInputToScript { - BEGIN_SERIALIZE_OBJECT() - END_SERIALIZE() - }; - - struct TransactionInputToScriptHash { - BEGIN_SERIALIZE_OBJECT() - END_SERIALIZE() - }; - - struct TransactionOutputToScript { - BEGIN_SERIALIZE_OBJECT() - END_SERIALIZE() - }; - - struct TransactionOutputToScriptHash { - BEGIN_SERIALIZE_OBJECT() - END_SERIALIZE() - }; - - typedef boost::variant< - TransactionInputGenerate, - TransactionInputToScript, - TransactionInputToScriptHash, - TransactionInputToKey, - TransactionInputMultisignature> TransactionInput; - - typedef boost::variant< - TransactionOutputToScript, - TransactionOutputToScriptHash, - TransactionOutputToKey, - TransactionOutputMultisignature> TransactionOutputTarget; - - struct TransactionOutput { - uint64_t amount; - TransactionOutputTarget target; - - BEGIN_SERIALIZE_OBJECT() - VARINT_FIELD(amount); - FIELD(target); - END_SERIALIZE() - - void serialize(Common::IOutputStream& out) const; - static TransactionOutput deserialize(Common::IInputStream& in); - }; - - struct TransactionPrefix { - // tx information - uint8_t version; - uint64_t unlockTime; //number of block (or time), used as a limitation like: spend this tx not early then block/time - - std::vector vin; - std::vector vout; - //extra - std::vector extra; - - BEGIN_SERIALIZE() - VARINT_FIELD(version); - if(CURRENT_TRANSACTION_VERSION < version) { - return false; - } - VARINT_FIELD(unlockTime); - FIELD(vin); - FIELD(vout); - FIELD(extra); - END_SERIALIZE() - - protected: - TransactionPrefix() {} - }; - - struct Transaction: public TransactionPrefix { - std::vector > signatures; //count signatures always the same as inputs count - - Transaction() { - clear(); - } - - void clear() { - version = 0; - unlockTime = 0; - vin.clear(); - vout.clear(); - extra.clear(); - signatures.clear(); - } - - BEGIN_SERIALIZE_OBJECT() - FIELDS(*static_cast(this)) - - ar.tag("signatures"); - ar.begin_array(); - PREPARE_CUSTOM_VECTOR_SERIALIZATION(vin.size(), signatures); - bool signatures_not_expected = signatures.empty(); - if (!signatures_not_expected && vin.size() != signatures.size()) { - return false; - } - - for (size_t i = 0; i < vin.size(); ++i) { - size_t signatureSize = getSignatureSize(vin[i]); - if (signatures_not_expected) { - if (0 == signatureSize) { - continue; - } else { - return false; - } - } - - PREPARE_CUSTOM_VECTOR_SERIALIZATION(signatureSize, signatures[i]); - if (signatureSize != signatures[i].size()) { - return false; - } - - FIELDS(signatures[i]); - - if (vin.size() - i > 1) { - ar.delimit_array(); - } - } - ar.end_array(); - END_SERIALIZE() - - static size_t getSignatureSize(const TransactionInput& input) { - struct txin_signature_size_visitor : public boost::static_visitor { - size_t operator()(const TransactionInputGenerate& txin) const { return 0; } - size_t operator()(const TransactionInputToScript& txin) const { assert(false); return 0; } - size_t operator()(const TransactionInputToScriptHash& txin) const { assert(false); return 0; } - size_t operator()(const TransactionInputToKey& txin) const { return txin.keyOffsets.size();} - size_t operator()(const TransactionInputMultisignature& txin) const { return txin.signatures; } - }; - - return boost::apply_visitor(txin_signature_size_visitor(), input); - } - - void serialize(Common::IOutputStream& out) const; - static Transaction deserialize(Common::IInputStream& in); - }; - - struct ParentBlock { - uint8_t majorVersion; - uint8_t minorVersion; - crypto::hash prevId; - uint16_t numberOfTransactions; - std::vector minerTxBranch; - Transaction minerTx; - std::vector blockchainBranch; - }; - - struct ParentBlockSerializer { - ParentBlockSerializer(ParentBlock& parentBlock, uint64_t& timestamp, uint32_t& nonce, bool hashingSerialization, bool headerOnly) : - m_parentBlock(parentBlock), m_timestamp(timestamp), m_nonce(nonce), m_hashingSerialization(hashingSerialization), m_headerOnly(headerOnly) { - } - - BEGIN_SERIALIZE_OBJECT() - VARINT_FIELD_N("majorVersion", m_parentBlock.majorVersion); - if (BLOCK_MAJOR_VERSION_1 < m_parentBlock.majorVersion) { - return false; - } - VARINT_FIELD_N("minorVersion", m_parentBlock.minorVersion); - VARINT_FIELD_N("timestamp", m_timestamp); - FIELD_N("prevId", m_parentBlock.prevId); - FIELD_N("nonce", m_nonce); - - if (m_hashingSerialization) { - crypto::hash minerTxHash; - if (!get_transaction_hash(m_parentBlock.minerTx, minerTxHash)) { - return false; - } - - crypto::hash merkleRoot; - crypto::tree_hash_from_branch(m_parentBlock.minerTxBranch.data(), m_parentBlock.minerTxBranch.size(), minerTxHash, 0, merkleRoot); - - FIELD(merkleRoot); - } - - VARINT_FIELD_N("numberOfTransactions", m_parentBlock.numberOfTransactions); - if (m_parentBlock.numberOfTransactions < 1) { - return false; - } - - if (!m_headerOnly) { - ar.tag("minerTxBranch"); - ar.begin_array(); - size_t branchSize = crypto::tree_depth(m_parentBlock.numberOfTransactions); - PREPARE_CUSTOM_VECTOR_SERIALIZATION(branchSize, const_cast(m_parentBlock).minerTxBranch); - if (m_parentBlock.minerTxBranch.size() != branchSize) { - return false; - } - for (size_t i = 0; i < branchSize; ++i) { - FIELDS(m_parentBlock.minerTxBranch[i]); - if (i + 1 < branchSize) { - ar.delimit_array(); - } - } - ar.end_array(); - - FIELD(m_parentBlock.minerTx); - - tx_extra_merge_mining_tag mmTag; - if (!get_mm_tag_from_extra(m_parentBlock.minerTx.extra, mmTag)) { - return false; - } - - if (mmTag.depth > 8 * sizeof(crypto::hash)) { - return false; - } - - ar.tag("blockchainBranch"); - ar.begin_array(); - PREPARE_CUSTOM_VECTOR_SERIALIZATION(mmTag.depth, const_cast(m_parentBlock).blockchainBranch); - if (mmTag.depth != m_parentBlock.blockchainBranch.size()) { - return false; - } - for (size_t i = 0; i < mmTag.depth; ++i) { - FIELDS(m_parentBlock.blockchainBranch[i]); - if (i + 1 < mmTag.depth) { - ar.delimit_array(); - } - } - ar.end_array(); - } - END_SERIALIZE() - - ParentBlock& m_parentBlock; - uint64_t& m_timestamp; - uint32_t& m_nonce; - bool m_hashingSerialization; - bool m_headerOnly; - }; - - // Implemented below - inline ParentBlockSerializer makeParentBlockSerializer(const Block& b, bool hashingSerialization, bool headerOnly); - - struct BlockHeader { - uint8_t majorVersion; - uint8_t minorVersion; - uint32_t nonce; - uint64_t timestamp; - crypto::hash prevId; - - BEGIN_SERIALIZE() - VARINT_FIELD(majorVersion) - if (majorVersion > BLOCK_MAJOR_VERSION_2) { - return false; - } - VARINT_FIELD(minorVersion) - if (majorVersion == BLOCK_MAJOR_VERSION_1) { - VARINT_FIELD(timestamp); - FIELD(prevId); - FIELD(nonce); - } else if (majorVersion == BLOCK_MAJOR_VERSION_2) { - FIELD(prevId); - } else { - return false; - } - END_SERIALIZE() - }; - - struct Block: public BlockHeader { - ParentBlock parentBlock; - - Transaction minerTx; - std::vector txHashes; - - BEGIN_SERIALIZE_OBJECT() - FIELDS(*static_cast(this)); - if (majorVersion == BLOCK_MAJOR_VERSION_2) { - auto serializer = makeParentBlockSerializer(*this, false, false); - FIELD_N("parentBlock", serializer); - } - FIELD(minerTx); - FIELD(txHashes); - END_SERIALIZE() - - void serialize(Common::IOutputStream& out) const; - static Block deserialize(Common::IInputStream& in); - }; - - inline ParentBlockSerializer makeParentBlockSerializer(const Block& b, bool hashingSerialization, bool headerOnly) { - Block& blockRef = const_cast(b); - return ParentBlockSerializer(blockRef.parentBlock, blockRef.timestamp, blockRef.nonce, hashingSerialization, headerOnly); - } - - struct AccountPublicAddress { - crypto::public_key m_spendPublicKey; - crypto::public_key m_viewPublicKey; - - BEGIN_SERIALIZE_OBJECT() - FIELD(m_spendPublicKey); - FIELD(m_viewPublicKey); - END_SERIALIZE() - }; - - struct KeyPair { - crypto::public_key pub; - crypto::secret_key sec; - - static KeyPair generate() { - KeyPair k; - generate_keys(k.pub, k.sec); - return k; - } - }; -} - -BLOB_SERIALIZER(CryptoNote::TransactionOutputToKey); - -VARIANT_TAG(binary_archive, CryptoNote::TransactionInputGenerate, 0xff); -VARIANT_TAG(binary_archive, CryptoNote::TransactionInputToScript, 0x0); -VARIANT_TAG(binary_archive, CryptoNote::TransactionInputToScriptHash, 0x1); -VARIANT_TAG(binary_archive, CryptoNote::TransactionInputToKey, 0x2); -VARIANT_TAG(binary_archive, CryptoNote::TransactionInputMultisignature, 0x3); -VARIANT_TAG(binary_archive, CryptoNote::TransactionOutputToScript, 0x0); -VARIANT_TAG(binary_archive, CryptoNote::TransactionOutputToScriptHash, 0x1); -VARIANT_TAG(binary_archive, CryptoNote::TransactionOutputToKey, 0x2); -VARIANT_TAG(binary_archive, CryptoNote::TransactionOutputMultisignature, 0x3); -VARIANT_TAG(binary_archive, CryptoNote::Transaction, 0xcc); -VARIANT_TAG(binary_archive, CryptoNote::Block, 0xbb); - -VARIANT_TAG(json_archive, CryptoNote::TransactionInputGenerate, "generate"); -VARIANT_TAG(json_archive, CryptoNote::TransactionInputToScript, "script"); -VARIANT_TAG(json_archive, CryptoNote::TransactionInputToScriptHash, "scripthash"); -VARIANT_TAG(json_archive, CryptoNote::TransactionInputToKey, "key"); -VARIANT_TAG(json_archive, CryptoNote::TransactionInputMultisignature, "multisignature"); -VARIANT_TAG(json_archive, CryptoNote::TransactionOutputToScript, "script"); -VARIANT_TAG(json_archive, CryptoNote::TransactionOutputToScriptHash, "scripthash"); -VARIANT_TAG(json_archive, CryptoNote::TransactionOutputToKey, "key"); -VARIANT_TAG(json_archive, CryptoNote::TransactionOutputMultisignature, "multisignature"); -VARIANT_TAG(json_archive, CryptoNote::Transaction, "Transaction"); -VARIANT_TAG(json_archive, CryptoNote::Block, "Block"); diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp deleted file mode 100644 index de23c866..00000000 --- a/src/cryptonote_core/cryptonote_core.cpp +++ /dev/null @@ -1,622 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "cryptonote_core.h" -#include -#include -#include "../cryptonote_config.h" -#include "../Common/command_line.h" -#include "../Common/util.h" -#include "../crypto/crypto.h" -#include "../cryptonote_protocol/cryptonote_protocol_defs.h" -#include "../Logging/LoggerRef.h" -#include "../rpc/core_rpc_server_commands_defs.h" -#include "cryptonote_format_utils.h" -#include "cryptonote_stat_info.h" -#include "miner.h" -#undef ERROR - -using namespace Logging; -#include "cryptonote_core/CoreConfig.h" - -namespace CryptoNote { - -core::core(const Currency& currency, i_cryptonote_protocol* pprotocol, Logging::ILogger& logger) : -m_currency(currency), -logger(logger, "core"), -m_mempool(currency, m_blockchain_storage, m_timeProvider, logger), -m_blockchain_storage(currency, m_mempool, logger), -m_miner(new miner(currency, *this, logger)), -m_starter_message_showed(false) { - set_cryptonote_protocol(pprotocol); - m_blockchain_storage.addObserver(this); - m_mempool.addObserver(this); - } - //----------------------------------------------------------------------------------------------- - core::~core() { - m_blockchain_storage.removeObserver(this); -} - -void core::set_cryptonote_protocol(i_cryptonote_protocol* pprotocol) { - if (pprotocol) - m_pprotocol = pprotocol; - else - m_pprotocol = &m_protocol_stub; -} -//----------------------------------------------------------------------------------- -void core::set_checkpoints(checkpoints&& chk_pts) { - m_blockchain_storage.set_checkpoints(std::move(chk_pts)); -} -//----------------------------------------------------------------------------------- -void core::init_options(boost::program_options::options_description& /*desc*/) { -} - -bool core::handle_command_line(const boost::program_options::variables_map& vm) { - m_config_folder = command_line::get_arg(vm, command_line::arg_data_dir); - return true; -} - -bool core::is_ready() { - return !m_blockchain_storage.is_storing_blockchain(); -} - - -uint64_t core::get_current_blockchain_height() { - return m_blockchain_storage.get_current_blockchain_height(); -} - -bool core::get_blockchain_top(uint64_t& height, crypto::hash& top_id) { - top_id = m_blockchain_storage.get_tail_id(height); - return true; -} - -bool core::get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) { - return m_blockchain_storage.get_blocks(start_offset, count, blocks, txs); -} - -bool core::get_blocks(uint64_t start_offset, size_t count, std::list& blocks) { - return m_blockchain_storage.get_blocks(start_offset, count, blocks); -} -void core::getTransactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs, bool checkTxPool) { - m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs, checkTxPool); -} - -bool core::get_alternative_blocks(std::list& blocks) { - return m_blockchain_storage.get_alternative_blocks(blocks); -} - -size_t core::get_alternative_blocks_count() { - return m_blockchain_storage.get_alternative_blocks_count(); - } - //----------------------------------------------------------------------------------------------- - bool core::init(const CoreConfig& config, const MinerConfig& minerConfig, bool load_existing) { - m_config_folder = config.configFolder; - bool r = m_mempool.init(m_config_folder); - if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize memory pool"; return false; } - - r = m_blockchain_storage.init(m_config_folder, load_existing); - if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize blockchain storage"; return false; } - - r = m_miner->init(minerConfig); - if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize blockchain storage"; return false; } - - return load_state_data(); -} - -bool core::set_genesis_block(const Block& b) { - return m_blockchain_storage.reset_and_set_genesis_block(b); -} - -bool core::load_state_data() { - // may be some code later - return true; -} - -bool core::deinit() { - m_miner->stop(); - m_mempool.deinit(); - m_blockchain_storage.deinit(); - return true; -} - -bool core::handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block) { - tvc = boost::value_initialized(); - //want to process all transactions sequentially - std::lock_guard lk(m_incoming_tx_lock); - - if (tx_blob.size() > m_currency.maxTxSize()) { - logger(INFO) << "WRONG TRANSACTION BLOB, too big size " << tx_blob.size() << ", rejected"; - tvc.m_verifivation_failed = true; - return false; - } - - crypto::hash tx_hash = null_hash; - crypto::hash tx_prefixt_hash = null_hash; - Transaction tx; - - if (!parse_tx_from_blob(tx, tx_hash, tx_prefixt_hash, tx_blob)) { - logger(INFO) << "WRONG TRANSACTION BLOB, Failed to parse, rejected"; - tvc.m_verifivation_failed = true; - return false; - } - //std::cout << "!"<< tx.vin.size() << std::endl; - - if (!check_tx_syntax(tx)) { - logger(INFO) << "WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " syntax, rejected"; - tvc.m_verifivation_failed = true; - return false; - } - - if (!check_tx_semantic(tx, keeped_by_block)) { - logger(INFO) << "WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " semantic, rejected"; - tvc.m_verifivation_failed = true; - return false; - } - - bool r = add_new_tx(tx, tx_hash, tx_prefixt_hash, tx_blob.size(), tvc, keeped_by_block); - if (tvc.m_verifivation_failed) { - if (!tvc.m_tx_fee_too_small) { - logger(ERROR) << "Transaction verification failed: " << tx_hash; - } else { - logger(INFO) << "Transaction verification failed: " << tx_hash; - } - } else if (tvc.m_verifivation_impossible) { - logger(ERROR) << "Transaction verification impossible: " << tx_hash; - } - - if (tvc.m_added_to_pool) { - logger(DEBUGGING) << "tx added: " << tx_hash; - poolUpdated(); - } - - return r; -} - -bool core::get_stat_info(core_stat_info& st_inf) { - st_inf.mining_speed = m_miner->get_speed(); - st_inf.alternative_blocks = m_blockchain_storage.get_alternative_blocks_count(); - st_inf.blockchain_height = m_blockchain_storage.get_current_blockchain_height(); - st_inf.tx_pool_size = m_mempool.get_transactions_count(); - st_inf.top_block_id_str = Common::podToHex(m_blockchain_storage.get_tail_id()); - return true; -} - - -bool core::check_tx_semantic(const Transaction& tx, bool keeped_by_block) { - if (!tx.vin.size()) { - logger(ERROR) << "tx with empty inputs, rejected for tx id= " << get_transaction_hash(tx); - return false; - } - - if (!check_inputs_types_supported(tx)) { - logger(ERROR) << "unsupported input types for tx id= " << get_transaction_hash(tx); - return false; - } - - std::string errmsg; - if (!check_outs_valid(tx, &errmsg)) { - logger(ERROR) << "tx with invalid outputs, rejected for tx id= " << get_transaction_hash(tx) << ": " << errmsg; - return false; - } - - if (!check_money_overflow(tx)) { - logger(ERROR) << "tx have money overflow, rejected for tx id= " << get_transaction_hash(tx); - return false; - } - - uint64_t amount_in = 0; - get_inputs_money_amount(tx, amount_in); - uint64_t amount_out = get_outs_money_amount(tx); - - if (amount_in <= amount_out) { - logger(ERROR) << "tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << get_transaction_hash(tx); - return false; - } - - if (!keeped_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_comulative_blocksize_limit() - m_currency.minerTxBlobReservedSize()) { - logger(ERROR) << "transaction is too big " << get_object_blobsize(tx) << ", maximum allowed size is " << - (m_blockchain_storage.get_current_comulative_blocksize_limit() - m_currency.minerTxBlobReservedSize()); - return false; - } - - //check if tx use different key images - if (!check_tx_inputs_keyimages_diff(tx)) { - logger(ERROR) << "tx has a few inputs with identical keyimages"; - return false; - } - - if (!checkMultisignatureInputsDiff(tx)) { - logger(ERROR) << "tx has a few multisignature inputs with identical output indexes"; - return false; - } - - return true; -} - -bool core::check_tx_inputs_keyimages_diff(const Transaction& tx) { - std::unordered_set ki; - for (const auto& in : tx.vin) { - if (in.type() == typeid(TransactionInputToKey)) { - if (!ki.insert(boost::get(in).keyImage).second) - return false; - } - } - return true; -} - -size_t core::get_blockchain_total_transactions() { - return m_blockchain_storage.get_total_transactions(); -} - -//bool core::get_outs(uint64_t amount, std::list& pkeys) -//{ -// return m_blockchain_storage.get_outs(amount, pkeys); -//} - -bool core::add_new_tx(const Transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block) { - if (m_blockchain_storage.have_tx(tx_hash)) { - logger(TRACE) << "tx " << tx_hash << " is already in blockchain"; - return true; - } - - // It's not very good to lock on m_mempool here, because it's very hard to understand the order of locking - // tx_memory_pool::m_transactions_lock, blockchain_storage::m_blockchain_lock, and core::m_incoming_tx_lock - std::lock_guard lk(m_mempool); - - if (m_mempool.have_tx(tx_hash)) { - logger(TRACE) << "tx " << tx_hash << " is already in transaction pool"; - return true; - } - - return m_mempool.add_tx(tx, tx_hash, blob_size, tvc, keeped_by_block); -} - -bool core::get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint32_t& height, const blobdata& ex_nonce) { - return m_blockchain_storage.create_block_template(b, adr, diffic, height, ex_nonce); -} - -bool core::find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) { - return m_blockchain_storage.find_blockchain_supplement(qblock_ids, resp); -} - -bool core::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) { - return m_blockchain_storage.find_blockchain_supplement(qblock_ids, blocks, total_height, start_height, max_count); -} - -void core::print_blockchain(uint64_t start_index, uint64_t end_index) { - m_blockchain_storage.print_blockchain(start_index, end_index); -} - -void core::print_blockchain_index() { - m_blockchain_storage.print_blockchain_index(); -} - -void core::print_blockchain_outs(const std::string& file) { - m_blockchain_storage.print_blockchain_outs(file); -} - -bool core::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) { - return m_blockchain_storage.get_random_outs_for_amounts(req, res); -} - -bool core::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) { - return m_blockchain_storage.get_tx_outputs_gindexs(tx_id, indexs); -} - -void core::pause_mining() { - m_miner->pause(); -} - -void core::update_block_template_and_resume_mining() { - update_miner_block_template(); - m_miner->resume(); -} - -bool core::handle_block_found(Block& b) { - block_verification_context bvc = boost::value_initialized(); - handle_incoming_block(b, bvc, true, true); - - if (bvc.m_verifivation_failed) { - logger(ERROR) << "mined block failed verification"; - } - - return bvc.m_added_to_main_chain; -} - -void core::on_synchronized() { - m_miner->on_synchronized(); -} -//----------------------------------------------------------------------------------------------- -bool core::getPoolChanges(const crypto::hash& tailBlockId, const std::vector& knownTxsIds, - std::vector& addedTxs, std::vector& deletedTxsIds) { - return m_blockchain_storage.getPoolChanges(tailBlockId, knownTxsIds, addedTxs, deletedTxsIds); -} -//----------------------------------------------------------------------------------------------- -void core::getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, - std::vector& deletedTxsIds) { - m_blockchain_storage.getPoolChanges(knownTxsIds, addedTxs, deletedTxsIds); -} -//----------------------------------------------------------------------------------------------- -bool core::handle_incoming_block_blob(const blobdata& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block) { - if (block_blob.size() > m_currency.maxBlockBlobSize()) { - logger(INFO) << "WRONG BLOCK BLOB, too big size " << block_blob.size() << ", rejected"; - bvc.m_verifivation_failed = true; - return false; - } - - Block b; - if (!parse_and_validate_block_from_blob(block_blob, b)) { - logger(INFO) << "Failed to parse and validate new block"; - bvc.m_verifivation_failed = true; - return false; - } - - return handle_incoming_block(b, bvc, control_miner, relay_block); -} - -bool core::handle_incoming_block(const Block& b, block_verification_context& bvc, bool control_miner, bool relay_block) { - if (control_miner) { - pause_mining(); - } - - m_blockchain_storage.add_new_block(b, bvc); - - if (control_miner) { - update_block_template_and_resume_mining(); - } - - if (relay_block && bvc.m_added_to_main_chain) { - std::list missed_txs; - std::list txs; - m_blockchain_storage.get_transactions(b.txHashes, txs, missed_txs); - if (!missed_txs.empty() && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b)) { - logger(INFO) << "Block added, but it seems that reorganize just happened after that, do not relay this block"; - } else { - if (!(txs.size() == b.txHashes.size() && missed_txs.empty())) { - logger(ERROR, BRIGHT_RED) << "can't find some transactions in found block:" << - get_block_hash(b) << " txs.size()=" << txs.size() << ", b.txHashes.size()=" << b.txHashes.size() << ", missed_txs.size()" << missed_txs.size(); return false; - } - - NOTIFY_NEW_BLOCK::request arg; - arg.hop = 0; - arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height(); - bool r = block_to_blob(b, arg.b.block); - if (!(r)) { logger(ERROR, BRIGHT_RED) << "failed to serialize block"; return false; } - for (auto& tx : txs) { - arg.b.txs.push_back(t_serializable_object_to_blob(tx)); - } - - m_pprotocol->relay_block(arg); - } - } - - return true; -} - -crypto::hash core::get_tail_id() { - return m_blockchain_storage.get_tail_id(); -} - -size_t core::get_pool_transactions_count() { - return m_mempool.get_transactions_count(); -} - -bool core::have_block(const crypto::hash& id) { - return m_blockchain_storage.have_block(id); -} - -bool core::parse_tx_from_blob(Transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob) { - return parse_and_validate_tx_from_blob(blob, tx, tx_hash, tx_prefix_hash); -} - -bool core::check_tx_syntax(const Transaction& tx) { - return true; -} - -std::vector core::getPoolTransactions() { - std::list txs; - m_mempool.get_transactions(txs); - - std::vector result; - for (auto& tx : txs) { - result.emplace_back(std::move(tx)); - } - return result; -} - -bool core::get_short_chain_history(std::list& ids) { - return m_blockchain_storage.get_short_chain_history(ids); -} - -bool core::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) { - return m_blockchain_storage.handle_get_objects(arg, rsp); -} - -crypto::hash core::getBlockIdByHeight(uint64_t height) { - return m_blockchain_storage.get_block_id_by_height(height); -} - -bool core::getBlockByHash(const crypto::hash &h, Block &blk) { - return m_blockchain_storage.get_block_by_hash(h, blk); -} - -//void core::get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid) { -// m_blockchain_storage.get_all_known_block_ids(main, alt, invalid); -//} - -std::string core::print_pool(bool short_format) { - return m_mempool.print_pool(short_format); -} - -bool core::update_miner_block_template() { - m_miner->on_block_chain_update(); - return true; -} - -bool core::on_idle() { - if (!m_starter_message_showed) { - logger(INFO) << ENDL << "**********************************************************************" << ENDL - << "The daemon will start synchronizing with the network. It may take up to several hours." << ENDL - << ENDL - << "You can set the level of process detailization* through \"set_log \" command*, where is between 0 (no details) and 4 (very verbose)." << ENDL - << ENDL - << "Use \"help\" command to see the list of available commands." << ENDL - << ENDL - << "Note: in case you need to interrupt the process, use \"exit\" command. Otherwise, the current progress won't be saved." << ENDL - << "**********************************************************************"; - m_starter_message_showed = true; - } - - m_miner->on_idle(); - m_mempool.on_idle(); - return true; -} - -bool core::addObserver(ICoreObserver* observer) { - return m_observerManager.add(observer); -} - -bool core::removeObserver(ICoreObserver* observer) { - return m_observerManager.remove(observer); -} - -void core::blockchainUpdated() { - m_observerManager.notify(&ICoreObserver::blockchainUpdated); -} - - void core::txDeletedFromPool() { - poolUpdated(); - } - - void core::poolUpdated() { - m_observerManager.notify(&ICoreObserver::poolUpdated); - } - - bool core::queryBlocks(const std::list& knownBlockIds, uint64_t timestamp, - uint64_t& resStartHeight, uint64_t& resCurrentHeight, uint64_t& resFullOffset, std::list& entries) { - - LockedBlockchainStorage lbs(m_blockchain_storage); - - uint64_t currentHeight = lbs->get_current_blockchain_height(); - uint64_t startOffset = 0; - - if (!lbs->find_blockchain_supplement(knownBlockIds, startOffset)) { - return false; - } - - uint64_t startFullOffset = 0; - - if (!lbs->getLowerBound(timestamp, startOffset, startFullOffset)) - startFullOffset = startOffset; - - resFullOffset = startFullOffset; - - if (startOffset != startFullOffset) { - std::list blockIds; - if (!lbs->getBlockIds(startOffset, std::min(uint64_t(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT), startFullOffset - startOffset), blockIds)) { - return false; - } - - for (const auto& id : blockIds) { - entries.push_back(BlockFullInfo()); - entries.back().block_id = id; - } - } - - auto blocksLeft = std::min(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT - entries.size(), size_t(BLOCKS_SYNCHRONIZING_DEFAULT_COUNT)); - - if (blocksLeft) { - std::list blocks; - lbs->get_blocks(startFullOffset, blocksLeft, blocks); - - for (auto& b : blocks) { - BlockFullInfo item; - - item.block_id = get_block_hash(b); - - if (b.timestamp >= timestamp) { - // query transactions - std::list txs; - std::list missedTxs; - lbs->get_transactions(b.txHashes, txs, missedTxs); - - // fill data - block_complete_entry& completeEntry = item; - completeEntry.block = block_to_blob(b); - for (auto& tx : txs) { - completeEntry.txs.push_back(tx_to_blob(tx)); - } - } - - entries.push_back(std::move(item)); - } - } - - resCurrentHeight = currentHeight; - resStartHeight = startOffset; - - return true; -} - -bool core::getBackwardBlocksSizes(uint64_t fromHeight, std::vector& sizes, size_t count) { - return m_blockchain_storage.get_backward_blocks_sizes(fromHeight, sizes, count); -} - -bool core::getBlockSize(const crypto::hash& hash, size_t& size) { - return m_blockchain_storage.getBlockSize(hash, size); -} - -bool core::getAlreadyGeneratedCoins(const crypto::hash& hash, uint64_t& generatedCoins) { - return m_blockchain_storage.getAlreadyGeneratedCoins(hash, generatedCoins); -} - -bool core::getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, - bool penalizeFee, uint64_t& reward, int64_t& emissionChange) { - return m_currency.getBlockReward(medianSize, currentBlockSize, alreadyGeneratedCoins, fee, penalizeFee, reward, emissionChange); -} - -bool core::scanOutputkeysForIndices(const TransactionInputToKey& txInToKey, std::list>& outputReferences) { - struct outputs_visitor - { - std::list>& m_resultsCollector; - outputs_visitor(std::list>& resultsCollector):m_resultsCollector(resultsCollector){} - bool handle_output(const Transaction& tx, const TransactionOutput& out, size_t transactionOutputIndex) - { - m_resultsCollector.push_back(std::make_pair(get_transaction_hash(tx), transactionOutputIndex)); - return true; - } - }; - - outputs_visitor vi(outputReferences); - - return m_blockchain_storage.scan_outputkeys_for_indexes(txInToKey, vi); -} - -bool core::getBlockDifficulty(uint64_t height, difficulty_type& difficulty) { - difficulty = m_blockchain_storage.block_difficulty(height); - return true; -} - -bool core::getBlockContainingTx(const crypto::hash& txId, crypto::hash& blockId, uint64_t& blockHeight) { - return m_blockchain_storage.getBlockContainingTx(txId, blockId, blockHeight); -} - -bool core::getMultisigOutputReference(const TransactionInputMultisignature& txInMultisig, std::pair& outputReference) { - return m_blockchain_storage.getMultisigOutputReference(txInMultisig, outputReference); -} - -} diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h deleted file mode 100644 index d26891b9..00000000 --- a/src/cryptonote_core/cryptonote_core.h +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include -#include - -#include "p2p/net_node_common.h" -#include "cryptonote_protocol/cryptonote_protocol_handler_common.h" -#include "Currency.h" -#include "tx_pool.h" -#include "blockchain_storage.h" -#include "cryptonote_core/i_miner_handler.h" -#include "cryptonote_core/MinerConfig.h" -#include "crypto/hash.h" -#include "ICore.h" -#include "ICoreObserver.h" -#include "Common/ObserverManager.h" -#include - -namespace CryptoNote { - - struct core_stat_info; - class miner; - class CoreConfig; - - class core : public ICore, public i_miner_handler, public IBlockchainStorageObserver, public ITxPoolObserver { - public: - core(const Currency& currency, i_cryptonote_protocol* pprotocol, Logging::ILogger& logger); - ~core(); - - bool on_idle(); - virtual bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block); - bool handle_incoming_block_blob(const blobdata& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block); - virtual i_cryptonote_protocol* get_protocol(){return m_pprotocol;} - const Currency& currency() const { return m_currency; } - - //-------------------- i_miner_handler ----------------------- - virtual bool handle_block_found(Block& b); - virtual bool get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint32_t& height, const blobdata& ex_nonce); - - bool addObserver(ICoreObserver* observer); - bool removeObserver(ICoreObserver* observer); - - miner& get_miner() { return *m_miner; } - static void init_options(boost::program_options::options_description& desc); - bool init(const CoreConfig& config, const MinerConfig& minerConfig, bool load_existing); - bool set_genesis_block(const Block& b); - bool deinit(); - - // ICore - virtual bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp) override; - virtual bool getBackwardBlocksSizes(uint64_t fromHeight, std::vector& sizes, size_t count) override; - virtual bool getBlockSize(const crypto::hash& hash, size_t& size) override; - virtual bool getAlreadyGeneratedCoins(const crypto::hash& hash, uint64_t& generatedCoins) override; - virtual bool getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, - bool penalizeFee, uint64_t& reward, int64_t& emissionChange) override; - virtual bool scanOutputkeysForIndices(const TransactionInputToKey& txInToKey, std::list>& outputReferences) override; - virtual bool getBlockDifficulty(uint64_t height, difficulty_type& difficulty) override; - virtual bool getBlockContainingTx(const crypto::hash& txId, crypto::hash& blockId, uint64_t& blockHeight) override; - virtual bool getMultisigOutputReference(const TransactionInputMultisignature& txInMultisig, std::pair& output_reference) override; - - uint64_t get_current_blockchain_height(); - bool have_block(const crypto::hash& id); - bool get_short_chain_history(std::list& ids); - void on_synchronized(); - bool is_ready() override; - - virtual bool get_blockchain_top(uint64_t& heeight, crypto::hash& top_id); - bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs); - bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks); - template - bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) - { - return m_blockchain_storage.get_blocks(block_ids, blocks, missed_bs); - } - virtual bool queryBlocks(const std::list& block_ids, uint64_t timestamp, - uint64_t& start_height, uint64_t& current_height, uint64_t& full_offset, std::list& entries); - virtual crypto::hash getBlockIdByHeight(uint64_t height) override; - void getTransactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs, bool checkTxPool = false) override; - virtual bool getBlockByHash(const crypto::hash &h, Block &blk) override; - //void get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid); - - bool get_alternative_blocks(std::list& blocks); - size_t get_alternative_blocks_count(); - - void set_cryptonote_protocol(i_cryptonote_protocol* pprotocol); - void set_checkpoints(checkpoints&& chk_pts); - - std::vector getPoolTransactions() override; - size_t get_pool_transactions_count(); - size_t get_blockchain_total_transactions(); - //bool get_outs(uint64_t amount, std::list& pkeys); - virtual bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY_request& resp); - virtual bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); - bool get_stat_info(core_stat_info& st_inf); - - virtual bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs); - crypto::hash get_tail_id(); - virtual bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res); - void pause_mining(); - void update_block_template_and_resume_mining(); - blockchain_storage& get_blockchain_storage(){return m_blockchain_storage;} - //debug functions - void print_blockchain(uint64_t start_index, uint64_t end_index); - void print_blockchain_index(); - std::string print_pool(bool short_format); - void print_blockchain_outs(const std::string& file); - virtual bool getPoolChanges(const crypto::hash& tailBlockId, const std::vector& knownTxsIds, - std::vector& addedTxs, std::vector& deletedTxsIds) override; - virtual void getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, - std::vector& deletedTxsIds) override; - - private: - bool add_new_tx(const Transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block); - bool load_state_data(); - bool parse_tx_from_blob(Transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob); - bool handle_incoming_block(const Block& b, block_verification_context& bvc, bool control_miner, bool relay_block); - - bool check_tx_syntax(const Transaction& tx); - //check correct values, amounts and all lightweight checks not related with database - bool check_tx_semantic(const Transaction& tx, bool keeped_by_block); - //check if tx already in memory pool or in main blockchain - - bool is_key_image_spent(const crypto::key_image& key_im); - - bool check_tx_ring_signature(const TransactionInputToKey& tx, const crypto::hash& tx_prefix_hash, const std::vector& sig); - bool is_tx_spendtime_unlocked(uint64_t unlock_time); - bool update_miner_block_template(); - bool handle_command_line(const boost::program_options::variables_map& vm); - bool on_update_blocktemplate_interval(); - bool check_tx_inputs_keyimages_diff(const Transaction& tx); - virtual void blockchainUpdated() override; - virtual void txDeletedFromPool() override; - void poolUpdated(); - - const Currency& m_currency; - Logging::LoggerRef logger; - CryptoNote::RealTimeProvider m_timeProvider; - tx_memory_pool m_mempool; - blockchain_storage m_blockchain_storage; - i_cryptonote_protocol* m_pprotocol; - std::mutex m_incoming_tx_lock; - std::unique_ptr m_miner; - std::string m_config_folder; - cryptonote_protocol_stub m_protocol_stub; - friend class tx_validate_inputs; - std::atomic m_starter_message_showed; - tools::ObserverManager m_observerManager; - }; -} diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp deleted file mode 100644 index e24b121e..00000000 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ /dev/null @@ -1,754 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "cryptonote_format_utils.h" -#include -#include "../Logging/LoggerRef.h" -#include "account.h" -#include "cryptonote_basic_impl.h" - -using namespace Logging; - -namespace CryptoNote { - -void get_transaction_prefix_hash(const TransactionPrefix& tx, crypto::hash& h) { - std::ostringstream s; - binary_archive a(s); - ::serialization::serialize(a, const_cast(tx)); - crypto::cn_fast_hash(s.str().data(), s.str().size(), h); -} - -crypto::hash get_transaction_prefix_hash(const TransactionPrefix& tx) { - crypto::hash h = null_hash; - get_transaction_prefix_hash(tx, h); - return h; -} - -bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, Transaction& tx) { - std::stringstream ss; - ss << tx_blob; - binary_archive ba(ss); - return ::serialization::serialize(ba, tx); -} - -bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, Transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash) { - std::stringstream ss; - ss << tx_blob; - binary_archive ba(ss); - bool r = ::serialization::serialize(ba, tx); - - if (!r) { - return false; - } - - //TODO: validate tx - crypto::cn_fast_hash(tx_blob.data(), tx_blob.size(), tx_hash); - get_transaction_prefix_hash(tx, tx_prefix_hash); - return true; -} - -bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, KeyPair& in_ephemeral, crypto::key_image& ki) { - crypto::key_derivation recv_derivation; - bool r = crypto::generate_key_derivation(tx_public_key, ack.m_view_secret_key, recv_derivation); - - assert(r && "key image helper: failed to generate_key_derivation"); - - if (!r) { - return false; - } - - r = crypto::derive_public_key(recv_derivation, real_output_index, ack.m_account_address.m_spendPublicKey, in_ephemeral.pub); - - assert(r && "key image helper: failed to derive_public_key"); - - if (!r) { - return false; - } - - crypto::derive_secret_key(recv_derivation, real_output_index, ack.m_spend_secret_key, in_ephemeral.sec); - crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki); - return true; -} - -uint64_t power_integral(uint64_t a, uint64_t b) { - if (b == 0) - return 1; - uint64_t total = a; - for (uint64_t i = 1; i != b; i++) - total *= a; - return total; -} - -bool get_tx_fee(const Transaction& tx, uint64_t & fee) { - uint64_t amount_in = 0; - uint64_t amount_out = 0; - - for (const auto& in : tx.vin) { - if (in.type() == typeid(TransactionInputToKey)) { - amount_in += boost::get(in).amount; - } else if (in.type() == typeid(TransactionInputMultisignature)) { - amount_in += boost::get(in).amount; - } - } - - for (const auto& o : tx.vout) { - amount_out += o.amount; - } - - if (!(amount_in >= amount_out)) { - return false; - } - - fee = amount_in - amount_out; - return true; -} - -uint64_t get_tx_fee(const Transaction& tx) { - uint64_t r = 0; - if (!get_tx_fee(tx, r)) - return 0; - return r; -} - -bool parse_tx_extra(const std::vector& tx_extra, std::vector& tx_extra_fields) { - tx_extra_fields.clear(); - - if (tx_extra.empty()) - return true; - - std::string extra_str(reinterpret_cast(tx_extra.data()), tx_extra.size()); - std::istringstream iss(extra_str); - binary_archive ar(iss); - - bool eof = false; - while (!eof) { - tx_extra_field field; - bool r = ::do_serialize(ar, field); - if (!r) { - return false; - } - tx_extra_fields.push_back(field); - - std::ios_base::iostate state = iss.rdstate(); - eof = (EOF == iss.peek()); - iss.clear(state); - } - - if (!::serialization::check_stream_state(ar)) { - return false; - } - - return true; -} - -crypto::public_key get_tx_pub_key_from_extra(const std::vector& tx_extra) { - std::vector tx_extra_fields; - parse_tx_extra(tx_extra, tx_extra_fields); - - tx_extra_pub_key pub_key_field; - if (!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field)) - return null_pkey; - - return pub_key_field.pub_key; -} - -crypto::public_key get_tx_pub_key_from_extra(const Transaction& tx) { - return get_tx_pub_key_from_extra(tx.extra); -} - -bool add_tx_pub_key_to_extra(Transaction& tx, const crypto::public_key& tx_pub_key) { - tx.extra.resize(tx.extra.size() + 1 + sizeof(crypto::public_key)); - tx.extra[tx.extra.size() - 1 - sizeof(crypto::public_key)] = TX_EXTRA_TAG_PUBKEY; - *reinterpret_cast(&tx.extra[tx.extra.size() - sizeof(crypto::public_key)]) = tx_pub_key; - return true; -} - -bool add_extra_nonce_to_tx_extra(std::vector& tx_extra, const blobdata& extra_nonce) { - if (extra_nonce.size() > TX_EXTRA_NONCE_MAX_COUNT) { - return false; - } - - size_t start_pos = tx_extra.size(); - tx_extra.resize(tx_extra.size() + 2 + extra_nonce.size()); - //write tag - tx_extra[start_pos] = TX_EXTRA_NONCE; - //write len - ++start_pos; - tx_extra[start_pos] = static_cast(extra_nonce.size()); - //write data - ++start_pos; - memcpy(&tx_extra[start_pos], extra_nonce.data(), extra_nonce.size()); - return true; -} - -bool append_mm_tag_to_extra(std::vector& tx_extra, const tx_extra_merge_mining_tag& mm_tag) { - blobdata blob; - if (!t_serializable_object_to_blob(mm_tag, blob)) { - return false; - } - - tx_extra.push_back(TX_EXTRA_MERGE_MINING_TAG); - std::copy(reinterpret_cast(blob.data()), reinterpret_cast(blob.data() + blob.size()), std::back_inserter(tx_extra)); - return true; -} - -bool get_mm_tag_from_extra(const std::vector& tx_extra, tx_extra_merge_mining_tag& mm_tag) { - std::vector tx_extra_fields; - parse_tx_extra(tx_extra, tx_extra_fields); - - return find_tx_extra_field_by_type(tx_extra_fields, mm_tag); -} - -void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id) { - extra_nonce.clear(); - extra_nonce.push_back(TX_EXTRA_NONCE_PAYMENT_ID); - const uint8_t* payment_id_ptr = reinterpret_cast(&payment_id); - std::copy(payment_id_ptr, payment_id_ptr + sizeof(payment_id), std::back_inserter(extra_nonce)); -} - -bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id) { - if (sizeof(crypto::hash) + 1 != extra_nonce.size()) - return false; - if (TX_EXTRA_NONCE_PAYMENT_ID != extra_nonce[0]) - return false; - payment_id = *reinterpret_cast(extra_nonce.data() + 1); - return true; -} - -bool parsePaymentId(const std::string& paymentIdString, crypto::hash& paymentId) { - return Common::podFromHex(paymentIdString, paymentId); -} - -bool createTxExtraWithPaymentId(const std::string& paymentIdString, std::vector& extra) { - crypto::hash paymentIdBin; - - if (!parsePaymentId(paymentIdString, paymentIdBin)) { - return false; - } - - std::string extraNonce; - CryptoNote::set_payment_id_to_tx_extra_nonce(extraNonce, paymentIdBin); - - if (!CryptoNote::add_extra_nonce_to_tx_extra(extra, extraNonce)) { - return false; - } - - return true; -} - -bool getPaymentIdFromTxExtra(const std::vector& extra, crypto::hash& paymentId) { - std::vector tx_extra_fields; - if (!parse_tx_extra(extra, tx_extra_fields)) { - return false; - } - - tx_extra_nonce extra_nonce; - if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) { - if (!get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, paymentId)) { - return false; - } - } else { - return false; - } - - return true; -} - -bool construct_tx( - const account_keys& sender_account_keys, - const std::vector& sources, - const std::vector& destinations, - std::vector extra, - Transaction& tx, - uint64_t unlock_time, - Logging::ILogger& log) { - LoggerRef logger(log, "construct_tx"); - - tx.vin.clear(); - tx.vout.clear(); - tx.signatures.clear(); - - tx.version = CURRENT_TRANSACTION_VERSION; - tx.unlockTime = unlock_time; - - tx.extra = extra; - KeyPair txkey = KeyPair::generate(); - add_tx_pub_key_to_extra(tx, txkey.pub); - - struct input_generation_context_data { - KeyPair in_ephemeral; - }; - - std::vector in_contexts; - uint64_t summary_inputs_money = 0; - //fill inputs - for (const tx_source_entry& src_entr : sources) { - if (src_entr.real_output >= src_entr.outputs.size()) { - logger(ERROR) << "real_output index (" << src_entr.real_output << ")bigger than output_keys.size()=" << src_entr.outputs.size(); - return false; - } - summary_inputs_money += src_entr.amount; - - //key_derivation recv_derivation; - in_contexts.push_back(input_generation_context_data()); - KeyPair& in_ephemeral = in_contexts.back().in_ephemeral; - crypto::key_image img; - if (!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img)) - return false; - - //check that derivated key is equal with real output key - if (!(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second)) { - logger(ERROR) << "derived public key missmatch with output public key! " << ENDL << "derived_key:" - << Common::podToHex(in_ephemeral.pub) << ENDL << "real output_public_key:" - << Common::podToHex(src_entr.outputs[src_entr.real_output].second); - return false; - } - - //put key image into tx input - TransactionInputToKey input_to_key; - input_to_key.amount = src_entr.amount; - input_to_key.keyImage = img; - - //fill outputs array and use relative offsets - for (const tx_source_entry::output_entry& out_entry : src_entr.outputs) { - input_to_key.keyOffsets.push_back(out_entry.first); - } - - input_to_key.keyOffsets = absolute_output_offsets_to_relative(input_to_key.keyOffsets); - tx.vin.push_back(input_to_key); - } - - // "Shuffle" outs - std::vector shuffled_dsts(destinations); - std::sort(shuffled_dsts.begin(), shuffled_dsts.end(), [](const tx_destination_entry& de1, const tx_destination_entry& de2) { return de1.amount < de2.amount; }); - - uint64_t summary_outs_money = 0; - //fill outputs - size_t output_index = 0; - for (const tx_destination_entry& dst_entr : shuffled_dsts) { - if (!(dst_entr.amount > 0)) { - logger(ERROR, BRIGHT_RED) << "Destination with wrong amount: " << dst_entr.amount; - return false; - } - crypto::key_derivation derivation; - crypto::public_key out_eph_public_key; - bool r = crypto::generate_key_derivation(dst_entr.addr.m_viewPublicKey, txkey.sec, derivation); - - if (!(r)) { - logger(ERROR, BRIGHT_RED) - << "at creation outs: failed to generate_key_derivation(" - << dst_entr.addr.m_viewPublicKey << ", " << txkey.sec << ")"; - return false; - } - - r = crypto::derive_public_key(derivation, output_index, - dst_entr.addr.m_spendPublicKey, - out_eph_public_key); - if (!(r)) { - logger(ERROR, BRIGHT_RED) - << "at creation outs: failed to derive_public_key(" << derivation - << ", " << output_index << ", " << dst_entr.addr.m_spendPublicKey - << ")"; - return false; - } - - TransactionOutput out; - out.amount = dst_entr.amount; - TransactionOutputToKey tk; - tk.key = out_eph_public_key; - out.target = tk; - tx.vout.push_back(out); - output_index++; - summary_outs_money += dst_entr.amount; - } - - //check money - if (summary_outs_money > summary_inputs_money) { - logger(ERROR) << "Transaction inputs money (" << summary_inputs_money << ") less than outputs money (" << summary_outs_money << ")"; - return false; - } - - //generate ring signatures - crypto::hash tx_prefix_hash; - get_transaction_prefix_hash(tx, tx_prefix_hash); - - size_t i = 0; - for (const tx_source_entry& src_entr : sources) { - std::vector keys_ptrs; - for (const tx_source_entry::output_entry& o : src_entr.outputs) { - keys_ptrs.push_back(&o.second); - } - - tx.signatures.push_back(std::vector()); - std::vector& sigs = tx.signatures.back(); - sigs.resize(src_entr.outputs.size()); - crypto::generate_ring_signature(tx_prefix_hash, boost::get(tx.vin[i]).keyImage, keys_ptrs, - in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data()); - i++; - } - - return true; -} - -bool get_inputs_money_amount(const Transaction& tx, uint64_t& money) { - money = 0; - - for (const auto& in : tx.vin) { - uint64_t amount = 0; - - if (in.type() == typeid(TransactionInputToKey)) { - amount = boost::get(in).amount; - } else if (in.type() == typeid(TransactionInputMultisignature)) { - amount = boost::get(in).amount; - } - - money += amount; - } - return true; -} - -uint32_t get_block_height(const Block& b) { - if (b.minerTx.vin.size() != 1) { - return 0; - } - const auto& in = b.minerTx.vin[0]; - if (in.type() != typeid(TransactionInputGenerate)) { - return 0; - } - return boost::get(in).height; -} - -bool check_inputs_types_supported(const Transaction& tx) { - for (const auto& in : tx.vin) { - if (in.type() != typeid(TransactionInputToKey) && in.type() != typeid(TransactionInputMultisignature)) { - return false; - } - } - - return true; -} - -bool check_outs_valid(const Transaction& tx, std::string* error) { - for (const TransactionOutput& out : tx.vout) { - if (out.target.type() == typeid(TransactionOutputToKey)) { - if (out.amount == 0) { - if (error) { - *error = "Zero amount ouput"; - } - return false; - } - - if (!check_key(boost::get(out.target).key)) { - if (error) { - *error = "Output with invalid key"; - } - return false; - } - } else if (out.target.type() == typeid(TransactionOutputMultisignature)) { - const TransactionOutputMultisignature& multisignatureOutput = ::boost::get(out.target); - if (multisignatureOutput.requiredSignatures > multisignatureOutput.keys.size()) { - if (error) { - *error = "Multisignature output with invalid required signature count"; - } - return false; - } - for (const crypto::public_key& key : multisignatureOutput.keys) { - if (!check_key(key)) { - if (error) { - *error = "Multisignature output with invalid public key"; - } - return false; - } - } - } else { - if (error) { - *error = "Output with invalid type"; - } - return false; - } - } - - return true; -} - -bool checkMultisignatureInputsDiff(const Transaction& tx) { - std::set> inputsUsage; - for (const auto& inv : tx.vin) { - if (inv.type() == typeid(TransactionInputMultisignature)) { - const TransactionInputMultisignature& in = ::boost::get(inv); - if (!inputsUsage.insert(std::make_pair(in.amount, static_cast(in.outputIndex))).second) { - return false; - } - } - } - return true; -} - -bool check_money_overflow(const Transaction &tx) { - return check_inputs_overflow(tx) && check_outs_overflow(tx); -} - -bool check_inputs_overflow(const Transaction &tx) { - uint64_t money = 0; - - for (const auto &in : tx.vin) { - uint64_t amount = 0; - - if (in.type() == typeid(TransactionInputToKey)) { - amount = boost::get(in).amount; - } else if (in.type() == typeid(TransactionInputMultisignature)) { - amount = boost::get(in).amount; - } - - if (money > amount + money) - return false; - - money += amount; - } - return true; -} - -bool check_outs_overflow(const Transaction& tx) { - uint64_t money = 0; - for (const auto& o : tx.vout) { - if (money > o.amount + money) - return false; - money += o.amount; - } - return true; -} - -uint64_t get_outs_money_amount(const Transaction& tx) { - uint64_t outputs_amount = 0; - for (const auto& o : tx.vout) { - outputs_amount += o.amount; - } - return outputs_amount; -} - -std::string short_hash_str(const crypto::hash& h) { - std::string res = Common::podToHex(h); - - if (res.size() == 64) { - auto erased_pos = res.erase(8, 48); - res.insert(8, "...."); - } - - return res; -} - -bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::key_derivation& derivation, size_t keyIndex) { - crypto::public_key pk; - derive_public_key(derivation, keyIndex, acc.m_account_address.m_spendPublicKey, pk); - return pk == out_key.key; -} - -bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::public_key& tx_pub_key, size_t keyIndex) { - crypto::key_derivation derivation; - generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); - return is_out_to_acc(acc, out_key, derivation, keyIndex); -} - -bool lookup_acc_outs(const account_keys& acc, const Transaction& tx, std::vector& outs, uint64_t& money_transfered) { - crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); - if (null_pkey == tx_pub_key) - return false; - return lookup_acc_outs(acc, tx, tx_pub_key, outs, money_transfered); -} - -bool lookup_acc_outs(const account_keys& acc, const Transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered) { - money_transfered = 0; - size_t keyIndex = 0; - size_t outputIndex = 0; - - crypto::key_derivation derivation; - generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); - - for (const TransactionOutput& o : tx.vout) { - assert(o.target.type() == typeid(TransactionOutputToKey) || o.target.type() == typeid(TransactionOutputMultisignature)); - if (o.target.type() == typeid(TransactionOutputToKey)) { - if (is_out_to_acc(acc, boost::get(o.target), derivation, keyIndex)) { - outs.push_back(outputIndex); - money_transfered += o.amount; - } - - ++keyIndex; - } else if (o.target.type() == typeid(TransactionOutputMultisignature)) { - keyIndex += boost::get(o.target).keys.size(); - } - - ++outputIndex; - } - return true; -} - -void get_blob_hash(const blobdata& blob, crypto::hash& res) { - cn_fast_hash(blob.data(), blob.size(), res); -} - -crypto::hash get_blob_hash(const blobdata& blob) { - crypto::hash h = null_hash; - get_blob_hash(blob, h); - return h; -} - -crypto::hash get_transaction_hash(const Transaction& t) { - crypto::hash h = null_hash; - size_t blob_size = 0; - get_object_hash(t, h, blob_size); - return h; -} - -bool get_transaction_hash(const Transaction& t, crypto::hash& res) { - size_t blob_size = 0; - return get_object_hash(t, res, blob_size); -} - -bool get_transaction_hash(const Transaction& t, crypto::hash& res, size_t& blob_size) { - return get_object_hash(t, res, blob_size); -} - -bool get_block_hashing_blob(const Block& b, blobdata& blob) { - if (!t_serializable_object_to_blob(static_cast(b), blob)) { - return false; - } - crypto::hash tree_root_hash = get_tx_tree_hash(b); - blob.append(reinterpret_cast(&tree_root_hash), sizeof(tree_root_hash)); - blob.append(tools::get_varint_data(b.txHashes.size() + 1)); - - return true; -} - -bool get_parent_block_hashing_blob(const Block& b, blobdata& blob) { - auto serializer = makeParentBlockSerializer(b, true, true); - return t_serializable_object_to_blob(serializer, blob); -} - -bool get_block_hash(const Block& b, crypto::hash& res) { - blobdata blob; - if (!get_block_hashing_blob(b, blob)) { - return false; - } - - if (BLOCK_MAJOR_VERSION_2 <= b.majorVersion) { - blobdata parent_blob; - auto serializer = makeParentBlockSerializer(b, true, false); - if (!t_serializable_object_to_blob(serializer, parent_blob)) - return false; - - blob.append(parent_blob); - } - - return get_object_hash(blob, res); -} - -crypto::hash get_block_hash(const Block& b) { - crypto::hash p = null_hash; - get_block_hash(b, p); - return p; -} - -bool get_aux_block_header_hash(const Block& b, crypto::hash& res) { - blobdata blob; - if (!get_block_hashing_blob(b, blob)) { - return false; - } - - return get_object_hash(blob, res); -} - -bool get_block_longhash(crypto::cn_context &context, const Block& b, crypto::hash& res) { - blobdata bd; - if (b.majorVersion == BLOCK_MAJOR_VERSION_1) { - if (!get_block_hashing_blob(b, bd)) { - return false; - } - } else if (b.majorVersion == BLOCK_MAJOR_VERSION_2) { - if (!get_parent_block_hashing_blob(b, bd)) { - return false; - } - } else { - return false; - } - crypto::cn_slow_hash(context, bd.data(), bd.size(), res); - return true; -} - -std::vector relative_output_offsets_to_absolute(const std::vector& off) { - std::vector res = off; - for (size_t i = 1; i < res.size(); i++) - res[i] += res[i - 1]; - return res; -} - -std::vector absolute_output_offsets_to_relative(const std::vector& off) { - std::vector res = off; - if (!off.size()) - return res; - std::sort(res.begin(), res.end());//just to be sure, actually it is already should be sorted - for (size_t i = res.size() - 1; i != 0; i--) - res[i] -= res[i - 1]; - - return res; -} - -bool parse_and_validate_block_from_blob(const blobdata& b_blob, Block& b) { - std::stringstream ss; - ss << b_blob; - binary_archive ba(ss); - return ::serialization::serialize(ba, b); -} - -blobdata block_to_blob(const Block& b) { - return t_serializable_object_to_blob(b); -} - -bool block_to_blob(const Block& b, blobdata& b_blob) { - return t_serializable_object_to_blob(b, b_blob); -} - -blobdata tx_to_blob(const Transaction& tx) { - return t_serializable_object_to_blob(tx); -} - -bool tx_to_blob(const Transaction& tx, blobdata& b_blob) { - return t_serializable_object_to_blob(tx, b_blob); -} - -void get_tx_tree_hash(const std::vector& tx_hashes, crypto::hash& h) { - tree_hash(tx_hashes.data(), tx_hashes.size(), h); -} - -crypto::hash get_tx_tree_hash(const std::vector& tx_hashes) { - crypto::hash h = null_hash; - get_tx_tree_hash(tx_hashes, h); - return h; -} - -crypto::hash get_tx_tree_hash(const Block& b) { - std::vector txs_ids; - crypto::hash h = null_hash; - size_t bl_sz = 0; - get_transaction_hash(b.minerTx, h, bl_sz); - txs_ids.push_back(h); - for (auto& th : b.txHashes) { - txs_ids.push_back(th); - } - return get_tx_tree_hash(txs_ids); -} - -} diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h deleted file mode 100644 index 47899aa7..00000000 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include "../cryptonote_protocol/blobdatatype.h" -#include "cryptonote_basic.h" - -namespace Logging { -class ILogger; -} - -namespace CryptoNote { - -void get_transaction_prefix_hash(const TransactionPrefix& tx, crypto::hash& h); -crypto::hash get_transaction_prefix_hash(const TransactionPrefix& tx); -bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, Transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash); -bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, Transaction& tx); - -struct tx_source_entry { - typedef std::pair output_entry; - - std::vector outputs; //index + key - size_t real_output; //index in outputs vector of real output_entry - crypto::public_key real_out_tx_key; //incoming real tx public key - size_t real_output_in_tx_index; //index in transaction outputs vector - uint64_t amount; //money -}; - -struct tx_destination_entry { - uint64_t amount; //money - AccountPublicAddress addr; //destination address - - tx_destination_entry() : amount(0), addr(boost::value_initialized()) { } - tx_destination_entry(uint64_t a, const AccountPublicAddress &ad) : amount(a), addr(ad) { } -}; - - -bool construct_tx( - const account_keys& sender_account_keys, - const std::vector& sources, - const std::vector& destinations, - std::vector extra, Transaction& tx, uint64_t unlock_time, Logging::ILogger& log); - -template -bool find_tx_extra_field_by_type(const std::vector& tx_extra_fields, T& field) { - auto it = std::find_if(tx_extra_fields.begin(), tx_extra_fields.end(), - [](const tx_extra_field& f) { return typeid(T) == f.type(); }); - - if (tx_extra_fields.end() == it) - return false; - - field = boost::get(*it); - return true; -} - -bool parse_tx_extra(const std::vector& tx_extra, std::vector& tx_extra_fields); -crypto::public_key get_tx_pub_key_from_extra(const std::vector& tx_extra); -crypto::public_key get_tx_pub_key_from_extra(const Transaction& tx); -bool add_tx_pub_key_to_extra(Transaction& tx, const crypto::public_key& tx_pub_key); -bool add_extra_nonce_to_tx_extra(std::vector& tx_extra, const blobdata& extra_nonce); -void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id); -bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id); -bool append_mm_tag_to_extra(std::vector& tx_extra, const tx_extra_merge_mining_tag& mm_tag); -bool get_mm_tag_from_extra(const std::vector& tx_extra, tx_extra_merge_mining_tag& mm_tag); -bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::public_key& tx_pub_key, size_t keyIndex); -bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::key_derivation& derivation, size_t keyIndex); -bool lookup_acc_outs(const account_keys& acc, const Transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered); -bool lookup_acc_outs(const account_keys& acc, const Transaction& tx, std::vector& outs, uint64_t& money_transfered); -bool get_tx_fee(const Transaction& tx, uint64_t & fee); -uint64_t get_tx_fee(const Transaction& tx); -bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, KeyPair& in_ephemeral, crypto::key_image& ki); -void get_blob_hash(const blobdata& blob, crypto::hash& res); -crypto::hash get_blob_hash(const blobdata& blob); -std::string short_hash_str(const crypto::hash& h); -bool createTxExtraWithPaymentId(const std::string& paymentIdString, std::vector& extra); -//returns false if payment id is not found or parse error -bool getPaymentIdFromTxExtra(const std::vector& extra, crypto::hash& paymentId); -bool parsePaymentId(const std::string& paymentIdString, crypto::hash& paymentId); - -crypto::hash get_transaction_hash(const Transaction& t); -bool get_transaction_hash(const Transaction& t, crypto::hash& res); -bool get_transaction_hash(const Transaction& t, crypto::hash& res, size_t& blob_size); -bool get_block_hashing_blob(const Block& b, blobdata& blob); -bool get_parent_block_hashing_blob(const Block& b, blobdata& blob); -bool get_aux_block_header_hash(const Block& b, crypto::hash& res); -bool get_block_hash(const Block& b, crypto::hash& res); -crypto::hash get_block_hash(const Block& b); -bool get_block_longhash(crypto::cn_context &context, const Block& b, crypto::hash& res); -bool parse_and_validate_block_from_blob(const blobdata& b_blob, Block& b); -bool get_inputs_money_amount(const Transaction& tx, uint64_t& money); -uint64_t get_outs_money_amount(const Transaction& tx); -bool check_inputs_types_supported(const Transaction& tx); -bool check_outs_valid(const Transaction& tx, std::string* error = 0); -bool checkMultisignatureInputsDiff(const Transaction& tx); - -bool check_money_overflow(const Transaction& tx); -bool check_outs_overflow(const Transaction& tx); -bool check_inputs_overflow(const Transaction& tx); -uint32_t get_block_height(const Block& b); -std::vector relative_output_offsets_to_absolute(const std::vector& off); -std::vector absolute_output_offsets_to_relative(const std::vector& off); - -template -bool t_serializable_object_to_blob(const t_object& to, blobdata& b_blob) { - std::stringstream ss; - binary_archive ba(ss); - bool r = ::serialization::serialize(ba, const_cast(to)); - b_blob = ss.str(); - return r; -} - -template -blobdata t_serializable_object_to_blob(const t_object& to) { - blobdata b; - t_serializable_object_to_blob(to, b); - return b; -} - -template -bool get_object_hash(const t_object& o, crypto::hash& res) { - get_blob_hash(t_serializable_object_to_blob(o), res); - return true; -} - -template -bool get_object_blobsize(const t_object& o, size_t& size) { - blobdata blob; - if (!t_serializable_object_to_blob(o, blob)) { - size = (std::numeric_limits::max)(); - return false; - } - size = blob.size(); - return true; -} - -template -size_t get_object_blobsize(const t_object& o) { - size_t size; - get_object_blobsize(o, size); - return size; -} - -template -bool get_object_hash(const t_object& o, crypto::hash& res, size_t& blob_size) { - blobdata bl = t_serializable_object_to_blob(o); - blob_size = bl.size(); - get_blob_hash(bl, res); - return true; -} - -template -std::string obj_to_json_str(const T& obj) { - std::stringstream ss; - json_archive ar(ss, true); - bool r = ::serialization::serialize(ar, *const_cast(&obj)); - if (!r) { - return ""; - } - return ss.str(); -} - -// 62387455827 -> 455827 + 7000000 + 80000000 + 300000000 + 2000000000 + 60000000000, where 455827 <= dust_threshold -template -void decompose_amount_into_digits(uint64_t amount, uint64_t dust_threshold, const chunk_handler_t& chunk_handler, const dust_handler_t& dust_handler) { - if (0 == amount) { - return; - } - - bool is_dust_handled = false; - uint64_t dust = 0; - uint64_t order = 1; - while (0 != amount) { - uint64_t chunk = (amount % 10) * order; - amount /= 10; - order *= 10; - - if (dust + chunk <= dust_threshold) { - dust += chunk; - } else { - if (!is_dust_handled && 0 != dust) { - dust_handler(dust); - is_dust_handled = true; - } - if (0 != chunk) { - chunk_handler(chunk); - } - } - } - - if (!is_dust_handled && 0 != dust) { - dust_handler(dust); - } -} - -blobdata block_to_blob(const Block& b); -bool block_to_blob(const Block& b, blobdata& b_blob); -blobdata tx_to_blob(const Transaction& b); -bool tx_to_blob(const Transaction& b, blobdata& b_blob); -void get_tx_tree_hash(const std::vector& tx_hashes, crypto::hash& h); -crypto::hash get_tx_tree_hash(const std::vector& tx_hashes); -crypto::hash get_tx_tree_hash(const Block& b); - -#define CHECKED_GET_SPECIFIC_VARIANT(variant_var, specific_type, variable_name, fail_return_val) \ - if(variant_var.type() != typeid(specific_type)) { return fail_return_val; } \ - specific_type& variable_name = boost::get(variant_var); - -} diff --git a/src/cryptonote_core/tx_extra.h b/src/cryptonote_core/tx_extra.h deleted file mode 100644 index e7d3c14d..00000000 --- a/src/cryptonote_core/tx_extra.h +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include "crypto/crypto.h" -#include "crypto/hash.h" -#include "serialization/binary_archive.h" -#include "serialization/crypto.h" -#include "serialization/serialization.h" -#include "serialization/variant.h" - -#define TX_EXTRA_PADDING_MAX_COUNT 255 -#define TX_EXTRA_NONCE_MAX_COUNT 255 - -#define TX_EXTRA_TAG_PADDING 0x00 -#define TX_EXTRA_TAG_PUBKEY 0x01 -#define TX_EXTRA_NONCE 0x02 -#define TX_EXTRA_MERGE_MINING_TAG 0x03 - -#define TX_EXTRA_NONCE_PAYMENT_ID 0x00 - -namespace CryptoNote -{ - struct tx_extra_padding - { - size_t size; - - // load - template