Multisignature API, Low level and High level APIs

This commit is contained in:
Antonio Juarez 2015-04-06 17:13:07 +01:00
parent 257a2bf339
commit 1743402759
277 changed files with 30264 additions and 9849 deletions

View file

@ -12,11 +12,19 @@ if(APPLE)
include_directories(SYSTEM /usr/include/malloc)
endif()
if(MSVC)
include_directories(src/Platform/Windows)
elseif(APPLE)
include_directories(src/Platform/OSX)
else()
include_directories(src/Platform/Linux)
endif()
set(STATIC ${MSVC} CACHE BOOL "Link libraries statically")
if(MSVC)
add_definitions("/bigobj /MP /W3 /GS- /D_CRT_SECURE_NO_WARNINGS /wd4996 /wd4345 /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /DGTEST_HAS_TR1_TUPLE=0 /D_VARIADIC_MAX=8 /FIinline_c.h /D__SSE4_1__")
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Dinline=__inline")
add_definitions("/bigobj /MP /W3 /GS- /D_CRT_SECURE_NO_WARNINGS /wd4996 /wd4345 /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /DGTEST_HAS_TR1_TUPLE=0 /D_VARIADIC_MAX=8 /D__SSE4_1__")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:10485760")
if(STATIC)
foreach(VAR CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELWITHDEBINFO)
@ -35,7 +43,7 @@ else()
if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
set(WARNINGS "${WARNINGS} -Wno-error=mismatched-tags -Wno-error=null-conversion -Wno-overloaded-shift-op-parentheses -Wno-error=shift-count-overflow -Wno-error=tautological-constant-out-of-range-compare -Wno-error=unused-private-field -Wno-error=unneeded-internal-declaration -Wno-error=unused-function")
else()
set(WARNINGS "${WARNINGS} -Wlogical-op -Wno-error=maybe-uninitialized")
set(WARNINGS "${WARNINGS} -Wlogical-op -Wno-error=maybe-uninitialized -Wno-error=clobbered -Wno-error=unused-but-set-variable")
endif()
if(MINGW)
set(WARNINGS "${WARNINGS} -Wno-error=unused-value")
@ -46,13 +54,7 @@ else()
endif()
set(C_WARNINGS "-Waggregate-return -Wnested-externs -Wold-style-definition -Wstrict-prototypes")
set(CXX_WARNINGS "-Wno-reorder -Wno-missing-field-initializers")
try_compile(STATIC_ASSERT_RES "${CMAKE_CURRENT_BINARY_DIR}/static-assert" "${CMAKE_CURRENT_SOURCE_DIR}/utils/test-static-assert.c" COMPILE_DEFINITIONS "-std=c11")
if(STATIC_ASSERT_RES)
set(STATIC_ASSERT_FLAG "")
else()
set(STATIC_ASSERT_FLAG "-Dstatic_assert=_Static_assert")
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_FLAG} ${WARNINGS} ${C_WARNINGS} ${ARCH_FLAG} -maes")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -D_GNU_SOURCE ${MINGW_FLAG} ${WARNINGS} ${C_WARNINGS} ${ARCH_FLAG} -maes")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -D_GNU_SOURCE ${MINGW_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${ARCH_FLAG} -maes")
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGTEST_HAS_TR1_TUPLE=0")

View file

@ -1,3 +1,11 @@
Release notes 1.0.3
- Multisignature API
- Low level API
- High level API improvements
- Instant transaction pool notifications
- Fully refactored simplewallet
Release notes 1.0.2
- Transaction history for simplewallet

View file

@ -27,6 +27,7 @@
#pragma once
#include <cstdint>
#include <vector>
#include <boost/mpl/contains.hpp>
#include <boost/mpl/vector.hpp>

View file

@ -114,6 +114,7 @@ namespace epee
const_cast<t_arg&>(out_struct).store(stg);//TODO: add true const support to searilzation
std::string buff_to_send, buff_to_recv;
stg.store_to_binary(buff_to_send);
int res = transport.invoke_async(command, buff_to_send, conn_id, [cb, command](int code, const std::string& buff, typename t_transport::connection_context& context)->bool
{
t_result result_struct = AUTO_VAL_INIT(result_struct);
@ -131,6 +132,7 @@ namespace epee
return false;
}
result_struct.load(stg_ret);
cb(code, result_struct, context);
return true;
}, inv_timeout);

View file

@ -27,6 +27,7 @@
#pragma once
#include <boost/algorithm/string/predicate.hpp>
#include <boost/lexical_cast.hpp>
#include "parserse_base_utils.h"
#include "file_io_utils.h"

View file

@ -38,6 +38,8 @@
#include <string>
#include <sstream>
#include <boost/lexical_cast.hpp>
#include "warnings.h"
#ifndef OUT

View file

@ -35,6 +35,7 @@ public:
virtual void peerCountUpdated(size_t count) {}
virtual void localBlockchainUpdated(uint64_t height) {}
virtual void lastKnownBlockHeightUpdated(uint64_t height) {}
virtual void poolChanged() {}
};
struct OutEntry {
@ -47,6 +48,12 @@ struct OutsForAmount {
std::vector<OutEntry> outs;
};
struct BlockCompleteEntry {
crypto::hash blockHash;
cryptonote::blobdata block;
std::list<cryptonote::blobdata> txs;
};
class INode {
public:
typedef std::function<void(std::error_code)> Callback;
@ -61,11 +68,14 @@ public:
virtual size_t getPeerCount() const = 0;
virtual uint64_t getLastLocalBlockHeight() const = 0;
virtual uint64_t getLastKnownBlockHeight() const = 0;
virtual uint64_t getLastLocalBlockTimestamp() const = 0;
virtual void relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) = 0;
virtual void getRandomOutsByAmounts(std::vector<uint64_t>&& amounts, uint64_t outsCount, std::vector<cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount>& result, const Callback& callback) = 0;
virtual void getNewBlocks(std::list<crypto::hash>&& knownBlockIds, std::list<cryptonote::block_complete_entry>& newBlocks, uint64_t& startHeight, const Callback& callback) = 0;
virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector<uint64_t>& outsGlobalIndices, const Callback& callback) = 0;
virtual void queryBlocks(std::list<crypto::hash>&& knownBlockIds, uint64_t timestamp, std::list<BlockCompleteEntry>& newBlocks, uint64_t& startHeight, const Callback& callback) = 0;
virtual void getPoolSymmetricDifference(std::vector<crypto::hash>&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector<cryptonote::Transaction>& new_txs, std::vector<crypto::hash>& deleted_tx_ids, const Callback& callback) = 0;
};
}

29
include/IObservable.h Normal file
View file

@ -0,0 +1,29 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
namespace CryptoNote {
template <typename T>
class IObservable {
public:
virtual void addObserver(T* observer) = 0;
virtual void removeObserver(T* observer) = 0;
};
}

View file

@ -0,0 +1,30 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <sstream>
namespace CryptoNote {
class IStreamSerializable {
public:
virtual void save(std::ostream& os) = 0;
virtual void load(std::istream& in) = 0;
};
}

180
include/ITransaction.h Normal file
View file

@ -0,0 +1,180 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <array>
#include <cstdint>
#include <vector>
namespace CryptoNote {
typedef std::array<uint8_t, 32> PublicKey;
typedef std::array<uint8_t, 32> SecretKey;
typedef std::array<uint8_t, 32> KeyImage;
typedef std::array<uint8_t, 32> Hash;
typedef std::vector<uint8_t> Blob;
struct AccountAddress {
PublicKey spendPublicKey;
PublicKey viewPublicKey;
};
struct AccountKeys {
AccountAddress address;
SecretKey spendSecretKey;
SecretKey viewSecretKey;
};
struct KeyPair {
PublicKey publicKey;
SecretKey secretKey;
};
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<uint64_t> 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<PublicKey> keys;
uint32_t requiredSignatures;
};
struct GlobalOutput {
PublicKey targetKey;
uint64_t outputIndex;
};
typedef std::vector<GlobalOutput> GlobalOutputsContainer;
struct OutputKeyInfo {
PublicKey transactionPublicKey;
size_t transactionIndex;
size_t outputInTransaction;
};
struct InputKeyInfo {
uint64_t amount;
GlobalOutputsContainer outputs;
OutputKeyInfo realOutput;
};
}
//
// ITransactionReader
//
class ITransactionReader {
public:
virtual ~ITransactionReader() { }
virtual Hash getTransactionHash() const = 0;
virtual Hash getTransactionPrefixHash() const = 0;
virtual PublicKey getTransactionPublicKey() const = 0;
virtual uint64_t getUnlockTime() const = 0;
// extra
virtual bool getPaymentId(Hash& paymentId) const = 0;
virtual bool getExtraNonce(std::string& nonce) 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;
// 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;
// signatures
virtual size_t getRequiredSignaturesCount(size_t inputIndex) const = 0;
virtual bool findOutputsToAccount(const AccountAddress& addr, const SecretKey& viewSecretKey, std::vector<uint32_t>& outs, uint64_t& outputAmount) const = 0;
// various checks
virtual bool validateInputs() const = 0;
virtual bool validateOutputs() const = 0;
virtual bool validateSignatures() const = 0;
// serialized transaction
virtual Blob getTransactionData() const = 0;
};
//
// ITransactionWriter
//
class ITransactionWriter {
public:
virtual ~ITransactionWriter() { }
// transaction parameters
virtual void setUnlockTime(uint64_t unlockTime) = 0;
// extra
virtual void setPaymentId(const Hash& paymentId) = 0;
virtual void setExtraNonce(const std::string& nonce) = 0;
// Inputs/Outputs
virtual size_t addInput(const TransactionTypes::InputKey& input) = 0;
virtual size_t addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, KeyPair& ephKeys) = 0;
virtual size_t addInput(const TransactionTypes::InputMultisignature& input) = 0;
virtual size_t addOutput(uint64_t amount, const AccountAddress& to) = 0;
virtual size_t addOutput(uint64_t amount, const std::vector<AccountAddress>& to, uint32_t requiredSignatures) = 0;
// transaction info
virtual bool getTransactionSecretKey(SecretKey& key) const = 0;
virtual void setTransactionSecretKey(const SecretKey& key) = 0;
// signing
virtual void signInputKey(size_t input, const TransactionTypes::InputKeyInfo& info, const KeyPair& ephKeys) = 0;
virtual void signInputMultisignature(size_t input, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) = 0;
};
class ITransaction :
public ITransactionReader,
public ITransactionWriter {
public:
virtual ~ITransaction() { }
};
}

View file

@ -0,0 +1,105 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <cstdint>
#include <limits>
#include <vector>
#include "crypto/hash.h"
#include "ITransaction.h"
#include "IObservable.h"
#include "IStreamSerializable.h"
namespace CryptoNote {
const uint64_t UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX = std::numeric_limits<uint64_t>::max();
struct TransactionInformation {
// transaction info
Hash transactionHash;
PublicKey publicKey;
uint64_t blockHeight;
uint64_t timestamp;
uint64_t unlockTime;
uint64_t totalAmountIn;
uint64_t totalAmountOut;
std::vector<uint8_t> extra;
Hash paymentId;
};
struct TransactionOutputInformation {
// output info
TransactionTypes::OutputType type;
uint64_t amount;
uint64_t globalOutputIndex;
uint32_t outputInTransaction;
// transaction info
Hash transactionHash;
PublicKey transactionPublicKey;
union {
PublicKey outputKey; // Type: Key
uint32_t requiredSignatures; // Type: Multisignature
};
};
struct TransactionSpentOutputInformation: public TransactionOutputInformation {
uint64_t spendingBlockHeight;
uint64_t timestamp;
Hash spendingTransactionHash;
KeyImage keyImage; //!< \attention Used only for TransactionTypes::OutputType::Key
uint32_t inputInTransaction;
};
class ITransfersContainer : public IStreamSerializable {
public:
enum Flags : uint32_t {
// state
IncludeStateUnlocked = 0x01,
IncludeStateLocked = 0x02,
IncludeStateSoftLocked = 0x04,
// output type
IncludeTypeKey = 0x100,
IncludeTypeMultisignature = 0x200,
// combinations
IncludeStateAll = 0xff,
IncludeTypeAll = 0xff00,
IncludeKeyUnlocked = IncludeTypeKey | IncludeStateUnlocked,
IncludeKeyNotUnlocked = IncludeTypeKey | IncludeStateLocked | IncludeStateSoftLocked,
IncludeAllLocked = IncludeTypeAll | IncludeStateLocked | IncludeStateSoftLocked,
IncludeAllUnlocked = IncludeTypeAll | IncludeStateUnlocked,
IncludeAll = IncludeTypeAll | IncludeStateAll,
IncludeDefault = IncludeKeyUnlocked
};
virtual size_t transfersCount() = 0;
virtual size_t transactionsCount() = 0;
virtual uint64_t balance(uint32_t flags = IncludeDefault) = 0;
virtual void getOutputs(std::vector<TransactionOutputInformation>& transfers, uint32_t flags = IncludeDefault) = 0;
virtual bool getTransactionInformation(const Hash& transactionHash, TransactionInformation& info, int64_t& txBalance) = 0;
virtual std::vector<TransactionOutputInformation> getTransactionOutputs(const Hash& transactionHash, uint32_t flags = IncludeDefault) = 0;
virtual void getUnconfirmedTransactions(std::vector<crypto::hash>& transactions) = 0;
virtual std::vector<TransactionSpentOutputInformation> getSpentOutputs() = 0;
};
}

View file

@ -0,0 +1,75 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <cstdint>
#include <system_error>
#include "ITransaction.h"
#include "ITransfersContainer.h"
#include "IStreamSerializable.h"
namespace CryptoNote {
struct SynchronizationStart {
uint64_t timestamp;
uint64_t height;
};
struct AccountSubscription {
AccountKeys keys;
SynchronizationStart syncStart;
size_t transactionSpendableAge;
};
class ITransfersSubscription;
class ITransfersObserver {
public:
virtual void onError(ITransfersSubscription* object,
uint64_t height, std::error_code ec) {}
virtual void onTransactionUpdated(ITransfersSubscription* object, const 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) { }
};
class ITransfersSubscription : public IObservable < ITransfersObserver > {
public:
virtual ~ITransfersSubscription() {}
virtual AccountAddress getAddress() = 0;
virtual ITransfersContainer& getContainer() = 0;
};
class ITransfersSynchronizer : public IStreamSerializable {
public:
virtual ~ITransfersSynchronizer() {}
virtual ITransfersSubscription& addSubscription(const AccountSubscription& acc) = 0;
virtual bool removeSubscription(const AccountAddress& acc) = 0;
virtual void getSubscriptions(std::vector<AccountAddress>& subscriptions) = 0;
// returns nullptr if address is not found
virtual ITransfersSubscription* getSubscription(const AccountAddress& acc) = 0;
};
}

View file

@ -41,23 +41,48 @@ const TransactionId INVALID_TRANSACTION_ID = std::numeric_limits<TransactionI
const TransferId INVALID_TRANSFER_ID = std::numeric_limits<TransferId>::max();
const uint64_t UNCONFIRMED_TRANSACTION_HEIGHT = std::numeric_limits<uint64_t>::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;
TransactionHash hash;
bool isCoinbase;
uint64_t blockHeight;
uint64_t timestamp;
std::string extra;
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<uint8_t, 32> WalletPublicKey;
typedef std::array<uint8_t, 32> 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, 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) {}
@ -73,7 +98,9 @@ public:
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 shutdown() = 0;
virtual void reset() = 0;
virtual void save(std::ostream& destination, bool saveDetailed = true, bool saveCache = true) = 0;
@ -95,6 +122,8 @@ public:
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<Transfer>& 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;
};
}

35
src/CMakeLists.txt Normal file → Executable file
View file

@ -12,6 +12,19 @@ file(GLOB_RECURSE CONN_TOOL connectivity_tool/*)
file(GLOB_RECURSE WALLET wallet/*)
file(GLOB_RECURSE MINER miner/*)
file(GLOB_RECURSE NODE_RPC_PROXY node_rpc_proxy/*)
file(GLOB_RECURSE TRANSFERS transfers/*)
if(MSVC)
file(GLOB_RECURSE SYSTEM Platform/Windows/System/*)
elseif(APPLE)
file(GLOB_RECURSE SYSTEM Platform/OSX/System/*)
else()
file(GLOB_RECURSE SYSTEM Platform/Linux/System/*)
endif()
file(GLOB_RECURSE SERIALIZATION serialization/*)
file(GLOB_RECURSE LOGGER logger/*)
file(GLOB_RECURSE INPROCESS_NODE inprocess_node/*)
file(GLOB_RECURSE HTTP HTTP/*)
source_group(common FILES ${COMMON})
source_group(crypto FILES ${CRYPTO})
@ -20,32 +33,42 @@ source_group(cryptonote_protocol FILES ${CRYPTONOTE_PROTOCOL})
source_group(daemon FILES ${DAEMON})
source_group(p2p FILES ${P2P})
source_group(rpc FILES ${RPC})
source_group(System FILES ${SYSTEM} ${HTTP})
source_group(simplewallet FILES ${SIMPLEWALLET})
source_group(connectivity-tool FILES ${CONN_TOOL})
source_group(wallet FILES ${WALLET})
source_group(simpleminer FILES ${MINER})
source_group(node_rpc_proxy FILES ${NODE_RPC_PROXY})
source_group(transfers FILES ${TRANSFERS})
source_group(logger FILES ${LOGGER})
source_group(inprocess_node FILES ${INPROCESS_NODE})
add_library(common ${COMMON})
add_library(crypto ${CRYPTO})
add_library(serialization ${SERIALIZATION})
add_library(cryptonote_core ${CRYPTONOTE_CORE})
add_library(node_rpc_proxy ${NODE_RPC_PROXY})
add_library(inprocess_node ${INPROCESS_NODE})
add_executable(daemon ${DAEMON} ${P2P} ${CRYPTONOTE_PROTOCOL})
add_executable(connectivity_tool ${CONN_TOOL})
add_executable(simpleminer ${MINER})
target_link_libraries(daemon epee rpc cryptonote_core crypto common upnpc-static ${Boost_LIBRARIES})
target_link_libraries(connectivity_tool epee cryptonote_core crypto common ${Boost_LIBRARIES})
target_link_libraries(simpleminer epee cryptonote_core crypto common ${Boost_LIBRARIES})
target_link_libraries(daemon epee rpc cryptonote_core crypto common upnpc-static serialization ${Boost_LIBRARIES})
target_link_libraries(connectivity_tool epee rpc cryptonote_core crypto common serialization ${Boost_LIBRARIES})
target_link_libraries(simpleminer epee cryptonote_core crypto common serialization ${Boost_LIBRARIES})
add_library(rpc ${RPC})
add_library(System ${SYSTEM} ${HTTP} System/TcpStream.cpp System/TcpStream.h)
add_library(wallet ${WALLET})
add_executable(simplewallet ${SIMPLEWALLET} )
target_link_libraries(simplewallet epee wallet rpc cryptonote_core crypto common upnpc-static ${Boost_LIBRARIES})
add_library(node_rpc_proxy ${NODE_RPC_PROXY})
target_link_libraries(simplewallet epee wallet transfers rpc cryptonote_core crypto common upnpc-static node_rpc_proxy serialization ${Boost_LIBRARIES})
add_library(logger ${LOGGER})
add_library(transfers ${TRANSFERS})
add_dependencies(connectivity_tool version)
add_dependencies(daemon version)
add_dependencies(rpc version)
add_dependencies(simplewallet version)
set_property(TARGET common crypto cryptonote_core rpc wallet node_rpc_proxy PROPERTY FOLDER "libs")
set_property(TARGET common crypto cryptonote_core rpc System wallet node_rpc_proxy serialization logger transfers inprocess_node PROPERTY FOLDER "libs")
set_property(TARGET daemon simplewallet connectivity_tool simpleminer PROPERTY FOLDER "prog")
set_property(TARGET daemon PROPERTY OUTPUT_NAME "bytecoind")

0
src/System/HttpParser.cpp → src/HTTP/HttpParser.cpp Normal file → Executable file
View file

0
src/System/HttpParser.h → src/HTTP/HttpParser.h Normal file → Executable file
View file

View file

0
src/System/HttpRequest.h → src/HTTP/HttpRequest.h Normal file → Executable file
View file

View file

0
src/System/HttpResponse.h → src/HTTP/HttpResponse.h Normal file → Executable file
View file

View file

@ -0,0 +1,160 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "Dispatcher.h"
#include <iostream>
#include <ucontext.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <assert.h>
#include <sys/time.h>
#include <errno.h>
#include <stdexcept>
using namespace System;
void Dispatcher::contextProcedureStatic(void *context) {
reinterpret_cast<Dispatcher*>(context)->contextProcedure();
}
Dispatcher::Dispatcher() {
epoll = ::epoll_create1(0);
if (epoll == -1) {
std::cerr << "kqueue() fail errno=" << errno << std::endl;
} else {
currentContext = new ucontext_t;
if (getcontext(reinterpret_cast<ucontext_t*>(currentContext)) == -1) {
std::cerr << "getcontext() fail errno=" << errno << std::endl;
} else {
contextCount = 0;
return;
}
}
throw std::runtime_error("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<ucontext_t*>(reusableContexts.top());
reusableContexts.pop();
}
while (!timers.empty()) {
timers.pop();
}
if (-1 == close(epoll)) {
std::cerr << "close() fail errno=" << errno << std::endl;
}
}
void* Dispatcher::getCurrentContext() const {
return currentContext;
}
int Dispatcher::getEpoll() const {
return epoll;
}
void Dispatcher::pushContext(void* context) {
resumingContexts.push(context);
}
void Dispatcher::spawn(std::function<void()>&& procedure) {
ucontext_t *context;
if (reusableContexts.empty()) {
context = new ucontext_t;
if (getcontext(context) == -1) { //makecontext precondition
std::cerr << "getcontext() fail errno=" << errno << std::endl;
throw std::runtime_error("Dispatcher::spawn()");
}
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<int*>(this));
++contextCount;
} else {
context = static_cast<ucontext_t*>(reusableContexts.top());
reusableContexts.pop();
}
resumingContexts.push(context);
spawningProcedures.emplace(std::move(procedure));
}
void Dispatcher::clear() {
//TODO
}
void Dispatcher::yield() {
void* context;
for (;;) {
if (!resumingContexts.empty()) {
context = resumingContexts.front();
resumingContexts.pop();
assert(context);
break;
}
epoll_event event;
int count = epoll_wait(epoll, &event, 1, -1);
if (count == 1) {
if ((event.events & EPOLLOUT) != 0) {
context = static_cast<ContextExt *>(event.data.ptr)->writeContext;
} else {
context = static_cast<ContextExt *>(event.data.ptr)->context;
}
assert(context);
break;
}
if (errno != EINTR) {
std::cerr << "epoll_wait() failed, errno=" << errno << std::endl;
throw std::runtime_error("Dispatcher::yield()");
}
}
if (context != currentContext) {
ucontext_t* oldContext = static_cast<ucontext_t*>(currentContext);
currentContext = context;
if (-1 == swapcontext(oldContext, static_cast<ucontext_t *>(context))) {
std::cerr << "swapcontext() failed, errno=" << errno << std::endl;
throw std::runtime_error("Dispatcher::yield()");
}
}
}
void Dispatcher::contextProcedure() {
void* context = currentContext;
for (;;) {
assert(!spawningProcedures.empty());
std::function<void()> procedure = std::move(spawningProcedures.front());
spawningProcedures.pop();
procedure();
reusableContexts.push(context);
yield();
}
}

View file

@ -0,0 +1,64 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <functional>
#include <queue>
#include <stack>
namespace System {
class Dispatcher {
public:
Dispatcher();
Dispatcher(const Dispatcher&) = delete;
~Dispatcher();
Dispatcher& operator=(const Dispatcher&) = delete;
void spawn(std::function<void()>&& procedure);
void yield();
void clear();
struct ContextExt {
void *context;
void *writeContext; //required workaround
};
private:
friend class Event;
friend class DispatcherAccessor;
friend class TcpConnection;
friend class TcpConnector;
friend class TcpListener;
friend class Timer;
int epoll;
void* currentContext;
std::size_t contextCount;
std::queue<void*> resumingContexts;
std::stack<void*> reusableContexts;
std::stack<uint8_t *> allocatedStacks;
std::queue<std::function<void()>> spawningProcedures;
std::stack<int> timers;
int getEpoll() const;
void pushContext(void* context);
void* getCurrentContext() const;
void contextProcedure();
static void contextProcedureStatic(void* context);
};
}

View file

@ -17,28 +17,35 @@
#include "Event.h"
#include <cassert>
#include "System.h"
#include <iostream>
#include "Dispatcher.h"
struct Event::Waiter {
Event::Waiter* next;
using namespace System;
namespace {
struct Waiter {
Waiter* next;
void* context;
};
Event::Event() : system(nullptr) {
}
Event::Event(System& system) : system(&system), first(nullptr), state(false) {
Event::Event() : dispatcher(nullptr) {
}
Event::Event(Event&& other) : system(other.system) {
if (other.system != nullptr) {
Event::Event(Dispatcher& dispatcher) : dispatcher(&dispatcher), first(nullptr), state(false) {
}
Event::Event(Event&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
first = other.first;
if (other.first != nullptr) {
last = other.last;
}
state = other.state;
other.system = nullptr;
other.dispatcher = nullptr;
}
}
@ -48,51 +55,52 @@ Event::~Event() {
Event& Event::operator=(Event&& other) {
assert(first == nullptr);
system = other.system;
if (other.system != nullptr) {
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
first = other.first;
if (other.first != nullptr) {
last = other.last;
}
state = other.state;
other.system = nullptr;
other.dispatcher = nullptr;
}
return *this;
}
bool Event::get() const {
assert(system != nullptr);
assert(dispatcher != nullptr);
return state;
}
void Event::clear() {
assert(system != nullptr);
assert(dispatcher != nullptr);
state = false;
}
void Event::set() {
assert(system != nullptr);
assert(dispatcher != nullptr);
state = true;
for (Waiter* waiter = first; waiter != nullptr; waiter = waiter->next) {
system->pushContext(waiter->context);
for (Waiter* waiter = static_cast<Waiter*>(first); waiter != nullptr; waiter = waiter->next) {
dispatcher->pushContext(waiter->context);
}
first = nullptr;
}
void Event::wait() {
assert(system != nullptr);
Waiter waiter = {nullptr, system->getCurrentContext()};
if (first != nullptr) {
last->next = &waiter;
} else {
first = &waiter;
}
assert(dispatcher != nullptr);
if (!state) {
Waiter waiter = { nullptr, dispatcher->getCurrentContext() };
if (first != nullptr) {
static_cast<Waiter*>(last)->next = &waiter;
} else {
first = &waiter;
}
last = &waiter;
while (!state) {
system->yield();
last = &waiter;
dispatcher->yield();
assert(dispatcher != nullptr);
}
}

View file

@ -17,12 +17,14 @@
#pragma once
class System;
namespace System {
class Dispatcher;
class Event {
public:
Event();
explicit Event(System& system);
explicit Event(Dispatcher& dispatcher);
Event(const Event&) = delete;
Event(Event&& other);
~Event();
@ -34,10 +36,10 @@ public:
void wait();
private:
struct Waiter;
System* system;
Waiter* first;
Waiter* last;
Dispatcher* dispatcher;
void* first;
void* last;
bool state;
};
}

View file

@ -15,5 +15,4 @@
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
bool transactions_generation_from_blockchain(std::string& blockchain_path);
#include "InterruptedException.h"

View file

@ -15,9 +15,9 @@
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
#include <assert.h>
#pragma once
static_assert(1, "FAIL");
int main(int argc, char *argv[]) {
return 0;
}
#include <exception>
class InterruptedException : public std::exception {
};

View file

@ -0,0 +1,297 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "TcpConnection.h"
#include <iostream>
#include <sys/epoll.h>
#include <errno.h>
#include <unistd.h>
#include <assert.h>
#include <stdexcept>
#include <sys/socket.h>
#include "Dispatcher.h"
#include "InterruptedException.h"
using namespace System;
namespace {
struct ConnectionContext : public Dispatcher::ContextExt {
bool interrupted;
};
}
TcpConnection::TcpConnection() : dispatcher(nullptr) {
}
TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), stopped(false), context(nullptr) {
epoll_event connectionEvent;
connectionEvent.data.fd = connection;
connectionEvent.events = 0;
connectionEvent.data.ptr = nullptr;
if (epoll_ctl(dispatcher.getEpoll(), EPOLL_CTL_ADD, socket, &connectionEvent) == -1) {
std::cerr << errno << std::endl;
throw std::runtime_error("epoll_ctl() fail");
}
}
TcpConnection::TcpConnection(TcpConnection&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
connection = other.connection;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
TcpConnection::~TcpConnection() {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (close(connection) == -1) {
std::cerr << "close() failed, errno=" << errno << '.' << std::endl;
}
}
}
TcpConnection& TcpConnection::operator=(TcpConnection&& other) {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (close(connection) == -1) {
std::cerr << "close() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpConnection::operator=");
}
}
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
connection = other.connection;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void TcpConnection::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
void TcpConnection::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
ConnectionContext *context2 = static_cast<ConnectionContext *>(context);
if (!context2->interrupted) {
epoll_event connectionEvent;
connectionEvent.data.fd = connection;
connectionEvent.events = 0;
connectionEvent.data.ptr = nullptr;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) {
std::cerr << errno << std::endl;
throw std::runtime_error("epoll_ctl() fail");
}
context2->interrupted = true;
if (context2->context != nullptr) {
dispatcher->pushContext(context2->context);
}
if (context2->writeContext != nullptr) {
dispatcher->pushContext(context2->writeContext);
}
}
}
stopped = true;
}
size_t TcpConnection::read(uint8_t* data, size_t size) {
assert(dispatcher != nullptr);
assert(context == nullptr || static_cast<Dispatcher::ContextExt*>(context)->context == nullptr);
if (stopped) {
throw InterruptedException();
}
ssize_t transferred = ::recv(connection, (void *)data, size, 0);
if (transferred == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
std::cerr << "recv failed, result=" << errno << '.' << std::endl;
} else {
epoll_event connectionEvent;
connectionEvent.data.fd = connection;
ConnectionContext context2;
if (context == nullptr) {
context2.writeContext = nullptr;
context2.interrupted = false;
context2.context = dispatcher->getCurrentContext();
context = &context2;
connectionEvent.events = EPOLLIN | EPOLLONESHOT;
} else {
assert(static_cast<Dispatcher::ContextExt*>(context)->writeContext != nullptr);
connectionEvent.events = EPOLLIN | EPOLLOUT | EPOLLONESHOT;
}
connectionEvent.data.ptr = context;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
} else {
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == dispatcher->getCurrentContext());
if (static_cast<ConnectionContext*>(context)->interrupted) {
context = nullptr;
throw InterruptedException();
}
assert(static_cast<Dispatcher::ContextExt*>(context)->context == context2.context);
if (static_cast<Dispatcher::ContextExt*>(context)->writeContext != nullptr) { //write is presented, rearm
static_cast<Dispatcher::ContextExt*>(context)->context = nullptr;
epoll_event connectionEvent;
connectionEvent.data.fd = connection;
connectionEvent.events = EPOLLOUT | EPOLLONESHOT;
connectionEvent.data.ptr = context;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpConnection::read");
}
} else {
context = nullptr;
}
ssize_t transferred = ::recv(connection, (void *)data, size, 0);
if (transferred == -1) {
std::cerr << "recv failed, errno=" << errno << '.' << std::endl;
} else {
if (transferred == 0) {
std::cerr << "recv return after yield with 0 bytes" << std::endl;
int retval = -1;
socklen_t retValLen = sizeof(retval);
int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen);
if (s == -1) {
std::cerr << "getsockopt() failed, errno=" << errno << '.' << std::endl;
} else {
std::cerr << "recv getsockopt retval = " << retval << std::endl;
}
}
assert(transferred <= size);
return transferred;
}
}
}
throw std::runtime_error("TcpConnection::read");
}
assert(transferred <= size);
return transferred;
}
void TcpConnection::write(const uint8_t* data, size_t size) {
assert(dispatcher != nullptr);
assert(context == nullptr || static_cast<Dispatcher::ContextExt*>(context)->writeContext == nullptr);
if (stopped) {
throw InterruptedException();
}
if (size == 0) {
if (shutdown(connection, SHUT_WR) == -1) {
std::cerr << "shutdown failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpConnection::write");
}
return;
}
ssize_t transferred = ::send(connection, (void *)data, size, 0);
if (transferred == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
std::cerr << "send failed, result=" << errno << '.' << std::endl;
} else {
epoll_event connectionEvent;
connectionEvent.data.fd = connection;
ConnectionContext context2;
if (context == nullptr) {
context2.context = nullptr;
context2.interrupted = false;
context2.writeContext = dispatcher->getCurrentContext();
context = &context2;
connectionEvent.events = EPOLLOUT | EPOLLONESHOT;
} else {
assert(static_cast<Dispatcher::ContextExt*>(context)->context != nullptr);
connectionEvent.events = EPOLLIN | EPOLLOUT | EPOLLONESHOT;
}
connectionEvent.data.ptr = context;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
} else {
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.writeContext == dispatcher->getCurrentContext());
if (static_cast<ConnectionContext*>(context)->interrupted) {
context = nullptr;
throw InterruptedException();
}
assert(static_cast<Dispatcher::ContextExt*>(context)->writeContext == context2.writeContext);
if (static_cast<Dispatcher::ContextExt*>(context)->context != nullptr) { //read is presented, rearm
static_cast<Dispatcher::ContextExt*>(context)->writeContext = nullptr;
epoll_event connectionEvent;
connectionEvent.data.fd = connection;
connectionEvent.events = EPOLLIN | EPOLLONESHOT;
connectionEvent.data.ptr = context;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpConnection::write");
}
} else {
context = nullptr;
}
ssize_t transferred = ::send(connection, (void *)data, size, 0);
if (transferred == -1) {
std::cerr << "send failed, errno=" << errno << '.' << std::endl;
} else {
if (transferred == 0) {
throw std::runtime_error("send transferred 0 bytes.");
}
assert(transferred == size);
return;
}
}
}
throw std::runtime_error("TcpConnection::write");
}
}

View file

@ -19,8 +19,11 @@
#include <cstddef>
#include <cstdint>
#include <stdint.h>
class System;
namespace System {
class Dispatcher;
class TcpConnection {
public:
@ -39,9 +42,12 @@ private:
friend class TcpConnector;
friend class TcpListener;
explicit TcpConnection(System& system, void* socket);
explicit TcpConnection(Dispatcher& dispatcher, int socket);
System* system;
void* socket;
Dispatcher* dispatcher;
int connection;
bool stopped;
void* context;
};
}

View file

@ -0,0 +1,213 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "TcpConnector.h"
#include <stdexcept>
#include <iostream>
#include <random>
#include <sstream>
#include <cassert>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include "Dispatcher.h"
#include "TcpConnection.h"
#include "InterruptedException.h"
using namespace System;
namespace {
struct ConnectorContext : public Dispatcher::ContextExt {
int connection;
bool interrupted;
};
}
TcpConnector::TcpConnector() : dispatcher(nullptr) {
}
TcpConnector::TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher), address(address), port(port), stopped(false), context(nullptr) {
}
TcpConnector::TcpConnector(TcpConnector&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
address = other.address;
port = other.port;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
TcpConnector::~TcpConnector() {
}
TcpConnector& TcpConnector::operator=(TcpConnector&& other) {
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
address = other.address;
port = other.port;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void TcpConnector::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
TcpConnection TcpConnector::connect() {
assert(dispatcher != nullptr);
assert(context == nullptr);
if (stopped) {
throw InterruptedException();
}
std::ostringstream portStream;
portStream << port;
addrinfo hints = { 0, AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL };
addrinfo *addressInfos;
int result = getaddrinfo(address.c_str(), portStream.str().c_str(), &hints, &addressInfos);
if (result == -1) {
std::cerr << "getaddrinfo failed, errno=" << errno << '.' << std::endl;
} else {
std::size_t count = 0;
for (addrinfo* addressInfo = addressInfos; addressInfo != nullptr; addressInfo = addressInfo->ai_next) {
++count;
}
std::random_device randomDevice;
std::mt19937 generator(randomDevice());
std::uniform_int_distribution<std::size_t> distribution(0, count - 1);
std::size_t index = distribution(generator);
addrinfo* addressInfo = addressInfos;
for (std::size_t i = 0; i < index; ++i) {
addressInfo = addressInfo->ai_next;
}
sockaddr_in addressData = *reinterpret_cast<sockaddr_in*>(addressInfo->ai_addr);
freeaddrinfo(addressInfo);
int connection = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (connection == -1) {
std::cerr << "socket failed, errno=" << errno << '.' << std::endl;
} else {
sockaddr_in bindAddress;
bindAddress.sin_family = AF_INET;
bindAddress.sin_port = 0;
bindAddress.sin_addr.s_addr = INADDR_ANY;
if (bind(connection, reinterpret_cast<sockaddr*>(&bindAddress), sizeof bindAddress) != 0) {
std::cerr << "bind failed, errno=" << errno << '.' << std::endl;
} else {
int flags = fcntl(connection, F_GETFL, 0);
if (flags == -1 || fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1) {
std::cerr << "fcntl() failed errno=" << errno << std::endl;
} else {
int result = ::connect(connection, reinterpret_cast<sockaddr *>(&addressData), sizeof addressData);
if (result == -1) {
if (errno == EINPROGRESS) {
ConnectorContext context2;
context2.writeContext = dispatcher->getCurrentContext();
context2.context = nullptr;
context2.interrupted = false;
context2.connection = connection;
context = &context2;
epoll_event connectEvent;
connectEvent.data.fd = connection;
connectEvent.events = EPOLLOUT | EPOLLRDHUP | EPOLLERR | EPOLLONESHOT;
connectEvent.data.ptr = context;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_ADD, connection, &connectEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
} else {
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.writeContext == dispatcher->getCurrentContext());
assert(context == &context2);
context = nullptr;
context2.writeContext = nullptr;
if (context2.interrupted) {
if (close(connection) == -1) {
std::cerr << "close failed, errno=" << errno << std::endl;
}
throw InterruptedException();
}
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_DEL, connection, NULL) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
} else {
int retval = -1;
socklen_t retValLen = sizeof(retval);
int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen);
if (s == -1) {
std::cerr << "getsockopt() failed, errno=" << errno << '.' << std::endl;
} else {
if (retval != 0) {
std::cerr << "connect failed; getsockopt retval = " << retval << std::endl;
} else {
return TcpConnection(*dispatcher, connection);
}
}
}
}
}
} else {
return TcpConnection(*dispatcher, connection);
}
}
}
if (close(connection) == -1) {
std::cerr << "close failed, errno=" << errno << std::endl;
}
}
}
throw std::runtime_error("TcpConnector::connect");
}
void TcpConnector::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
ConnectorContext* context2 = static_cast<ConnectorContext*>(context);
if (!context2->interrupted) {
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_DEL, context2->connection, NULL) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpConnector::stop");
}
dispatcher->pushContext(context2->writeContext);
context2->interrupted = true;
}
}
stopped = true;
}

View file

@ -0,0 +1,50 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <cstdint>
#include <string>
#include <stdint.h>
namespace System {
class Dispatcher;
class TcpConnection;
class TcpConnector {
public:
TcpConnector();
TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port);
TcpConnector(const TcpConnector&) = delete;
TcpConnector(TcpConnector&& other);
~TcpConnector();
TcpConnector& operator=(const TcpConnector&) = delete;
TcpConnector& operator=(TcpConnector&& other);
void start();
void stop();
TcpConnection connect();
private:
Dispatcher* dispatcher;
std::string address;
uint16_t port;
bool stopped;
void* context;
};
}

View file

@ -0,0 +1,207 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "TcpListener.h"
#include <sys/epoll.h>
#include <unistd.h>
#include <netdb.h>
#include <fcntl.h>
#include <assert.h>
#include <iostream>
#include <cassert>
#include <errno.h>
#include <stdexcept>
#include "Dispatcher.h"
#include "TcpConnection.h"
#include "InterruptedException.h"
using namespace System;
namespace {
struct ListenerContext : public Dispatcher::ContextExt {
bool interrupted;
};
}
TcpListener::TcpListener() : dispatcher(nullptr) {
}
TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
listener = other.listener;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
TcpListener::~TcpListener() {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (close(listener) == -1) {
std::cerr << "close() failed, errno=" << errno << '.' << std::endl;
}
}
}
TcpListener& TcpListener::operator=(TcpListener&& other) {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (close(listener) == -1) {
std::cerr << "close() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpListener::operator=");
}
}
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
listener = other.listener;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void TcpListener::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
TcpListener::TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher) {
listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listener == -1) {
std::cerr << "socket failed, errno=" << errno << std::endl;
} else {
int flags = fcntl(listener, F_GETFL, 0);
if (flags == -1 || fcntl(listener, F_SETFL, flags | O_NONBLOCK) == -1) {
std::cerr << "fcntl() failed errno=" << errno << std::endl;
} else {
sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = INADDR_ANY;
if (bind(listener, reinterpret_cast<sockaddr*>(&address), sizeof address) != 0) {
std::cerr << "bind failed, errno=" << errno << std::endl;
} else if (listen(listener, SOMAXCONN) != 0) {
std::cerr << "listen failed, errno=" << errno << std::endl;
} else {
epoll_event listenEvent;
listenEvent.data.fd = listener;
listenEvent.events = 0;
listenEvent.data.ptr = nullptr;
if (epoll_ctl(dispatcher.getEpoll(), EPOLL_CTL_ADD, listener, &listenEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
} else {
stopped = false;
context = nullptr;
return;
}
}
}
if (close(listener) == -1) {
std::cerr << "close failed, errno=" << errno << std::endl;
}
}
throw std::runtime_error("TcpListener::TcpListener");
}
TcpConnection TcpListener::accept() {
assert(dispatcher != nullptr);
assert(context == nullptr);
if (stopped) {
throw InterruptedException();
}
ListenerContext context2;
context2.context = dispatcher->getCurrentContext();
context2.writeContext = nullptr;
context2.interrupted = false;
epoll_event listenEvent;
listenEvent.data.fd = listener;
listenEvent.events = EPOLLIN | EPOLLONESHOT;
listenEvent.data.ptr = &context2;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, listener, &listenEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
} else {
context = &context2;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == dispatcher->getCurrentContext());
assert(context2.writeContext == nullptr);
assert(context == &context2);
context = nullptr;
context2.context = nullptr;
if (context2.interrupted) {
if (close(listener) == -1) {
std::cerr << "close failed, errno=" << errno << std::endl;
}
throw InterruptedException();
}
sockaddr inAddr;
socklen_t inLen = sizeof(inAddr);
int connection = ::accept(listener, &inAddr, &inLen);
if (connection == -1) {
std::cerr << "accept() failed, errno=" << errno << '.' << std::endl;
} else {
int flags = fcntl(connection, F_GETFL, 0);
if (flags == -1 || fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1) {
std::cerr << "fcntl() failed errno=" << errno << std::endl;
} else {
return TcpConnection(*dispatcher, connection);
}
}
}
throw std::runtime_error("TcpListener::accept");
}
void TcpListener::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
ListenerContext* context2 = static_cast<ListenerContext*>(context);
if (!context2->interrupted) {
context2->interrupted = true;
epoll_event listenEvent;
listenEvent.data.fd = listener;
listenEvent.events = 0;
listenEvent.data.ptr = nullptr;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, listener, &listenEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpListener::stop");
}
dispatcher->pushContext(context2->context);
}
}
stopped = true;
}

View file

@ -0,0 +1,49 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <cstdint>
#include <string>
#include <stdint.h>
namespace System {
class Dispatcher;
class TcpConnection;
class TcpListener {
public:
TcpListener();
TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port);
TcpListener(const TcpListener&) = delete;
TcpListener(TcpListener&& other);
~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;
};
}

View file

@ -0,0 +1,160 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "Timer.h"
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <assert.h>
#include <unistd.h>
#include <iostream>
#include <stdexcept>
#include <errno.h>
#include "Dispatcher.h"
#include "InterruptedException.h"
using namespace System;
namespace {
struct TimerContext : public Dispatcher::ContextExt {
Dispatcher* dispatcher;
bool interrupted;
};
}
Timer::Timer() : dispatcher(nullptr) {
}
Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr) {
timer = timerfd_create(CLOCK_MONOTONIC, 0);
epoll_event timerEvent;
timerEvent.data.fd = timer;
timerEvent.events = 0;
timerEvent.data.ptr = nullptr;
if (epoll_ctl(this->dispatcher->getEpoll(), EPOLL_CTL_ADD, timer, &timerEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("Timer::Timer");
}
}
Timer::Timer(Timer&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
timer = other.timer;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
Timer::~Timer() {
if (dispatcher != nullptr) {
close(timer);
}
}
Timer& Timer::operator=(Timer&& other) {
if (dispatcher != nullptr) {
assert(context == nullptr);
close(timer);
}
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
timer = other.timer;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void Timer::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
void Timer::sleep(std::chrono::milliseconds duration) {
assert(dispatcher != nullptr);
assert(context == nullptr);
if (stopped) {
throw InterruptedException();
}
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration);
itimerspec expires;
expires.it_interval.tv_nsec = expires.it_interval.tv_sec = 0;
expires.it_value.tv_sec = seconds.count();
expires.it_value.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(duration - seconds).count();
timerfd_settime(timer, 0, &expires, NULL);
TimerContext context2;
context2.dispatcher = dispatcher;
context2.context = dispatcher->getCurrentContext();
context2.writeContext = nullptr;
context2.interrupted = false;
epoll_event timerEvent;
timerEvent.data.fd = timer;
timerEvent.events = EPOLLIN | EPOLLONESHOT;
timerEvent.data.ptr = &context2;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, timer, &timerEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("Timer::sleep");
}
context = &context2;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == dispatcher->getCurrentContext());
assert(context2.writeContext == nullptr);
assert(context == &context2);
context = nullptr;
context2.context = nullptr;
if (context2.interrupted) {
throw InterruptedException();
}
}
void Timer::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
TimerContext* context2 = reinterpret_cast<TimerContext*>(context);
if (context2->context != nullptr) {
epoll_event timerEvent;
timerEvent.data.fd = timer;
timerEvent.events = 0;
timerEvent.data.ptr = nullptr;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, timer, &timerEvent) == -1) {
std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("Timer::sleep");
}
dispatcher->pushContext(context2->context);
context2->interrupted = true;
}
}
stopped = true;
}

View file

@ -0,0 +1,46 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <chrono>
namespace System {
class Dispatcher;
class Timer {
public:
Timer();
explicit Timer(Dispatcher& dispatcher);
Timer(const Timer&) = delete;
Timer(Timer&& other);
~Timer();
Timer& operator=(const Timer&) = delete;
Timer& operator=(Timer&& other);
void start();
void stop();
void sleep(std::chrono::milliseconds duration);
private:
Dispatcher* dispatcher;
int timer;
bool stopped;
void* context;
};
}

View file

@ -0,0 +1,169 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "Dispatcher.h"
#include <iostream>
#define _XOPEN_SOURCE
#include <ucontext.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/event.h>
#include <assert.h>
#include <sys/time.h>
using namespace System;
void Dispatcher::contextProcedureStatic(void *context) {
reinterpret_cast<Dispatcher*>(context)->contextProcedure();
}
Dispatcher::Dispatcher() : lastCreatedTimer(0) {
kqueue = ::kqueue();
if (kqueue == -1) {
std::cerr << "kqueue() fail errno=" << errno << std::endl;
} else {
currentContext = new ucontext_t;
if (getcontext(reinterpret_cast<ucontext_t*>(currentContext)) == -1) {
std::cerr << "getcontext() fail errno=" << errno << std::endl;
} else {
contextCount = 0;
return;
}
}
throw std::runtime_error("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<ucontext_t*>(reusableContexts.top());
reusableContexts.pop();
}
while (!timers.empty()) {
timers.pop();
}
if (-1 == close(kqueue)) {
std::cerr << "close() fail errno=" << errno << std::endl;
}
}
void* Dispatcher::getCurrentContext() const {
return currentContext;
}
int Dispatcher::getKqueue() const {
return kqueue;
}
void Dispatcher::pushContext(void* context) {
resumingContexts.push(context);
}
void Dispatcher::spawn(std::function<void()>&& procedure) {
void* context;
if (reusableContexts.empty()) {
context = new ucontext_t;
if (-1 == getcontext(reinterpret_cast<ucontext_t *>(context))) { //makecontext precondition
std::cerr << "getcontext() fail errno=" << errno << std::endl;
throw std::runtime_error("Dispatcher::spawn()");
}
auto stackPointer = new uint8_t[64 * 1024];
reinterpret_cast<ucontext_t *>(context)->uc_stack.ss_sp = stackPointer;
allocatedStacks.push(stackPointer);
reinterpret_cast<ucontext_t *>(context)->uc_stack.ss_size = 64 * 1024;
makecontext(reinterpret_cast<ucontext_t *>(context), (void(*)())contextProcedureStatic, 1, reinterpret_cast<int*>(this));
++contextCount;
} else {
context = reusableContexts.top();
reusableContexts.pop();
}
resumingContexts.push(context);
spawningProcedures.emplace(std::move(procedure));
}
int Dispatcher::getTimer() {
int timer;
if (timers.empty()) {
timer = ++lastCreatedTimer;
} else {
timer = timers.top();
timers.pop();
}
return timer;
}
void Dispatcher::pushTimer(int timer) {
timers.push(timer);
}
void Dispatcher::clear() {
//TODO
}
void Dispatcher::yield() {
void* context;
for (;;) {
if (!resumingContexts.empty()) {
context = resumingContexts.front();
resumingContexts.pop();
break;
}
struct kevent event;
int count = kevent(kqueue, NULL, 0, &event, 1, NULL);
if (count == 1) {
context = static_cast<ContextExt*>(event.udata)->context;
break;
}
if (errno != EINTR) {
std::cerr << "kevent() failed, errno=" << errno << std::endl;
throw std::runtime_error("Dispatcher::yield()");
}
}
if (context != currentContext) {
ucontext_t* oldContext = static_cast<ucontext_t*>(currentContext);
currentContext = context;
if (-1 == swapcontext(oldContext, static_cast<ucontext_t *>(context))) {
std::cerr << "setcontext() failed, errno=" << errno << std::endl;
throw std::runtime_error("Dispatcher::yield()");
}
}
}
void Dispatcher::contextProcedure() {
void* context = currentContext;
for (;;) {
assert(!spawningProcedures.empty());
std::function<void()> procedure = std::move(spawningProcedures.front());
spawningProcedures.pop();
procedure();
reusableContexts.push(context);
yield();
}
}

View file

@ -0,0 +1,66 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <functional>
#include <queue>
#include <stack>
namespace System {
class Dispatcher {
public:
Dispatcher();
Dispatcher(const Dispatcher&) = delete;
~Dispatcher();
Dispatcher& operator=(const Dispatcher&) = delete;
void spawn(std::function<void()>&& procedure);
void yield();
void clear();
struct ContextExt {
void *context;
};
private:
friend class Event;
friend class DispatcherAccessor;
friend class TcpConnection;
friend class TcpConnector;
friend class TcpListener;
friend class Timer;
int kqueue;
void* currentContext;
int lastCreatedTimer;
std::size_t contextCount;
std::queue<void*> resumingContexts;
std::stack<void*> reusableContexts;
std::stack<uint8_t *> allocatedStacks;
std::queue<std::function<void()>> spawningProcedures;
std::stack<int> timers;
int getKqueue() const;
int getTimer();
void pushTimer(int timer);
void pushContext(void* context);
void* getCurrentContext() const;
void contextProcedure();
static void contextProcedureStatic(void* context);
};
}

106
src/Platform/OSX/System/Event.cpp Executable file
View file

@ -0,0 +1,106 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "Event.h"
#include <cassert>
#include <iostream>
#include "Dispatcher.h"
using namespace System;
namespace {
struct Waiter {
Waiter* next;
void* context;
};
}
Event::Event() : dispatcher(nullptr) {
}
Event::Event(Dispatcher& dispatcher) : dispatcher(&dispatcher), first(nullptr), state(false) {
}
Event::Event(Event&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
first = other.first;
if (other.first != nullptr) {
last = other.last;
}
state = other.state;
other.dispatcher = nullptr;
}
}
Event::~Event() {
assert(first == nullptr);
}
Event& Event::operator=(Event&& other) {
assert(first == nullptr);
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
first = other.first;
if (other.first != nullptr) {
last = other.last;
}
state = other.state;
other.dispatcher = nullptr;
}
return *this;
}
bool Event::get() const {
assert(dispatcher != nullptr);
return state;
}
void Event::clear() {
assert(dispatcher != nullptr);
state = false;
}
void Event::set() {
assert(dispatcher != nullptr);
state = true;
for (Waiter* waiter = static_cast<Waiter*>(first); waiter != nullptr; waiter = waiter->next) {
dispatcher->pushContext(waiter->context);
}
first = nullptr;
}
void Event::wait() {
assert(dispatcher != nullptr);
if (!state) {
Waiter waiter = { nullptr, dispatcher->getCurrentContext() };
if (first != nullptr) {
static_cast<Waiter*>(last)->next = &waiter;
} else {
first = &waiter;
}
last = &waiter;
dispatcher->yield();
assert(dispatcher != nullptr);
}
}

45
src/Platform/OSX/System/Event.h Executable file
View file

@ -0,0 +1,45 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
namespace System {
class Dispatcher;
class Event {
public:
Event();
explicit Event(Dispatcher& dispatcher);
Event(const Event&) = delete;
Event(Event&& other);
~Event();
Event& operator=(const Event&) = delete;
Event& operator=(Event&& other);
bool get() const;
void clear();
void set();
void wait();
private:
Dispatcher* dispatcher;
void* first;
void* last;
bool state;
};
}

View file

@ -0,0 +1,18 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "InterruptedException.h"

View file

@ -0,0 +1,27 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <exception>
namespace System {
class InterruptedException : public std::exception {
};
}

View file

@ -0,0 +1,247 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "TcpConnection.h"
#include <unistd.h>
#include <assert.h>
#include <iostream>
#include <sys/socket.h>
#include <sys/event.h>
#include <sys/socket.h>
#include "Dispatcher.h"
#include "InterruptedException.h"
using namespace System;
namespace {
struct ConnectionContext : public Dispatcher::ContextExt {
bool interrupted;
};
}
TcpConnection::TcpConnection() : dispatcher(nullptr) {
}
TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), stopped(false), readContext(nullptr), writeContext(nullptr) {
}
TcpConnection::TcpConnection(TcpConnection&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
connection = other.connection;
stopped = other.stopped;
readContext = other.readContext;
writeContext = other.writeContext;
other.dispatcher = nullptr;
}
}
TcpConnection::~TcpConnection() {
if (dispatcher != nullptr) {
assert(readContext == nullptr);
assert(writeContext == nullptr);
if (close(connection) == -1) {
std::cerr << "close() failed, errno=" << errno << '.' << std::endl;
}
}
}
TcpConnection& TcpConnection::operator=(TcpConnection&& other) {
if (dispatcher != nullptr) {
assert(readContext == nullptr);
assert(writeContext == nullptr);
if (close(connection) == -1) {
std::cerr << "close() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpConnection::operator=");
}
}
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
connection = other.connection;
stopped = other.stopped;
readContext = other.readContext;
writeContext = other.writeContext;
other.dispatcher = nullptr;
}
return *this;
}
void TcpConnection::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
size_t TcpConnection::read(uint8_t* data, size_t size) {
assert(dispatcher != nullptr);
assert(readContext == nullptr);
if (stopped) {
throw InterruptedException();
}
ssize_t transferred = ::recv(connection, (void *)data, size, 0);
if (transferred == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
std::cerr << "recv failed, result=" << errno << '.' << std::endl;
} else {
ConnectionContext context2;
context2.context = dispatcher->getCurrentContext();
context2.interrupted = false;
struct kevent event;
EV_SET(&event, connection, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR | EV_ONESHOT, 0, 0, &context2);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
} else {
readContext = &context2;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == dispatcher->getCurrentContext());
assert(readContext == &context2);
readContext = nullptr;
context2.context = nullptr;
if (context2.interrupted) {
throw InterruptedException();
}
ssize_t transferred = ::recv(connection, (void *)data, size, 0);
if (transferred == -1) {
std::cerr << "recv failed, errno=" << errno << '.' << std::endl;
} else {
if (transferred == 0) {
std::cerr << "recv return after yield with 0 bytes" << std::endl;
int retval = -1;
socklen_t retValLen = sizeof(retval);
int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen);
if (s == -1) {
std::cerr << "getsockopt() failed, errno=" << errno << '.' << std::endl;
} else {
std::cerr << "recv getsockopt retval = " << retval << std::endl;
}
}
assert(transferred <= size);
return transferred;
}
}
}
throw std::runtime_error("TcpConnection::read");
}
assert(transferred <= size);
return transferred;
}
void TcpConnection::write(const uint8_t* data, size_t size) {
assert(dispatcher != nullptr);
assert(writeContext == nullptr);
if (stopped) {
throw InterruptedException();
}
if (size == 0) {
if (shutdown(connection, SHUT_WR) == -1) {
std::cerr << "shutdown failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpConnection::write");
}
return;
}
ssize_t transferred = ::send(connection, (void *)data, size, 0);
if (transferred == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
std::cerr << "send failed, result=" << errno << '.' << std::endl;
} else {
ConnectionContext context2;
context2.context = dispatcher->getCurrentContext();
context2.interrupted = false;
struct kevent event;
EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, 0, &context2);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
} else {
writeContext = &context2;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == dispatcher->getCurrentContext());
assert(writeContext == &context2);
writeContext = nullptr;
context2.context = nullptr;
if (context2.interrupted) {
throw InterruptedException();
}
ssize_t transferred = ::send(connection, (void *)data, size, 0);
if (transferred == -1) {
std::cerr << "recv failed, errno=" << errno << '.' << std::endl;
} else {
if (transferred == 0) {
throw std::runtime_error("send transferred 0 bytes.");
}
assert(transferred == size);
return;
}
}
}
throw std::runtime_error("TcpConnection::write");
}
}
void TcpConnection::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (writeContext != nullptr && static_cast<ConnectionContext*>(writeContext)->context != nullptr) {
ConnectionContext* context2 = static_cast<ConnectionContext*>(writeContext);
if (!context2->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) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpListener::stop");
}
context2->interrupted = true;
dispatcher->pushContext(context2->context);
}
}
if (readContext != nullptr && static_cast<ConnectionContext*>(readContext)->context != nullptr) {
ConnectionContext* context2 = static_cast<ConnectionContext*>(readContext);
if (!context2->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) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpListener::stop");
}
context2->interrupted = true;
dispatcher->pushContext(context2->context);
}
}
stopped = true;
}

View file

@ -0,0 +1,53 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <cstddef>
#include <cstdint>
namespace System {
class Dispatcher;
class TcpConnection {
public:
TcpConnection();
TcpConnection(const TcpConnection&) = delete;
TcpConnection(TcpConnection&& other);
~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);
void write(const uint8_t* data, std::size_t size);
private:
friend class TcpConnector;
friend class TcpListener;
explicit TcpConnection(Dispatcher& dispatcher, int socket);
Dispatcher* dispatcher;
int connection;
bool stopped;
void* readContext;
void* writeContext;
};
}

View file

@ -0,0 +1,213 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>
#include "TcpConnector.h"
#include <cassert>
#include <iostream>
#include <random>
#include <sstream>
#include <sys/event.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>
#include "InterruptedException.h"
#include "Dispatcher.h"
#include "TcpConnection.h"
using namespace System;
namespace {
struct ConnectorContext : public Dispatcher::ContextExt {
int connection;
bool interrupted;
};
}
TcpConnector::TcpConnector() : dispatcher(nullptr) {
}
TcpConnector::TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher), address(address), port(port), stopped(false), context(nullptr) {
}
TcpConnector::TcpConnector(TcpConnector&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
address = other.address;
port = other.port;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
TcpConnector::~TcpConnector() {
}
TcpConnector& TcpConnector::operator=(TcpConnector&& other) {
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
address = other.address;
port = other.port;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void TcpConnector::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
void TcpConnector::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
ConnectorContext* context2 = static_cast<ConnectorContext*>(context);
if (!context2->interrupted) {
struct kevent event;
EV_SET(&event, context2->connection, EVFILT_WRITE, EV_DELETE | EV_DISABLE, 0, 0, NULL);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpConnector::stop");
}
dispatcher->pushContext(context2->context);
context2->interrupted = true;
}
}
stopped = true;
}
TcpConnection TcpConnector::connect() {
assert(dispatcher != nullptr);
assert(context == nullptr);
if (stopped) {
throw InterruptedException();
}
std::ostringstream portStream;
portStream << port;
addrinfo hints = { 0, AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL };
addrinfo *addressInfos;
int result = getaddrinfo(address.c_str(), portStream.str().c_str(), &hints, &addressInfos);
if (result == -1) {
std::cerr << "getaddrinfo failed, errno=" << errno << '.' << std::endl;
} else {
std::size_t count = 0;
for (addrinfo* addressInfo = addressInfos; addressInfo != nullptr; addressInfo = addressInfo->ai_next) {
++count;
}
std::random_device randomDevice;
std::mt19937 generator(randomDevice());
std::uniform_int_distribution<std::size_t> distribution(0, count - 1);
std::size_t index = distribution(generator);
addrinfo* addressInfo = addressInfos;
for (std::size_t i = 0; i < index; ++i) {
addressInfo = addressInfo->ai_next;
}
sockaddr_in addressData = *reinterpret_cast<sockaddr_in*>(addressInfo->ai_addr);
freeaddrinfo(addressInfo);
int connection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (connection == -1) {
std::cerr << "socket failed, errno=" << errno << '.' << std::endl;
} else {
sockaddr_in bindAddress;
bindAddress.sin_family = AF_INET;
bindAddress.sin_port = 0;
bindAddress.sin_addr.s_addr = INADDR_ANY;
if (bind(connection, reinterpret_cast<sockaddr*>(&bindAddress), sizeof bindAddress) != 0) {
std::cerr << "bind failed, errno=" << errno << '.' << std::endl;
} else {
int flags = fcntl(connection, F_GETFL, 0);
if (flags == -1 || (fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1)) {
std::cerr << "fcntl() failed errno=" << errno << std::endl;
} else {
int result = ::connect(connection, reinterpret_cast<sockaddr *>(&addressData), sizeof addressData);
if (result == -1) {
if (errno == EINPROGRESS) {
ConnectorContext context2;
context2.context = dispatcher->getCurrentContext();
context2.interrupted = false;
context2.connection = connection;
struct kevent event;
EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_ENABLE | EV_ONESHOT | EV_CLEAR, 0, 0, &context2);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
} else {
context = &context2;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == dispatcher->getCurrentContext());
assert(context == &context2);
context = nullptr;
context2.context = nullptr;
if (context2.interrupted) {
if (close(connection) == -1) {
std::cerr << "close failed, errno=" << errno << std::endl;
}
throw InterruptedException();
}
struct kevent event;
EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_DISABLE, 0, 0, NULL);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
} else {
int retval = -1;
socklen_t retValLen = sizeof(retval);
int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen);
if (s == -1) {
std::cerr << "getsockopt() failed, errno=" << errno << '.' << std::endl;
} else {
if (retval != 0) {
std::cerr << "connect failed; getsockopt retval = " << retval << std::endl;
} else {
return TcpConnection(*dispatcher, connection);
}
}
}
}
}
} else {
return TcpConnection(*dispatcher, connection);
}
}
}
if (close(connection) == -1) {
std::cerr << "close failed, errno=" << errno << std::endl;
}
}
}
throw std::runtime_error("TcpConnector::connect");
}

View file

@ -20,22 +20,30 @@
#include <cstdint>
#include <string>
class System;
namespace System {
class Dispatcher;
class TcpConnection;
class TcpConnector {
public:
TcpConnector();
TcpConnector(System& system, const std::string& address, uint16_t port);
TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port);
TcpConnector(const TcpConnector&) = delete;
TcpConnector(TcpConnector&& other);
~TcpConnector();
TcpConnector& operator=(const TcpConnector&) = delete;
TcpConnector& operator=(TcpConnector&& other);
void start();
void stop();
TcpConnection connect();
private:
System* system;
std::string m_address;
uint16_t m_port;
Dispatcher* dispatcher;
std::string address;
uint16_t port;
bool stopped;
void* context;
};
}

View file

@ -0,0 +1,202 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "TcpListener.h"
#include <cassert>
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/event.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "InterruptedException.h"
#include "Dispatcher.h"
#include "TcpConnection.h"
using namespace System;
namespace {
struct ListenerContext : public Dispatcher::ContextExt {
bool interrupted;
};
}
TcpListener::TcpListener() : dispatcher(nullptr) {
}
TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
listener = other.listener;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
TcpListener::~TcpListener() {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (close(listener) == -1) {
std::cerr << "close() failed, errno=" << errno << '.' << std::endl;
}
}
}
TcpListener& TcpListener::operator=(TcpListener&& other) {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (close(listener) == -1) {
std::cerr << "close() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpListener::operator=");
}
}
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
listener = other.listener;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void TcpListener::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
TcpListener::TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher) {
listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listener == -1) {
std::cerr << "socket failed, errno=" << errno << std::endl;
} else {
int flags = fcntl(listener, F_GETFL, 0);
if (flags == -1 || (fcntl(listener, F_SETFL, flags | O_NONBLOCK) == -1)) {
std::cerr << "fcntl() failed errno=" << errno << std::endl;
} else {
sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = INADDR_ANY;
if (bind(listener, reinterpret_cast<sockaddr*>(&address), sizeof address) != 0) {
std::cerr << "bind failed, errno=" << errno << std::endl;
} else if (listen(listener, SOMAXCONN) != 0) {
std::cerr << "listen failed, errno=" << errno << std::endl;
} else {
struct kevent event;
EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_DISABLE, 0, SOMAXCONN, NULL);
if (kevent(dispatcher.getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
} else {
stopped = false;
context = nullptr;
return;
}
}
}
if (close(listener) == -1) {
std::cerr << "close failed, errno=" << errno << std::endl;
}
}
throw std::runtime_error("TcpListener::TcpListener");
}
TcpConnection TcpListener::accept() {
assert(dispatcher != nullptr);
assert(context == nullptr);
if (stopped) {
throw InterruptedException();
}
ListenerContext context2;
context2.context = dispatcher->getCurrentContext();
context2.interrupted = false;
struct kevent event;
EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, SOMAXCONN, &context2);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
} else {
context = &context2;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == dispatcher->getCurrentContext());
assert(context == &context2);
context = nullptr;
context2.context = nullptr;
if (context2.interrupted) {
if (close(listener) == -1) {
std::cerr << "close failed, errno=" << errno << std::endl;
}
throw InterruptedException();
}
struct kevent event;
EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_DISABLE, 0, 0, NULL);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
} else {
sockaddr inAddr;
socklen_t inLen = sizeof(inAddr);
int connection = ::accept(listener, &inAddr, &inLen);
if (connection == -1) {
std::cerr << "accept() failed, errno=" << errno << '.' << std::endl;
} else {
int flags = fcntl(connection, F_GETFL, 0);
if (flags == -1 || (fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1)) {
std::cerr << "fcntl() failed errno=" << errno << std::endl;
} else {
return TcpConnection(*dispatcher, connection);
}
}
}
}
throw std::runtime_error("TcpListener::accept");
}
void TcpListener::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
ListenerContext* context2 = static_cast<ListenerContext*>(context);
if (!context2->interrupted) {
context2->interrupted = true;
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) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("TcpListener::stop");
}
dispatcher->pushContext(context2->context);
}
}
stopped = true;
}

View file

@ -20,13 +20,15 @@
#include <cstdint>
#include <string>
class System;
namespace System {
class Dispatcher;
class TcpConnection;
class TcpListener {
public:
TcpListener();
TcpListener(System& system, const std::string& address, uint16_t port);
TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port);
TcpListener(const TcpListener&) = delete;
TcpListener(TcpListener&& other);
~TcpListener();
@ -37,7 +39,10 @@ public:
TcpConnection accept();
private:
System* system;
void* listener;
Dispatcher* dispatcher;
int listener;
bool stopped;
void* context;
};
}

137
src/Platform/OSX/System/Timer.cpp Executable file
View file

@ -0,0 +1,137 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "Timer.h"
#include <cassert>
#include <iostream>
#include <sys/event.h>
#include <sys/time.h>
#include <assert.h>
#include <unistd.h>
#include "Dispatcher.h"
#include "InterruptedException.h"
using namespace System;
namespace {
struct TimerContext : public Dispatcher::ContextExt {
Dispatcher* dispatcher;
bool interrupted;
};
}
Timer::Timer() : dispatcher(nullptr) {
}
Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr) {
timer = dispatcher.getTimer();
}
Timer::~Timer() {
if (dispatcher != nullptr) {
assert(context == nullptr);
dispatcher->pushTimer(timer);
}
}
Timer::Timer(Timer&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
timer = other.timer;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
Timer& Timer::operator=(Timer&& other) {
if (dispatcher != nullptr) {
assert(context == nullptr);
dispatcher->pushTimer(timer);
}
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
timer = other.timer;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void Timer::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
void Timer::sleep(std::chrono::milliseconds duration) {
assert(dispatcher != nullptr);
assert(context == nullptr);
if (stopped) {
throw InterruptedException();
}
TimerContext context2;
context2.dispatcher = dispatcher;
context2.context = dispatcher->getCurrentContext();
context2.interrupted = false;
struct kevent event;
EV_SET(&event, timer, EVFILT_TIMER, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, duration.count(), &context2);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("Timer::sleep");
}
context = &context2;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == dispatcher->getCurrentContext());
assert(context == &context2);
context = nullptr;
context2.context = nullptr;
if (context2.interrupted) {
throw InterruptedException();
}
}
void Timer::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
TimerContext* context2 = reinterpret_cast<TimerContext*>(context);
if (context2->context != nullptr) {
struct kevent event;
EV_SET(&event, timer, EVFILT_TIMER, EV_DISABLE, 0, 0, NULL);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl;
throw std::runtime_error("Timer::stop");
}
dispatcher->pushContext(context2->context);
context2->interrupted = true;
}
}
stopped = true;
}

46
src/Platform/OSX/System/Timer.h Executable file
View file

@ -0,0 +1,46 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <chrono>
namespace System {
class Dispatcher;
class Timer {
public:
Timer();
explicit Timer(Dispatcher& dispatcher);
Timer(const Timer&) = delete;
Timer(Timer&& other);
~Timer();
Timer& operator=(const Timer&) = delete;
Timer& operator=(Timer&& other);
void start();
void stop();
void sleep(std::chrono::milliseconds duration);
private:
Dispatcher* dispatcher;
int timer;
bool stopped;
void* context;
};
}

View file

@ -0,0 +1,186 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "Dispatcher.h"
#include <cassert>
#include <iostream>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
using namespace System;
namespace {
struct OverlappedExt : public OVERLAPPED {
void* context;
};
}
Dispatcher::Dispatcher() {
completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (completionPort == NULL) {
std::cerr << "CreateIoCompletionPort failed, result=" << GetLastError() << '.' << std::endl;
} else {
if (ConvertThreadToFiberEx(NULL, 0) == NULL) {
std::cerr << "ConvertThreadToFiberEx failed, result=" << GetLastError() << '.' << std::endl;
} else {
WSADATA wsaData;
int result = WSAStartup(0x0202, &wsaData);
if (result != 0) {
std::cerr << "WSAStartup failed, result=" << result << '.' << std::endl;
} else {
contextCount = 0;
return;
}
if (ConvertFiberToThread() != TRUE) {
std::cerr << "ConvertFiberToThread failed, result=" << GetLastError() << '.' << std::endl;
}
}
if (CloseHandle(completionPort) != TRUE) {
std::cerr << "CloseHandle failed, result=" << GetLastError() << '.' << std::endl;
}
}
throw std::runtime_error("Dispatcher::Dispatcher");
}
Dispatcher::~Dispatcher() {
assert(resumingContexts.empty());
assert(reusableContexts.size() == contextCount);
assert(spawningProcedures.empty());
while (!reusableContexts.empty()) {
DeleteFiber(reusableContexts.top());
reusableContexts.pop();
}
while (!timers.empty()) {
if (CloseHandle(timers.top()) != TRUE) {
std::cerr << "CloseHandle failed, result=" << GetLastError() << '.' << std::endl;
}
timers.pop();
}
if (WSACleanup() != 0) {
std::cerr << "WSACleanup failed, result=" << WSAGetLastError() << '.' << std::endl;
}
if (ConvertFiberToThread() != TRUE) {
std::cerr << "ConvertFiberToThread failed, result=" << GetLastError() << '.' << std::endl;
}
if (CloseHandle(completionPort) != TRUE) {
std::cerr << "CloseHandle failed, result=" << GetLastError() << '.' << std::endl;
}
}
void* Dispatcher::getCompletionPort() const {
return completionPort;
}
void* Dispatcher::getTimer() {
void* timer;
if (timers.empty()) {
timer = CreateWaitableTimer(NULL, FALSE, NULL);
if (timer == NULL) {
std::cerr << "CreateWaitableTimer failed, result=" << GetLastError() << '.' << std::endl;
throw std::runtime_error("Dispatcher::getTimer");
}
} else {
timer = timers.top();
timers.pop();
}
return timer;
}
void Dispatcher::pushTimer(void* timer) {
timers.push(timer);
}
void Dispatcher::pushContext(void* context) {
resumingContexts.push(context);
}
void Dispatcher::spawn(std::function<void()>&& procedure) {
void* context;
if (reusableContexts.empty()) {
context = CreateFiberEx(16384, 65536, 0, contextProcedureStatic, this);
if (context == NULL) {
std::cerr << "CreateFiberEx failed, result=" << GetLastError() << '.' << std::endl;
throw std::runtime_error("Dispatcher::spawn");
}
++contextCount;
} else {
context = reusableContexts.top();
reusableContexts.pop();
}
resumingContexts.push(context);
spawningProcedures.emplace(std::move(procedure));
}
void Dispatcher::clear() {
//TODO
}
void Dispatcher::yield() {
void* context;
for (;;) {
if (!resumingContexts.empty()) {
context = resumingContexts.front();
resumingContexts.pop();
break;
}
OVERLAPPED_ENTRY entry;
ULONG actual = 0;
if (GetQueuedCompletionStatusEx(completionPort, &entry, 1, &actual, INFINITE, TRUE) == TRUE) {
context = reinterpret_cast<OverlappedExt*>(entry.lpOverlapped)->context;
break;
}
DWORD lastError = GetLastError();
if (lastError != WAIT_IO_COMPLETION) {
std::cerr << "GetQueuedCompletionStatusEx failed, result=" << lastError << '.' << std::endl;
throw std::runtime_error("Dispatcher::yield");
}
}
if (context != GetCurrentFiber()) {
SwitchToFiber(context);
}
}
void Dispatcher::contextProcedure() {
for (;;) {
assert(!spawningProcedures.empty());
std::function<void()> procedure = std::move(spawningProcedures.front());
spawningProcedures.pop();
procedure();
reusableContexts.push(GetCurrentFiber());
yield();
}
}
void __stdcall Dispatcher::contextProcedureStatic(void* context) {
static_cast<Dispatcher*>(context)->contextProcedure();
}

View file

@ -21,26 +21,40 @@
#include <queue>
#include <stack>
class System {
namespace System {
class Dispatcher {
public:
System();
System(const System&) = delete;
~System();
System& operator=(const System&) = delete;
void* getCurrentContext() const;
void* getIoService();
void pushContext(void* context);
Dispatcher();
Dispatcher(const Dispatcher&) = delete;
~Dispatcher();
Dispatcher& operator=(const Dispatcher&) = delete;
void spawn(std::function<void()>&& procedure);
void yield();
void wake();
void contextProcedure();
void clear();
private:
void* ioService;
void* work;
std::stack<void*> contexts;
std::queue<std::function<void()>> procedures;
friend class Event;
friend class DispatcherAccessor;
friend class TcpConnection;
friend class TcpConnector;
friend class TcpListener;
friend class Timer;
void* completionPort;
std::size_t contextCount;
std::queue<void*> resumingContexts;
void* currentContext;
std::stack<void*> reusableContexts;
std::queue<std::function<void()>> spawningProcedures;
std::stack<void*> timers;
void* getCompletionPort() const;
void* getTimer();
void pushTimer(void* timer);
void pushContext(void* context);
void contextProcedure();
static void __stdcall contextProcedureStatic(void* context);
};
}

View file

@ -0,0 +1,107 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "Event.h"
#include <cassert>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "Dispatcher.h"
using namespace System;
namespace {
struct Waiter {
Waiter* next;
void* context;
};
}
Event::Event() : dispatcher(nullptr) {
}
Event::Event(Dispatcher& dispatcher) : dispatcher(&dispatcher), first(nullptr), state(false) {
}
Event::Event(Event&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
first = other.first;
if (other.first != nullptr) {
last = other.last;
}
state = other.state;
other.dispatcher = nullptr;
}
}
Event::~Event() {
assert(first == nullptr);
}
Event& Event::operator=(Event&& other) {
assert(first == nullptr);
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
first = other.first;
if (other.first != nullptr) {
last = other.last;
}
state = other.state;
other.dispatcher = nullptr;
}
return *this;
}
bool Event::get() const {
assert(dispatcher != nullptr);
return state;
}
void Event::clear() {
assert(dispatcher != nullptr);
state = false;
}
void Event::set() {
assert(dispatcher != nullptr);
state = true;
for (Waiter* waiter = static_cast<Waiter*>(first); waiter != nullptr; waiter = waiter->next) {
dispatcher->pushContext(waiter->context);
}
first = nullptr;
}
void Event::wait() {
assert(dispatcher != nullptr);
if (!state) {
Waiter waiter = {nullptr, GetCurrentFiber()};
if (first != nullptr) {
static_cast<Waiter*>(last)->next = &waiter;
} else {
first = &waiter;
}
last = &waiter;
dispatcher->yield();
assert(dispatcher != nullptr);
}
}

View file

@ -0,0 +1,45 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
namespace System {
class Dispatcher;
class Event {
public:
Event();
explicit Event(Dispatcher& dispatcher);
Event(const Event&) = delete;
Event(Event&& other);
~Event();
Event& operator=(const Event&) = delete;
Event& operator=(Event&& other);
bool get() const;
void clear();
void set();
void wait();
private:
Dispatcher* dispatcher;
void* first;
void* last;
bool state;
};
}

View file

@ -0,0 +1,18 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "InterruptedException.h"

View file

@ -0,0 +1,27 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <exception>
namespace System {
class InterruptedException : public std::exception {
};
}

View file

@ -0,0 +1,240 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "TcpConnection.h"
#include <cassert>
#include <iostream>
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include "InterruptedException.h"
#include "Dispatcher.h"
using namespace System;
namespace {
struct OverlappedExt : public OVERLAPPED {
void* context;
bool interrupted;
};
struct Context {
Dispatcher* dispatcher;
OverlappedExt* read;
OverlappedExt* write;
};
}
TcpConnection::TcpConnection() : dispatcher(nullptr) {
}
TcpConnection::TcpConnection(Dispatcher& dispatcher, std::size_t connection) : dispatcher(&dispatcher), connection(connection), stopped(false), context(nullptr) {
}
TcpConnection::TcpConnection(TcpConnection&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
connection = other.connection;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
TcpConnection::~TcpConnection() {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (closesocket(connection) != 0) {
std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl;
}
}
}
TcpConnection& TcpConnection::operator=(TcpConnection&& other) {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (closesocket(connection) != 0) {
std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl;
throw std::runtime_error("TcpConnection::operator=");
}
}
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
connection = other.connection;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void TcpConnection::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
void TcpConnection::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
Context* context2 = static_cast<Context*>(context);
if (context2->read != nullptr && !context2->read->interrupted) {
if (CancelIoEx(reinterpret_cast<HANDLE>(connection), context2->read) != TRUE) {
std::cerr << "CancelIoEx failed, result=" << GetLastError() << '.' << std::endl;
throw std::runtime_error("TcpConnection::stop");
}
context2->read->interrupted = true;
}
if (context2->write != nullptr && !context2->write->interrupted) {
if (CancelIoEx(reinterpret_cast<HANDLE>(connection), context2->write) != TRUE) {
std::cerr << "CancelIoEx failed, result=" << GetLastError() << '.' << std::endl;
throw std::runtime_error("TcpConnection::stop");
}
context2->write->interrupted = true;
}
}
stopped = true;
}
size_t TcpConnection::read(uint8_t* data, size_t size) {
assert(dispatcher != nullptr);
assert(context == nullptr || static_cast<Context*>(context)->read == nullptr);
if (stopped) {
throw InterruptedException();
}
WSABUF buf{static_cast<ULONG>(size), reinterpret_cast<char*>(data)};
DWORD flags = 0;
OverlappedExt overlapped;
overlapped.hEvent = NULL;
if (WSARecv(connection, &buf, 1, NULL, &flags, &overlapped, NULL) != 0) {
int lastError = WSAGetLastError();
if (lastError != WSA_IO_PENDING) {
std::cerr << "WSARecv failed, result=" << lastError << '.' << std::endl;
throw std::runtime_error("TcpConnection::read");
}
}
assert(flags == 0);
Context context2;
if (context == nullptr) {
context2.dispatcher = dispatcher;
context2.write = nullptr;
context = &context2;
}
overlapped.context = GetCurrentFiber();
overlapped.interrupted = false;
static_cast<Context*>(context)->read = &overlapped;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(overlapped.context == GetCurrentFiber());
assert(static_cast<Context*>(context)->read == &overlapped);
if (static_cast<Context*>(context)->write != nullptr) {
static_cast<Context*>(context)->read = nullptr;
} else {
context = nullptr;
}
DWORD transferred;
if (WSAGetOverlappedResult(connection, &overlapped, &transferred, FALSE, &flags) != TRUE) {
int lastError = WSAGetLastError();
if (lastError == ERROR_OPERATION_ABORTED) {
assert(overlapped.interrupted);
throw InterruptedException();
}
std::cerr << "WSARecv failed, result=" << lastError << '.' << std::endl;
throw std::runtime_error("TcpConnection::read");
}
assert(transferred <= size);
assert(flags == 0);
return transferred;
}
void TcpConnection::write(const uint8_t* data, size_t size) {
assert(dispatcher != nullptr);
assert(context == nullptr || static_cast<Context*>(context)->write == nullptr);
if (stopped) {
throw InterruptedException();
}
if (size == 0) {
if (shutdown(connection, SD_SEND) != 0) {
std::cerr << "shutdown failed, result=" << WSAGetLastError() << '.' << std::endl;
throw std::runtime_error("TcpConnection::write");
}
return;
}
WSABUF buf{static_cast<ULONG>(size), reinterpret_cast<char*>(const_cast<uint8_t*>(data))};
OverlappedExt overlapped;
overlapped.hEvent = NULL;
if (WSASend(connection, &buf, 1, NULL, 0, &overlapped, NULL) != 0) {
int lastError = WSAGetLastError();
if (lastError != WSA_IO_PENDING) {
std::cerr << "WSASend failed, result=" << lastError << '.' << std::endl;
throw std::runtime_error("TcpConnection::write");
}
}
Context context2;
if (context == nullptr) {
context2.dispatcher = dispatcher;
context2.read = nullptr;
context = &context2;
}
overlapped.context = GetCurrentFiber();
overlapped.interrupted = false;
static_cast<Context*>(context)->write = &overlapped;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(overlapped.context == GetCurrentFiber());
assert(static_cast<Context*>(context)->write == &overlapped);
if (static_cast<Context*>(context)->read != nullptr) {
static_cast<Context*>(context)->write = nullptr;
} else {
context = nullptr;
}
DWORD transferred;
DWORD flags;
if (WSAGetOverlappedResult(connection, &overlapped, &transferred, FALSE, &flags) != TRUE) {
int lastError = WSAGetLastError();
if (lastError == ERROR_OPERATION_ABORTED) {
assert(overlapped.interrupted);
throw InterruptedException();
}
std::cerr << "WSSend failed, result=" << lastError << '.' << std::endl;
throw std::runtime_error("TcpConnection::write");
}
assert(transferred == size);
assert(flags == 0);
}

View file

@ -0,0 +1,52 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <cstddef>
#include <cstdint>
namespace System {
class Dispatcher;
class TcpConnection {
public:
TcpConnection();
TcpConnection(const TcpConnection&) = delete;
TcpConnection(TcpConnection&& other);
~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);
void write(const uint8_t* data, std::size_t size);
private:
friend class TcpConnector;
friend class TcpListener;
explicit TcpConnection(Dispatcher& dispatcher, std::size_t connection);
Dispatcher* dispatcher;
std::size_t connection;
bool stopped;
void* context;
};
}

View file

@ -0,0 +1,204 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "TcpConnector.h"
#include <cassert>
#include <iostream>
#include <random>
#include <sstream>
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>
#include <mswsock.h>
#include "InterruptedException.h"
#include "Dispatcher.h"
#include "TcpConnection.h"
using namespace System;
namespace {
struct Context : public OVERLAPPED {
void* context;
std::size_t connection;
bool interrupted;
};
LPFN_CONNECTEX connectEx = nullptr;
}
TcpConnector::TcpConnector() : dispatcher(nullptr) {
}
TcpConnector::TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher), address(address), port(port), stopped(false), context(nullptr) {
}
TcpConnector::TcpConnector(TcpConnector&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
address = other.address;
port = other.port;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
TcpConnector::~TcpConnector() {
}
TcpConnector& TcpConnector::operator=(TcpConnector&& other) {
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
address = other.address;
port = other.port;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void TcpConnector::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
void TcpConnector::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
Context* context2 = static_cast<Context*>(context);
if (!context2->interrupted) {
if (CancelIoEx(reinterpret_cast<HANDLE>(context2->connection), context2) != TRUE) {
std::cerr << "CancelIoEx failed, result=" << GetLastError() << '.' << std::endl;
throw std::runtime_error("TcpConnector::stop");
}
context2->interrupted = true;
}
}
stopped = true;
}
TcpConnection TcpConnector::connect() {
assert(dispatcher != nullptr);
assert(context == nullptr);
if (stopped) {
throw InterruptedException();
}
std::ostringstream portStream;
portStream << port;
addrinfo hints = {0, AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL};
addrinfo* addressInfos;
int result = getaddrinfo(address.c_str(), portStream.str().c_str(), &hints, &addressInfos);
if (result != 0) {
std::cerr << "getaddrinfo failed, result=" << result << '.' << std::endl;
} else {
std::size_t count = 0;
for (addrinfo* addressInfo = addressInfos; addressInfo != nullptr; addressInfo = addressInfo->ai_next) {
++count;
}
std::random_device randomDevice;
std::mt19937 generator(randomDevice());
std::uniform_int_distribution<std::size_t> distribution(0, count - 1);
std::size_t index = distribution(generator);
addrinfo* addressInfo = addressInfos;
for (std::size_t i = 0; i < index; ++i) {
addressInfo = addressInfo->ai_next;
}
sockaddr_in addressData = *reinterpret_cast<sockaddr_in*>(addressInfo->ai_addr);
freeaddrinfo(addressInfo);
SOCKET connection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (connection == INVALID_SOCKET) {
std::cerr << "socket failed, result=" << WSAGetLastError() << '.' << std::endl;
} else {
sockaddr_in bindAddress;
bindAddress.sin_family = AF_INET;
bindAddress.sin_port = 0;
bindAddress.sin_addr.s_addr = INADDR_ANY;
if (bind(connection, reinterpret_cast<sockaddr*>(&bindAddress), sizeof bindAddress) != 0) {
std::cerr << "bind failed, result=" << WSAGetLastError() << '.' << std::endl;
} else {
GUID guidConnectEx = WSAID_CONNECTEX;
DWORD read = sizeof connectEx;
if (connectEx == nullptr && WSAIoctl(connection, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidConnectEx, sizeof guidConnectEx, &connectEx, sizeof connectEx, &read, NULL, NULL) != 0) {
std::cerr << "WSAIoctl failed, result=" << WSAGetLastError() << '.' << std::endl;
} else {
assert(read == sizeof connectEx);
if (CreateIoCompletionPort(reinterpret_cast<HANDLE>(connection), dispatcher->getCompletionPort(), 0, 0) != dispatcher->getCompletionPort()) {
std::cerr << "CreateIoCompletionPort failed, result=" << GetLastError() << '.' << std::endl;
} else {
addressData.sin_port = htons(port);
Context context2;
context2.hEvent = NULL;
if (connectEx(connection, reinterpret_cast<sockaddr*>(&addressData), sizeof addressData, NULL, 0, NULL, &context2) == TRUE) {
std::cerr << "ConnectEx returned immediately, which is not supported." << std::endl;
} else {
int lastError = WSAGetLastError();
if (lastError != WSA_IO_PENDING) {
std::cerr << "ConnectEx failed, result=" << lastError << '.' << std::endl;
} else {
context2.context = GetCurrentFiber();
context2.connection = connection;
context2.interrupted = false;
context = &context2;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == GetCurrentFiber());
assert(context2.connection == connection);
assert(context == &context2);
context = nullptr;
DWORD transferred;
DWORD flags;
if (WSAGetOverlappedResult(connection, &context2, &transferred, FALSE, &flags) != TRUE) {
lastError = WSAGetLastError();
if (lastError == ERROR_OPERATION_ABORTED) {
assert(context2.interrupted);
if (closesocket(connection) != 0) {
std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl;
}
throw InterruptedException();
}
std::cerr << "ConnectEx failed, result=" << lastError << '.' << std::endl;
} else {
assert(transferred == 0);
assert(flags == 0);
return TcpConnection(*dispatcher, connection);
}
}
}
}
}
}
if (closesocket(connection) != 0) {
std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl;
}
}
}
throw std::runtime_error("TcpConnector::connect");
}

View file

@ -0,0 +1,49 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <cstdint>
#include <string>
namespace System {
class Dispatcher;
class TcpConnection;
class TcpConnector {
public:
TcpConnector();
TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port);
TcpConnector(const TcpConnector&) = delete;
TcpConnector(TcpConnector&& other);
~TcpConnector();
TcpConnector& operator=(const TcpConnector&) = delete;
TcpConnector& operator=(TcpConnector&& other);
void start();
void stop();
TcpConnection connect();
private:
Dispatcher* dispatcher;
std::string address;
uint16_t port;
bool stopped;
void* context;
};
}

View file

@ -0,0 +1,226 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "TcpListener.h"
#include <cassert>
#include <iostream>
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <mswsock.h>
#include "InterruptedException.h"
#include "Dispatcher.h"
#include "TcpConnection.h"
using namespace System;
namespace {
struct Context : public OVERLAPPED {
void* context;
bool interrupted;
};
LPFN_ACCEPTEX acceptEx = nullptr;
LPFN_GETACCEPTEXSOCKADDRS getAcceptExSockaddrs = nullptr;
}
TcpListener::TcpListener() : dispatcher(nullptr) {
}
TcpListener::TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher) {
listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listener == INVALID_SOCKET) {
std::cerr << "socket failed, result=" << WSAGetLastError() << '.' << std::endl;
} else {
sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = INADDR_ANY;
if (bind(listener, reinterpret_cast<sockaddr*>(&address), sizeof address) != 0) {
std::cerr << "bind failed, result=" << WSAGetLastError() << '.' << std::endl;
} else if (listen(listener, SOMAXCONN) != 0) {
std::cerr << "listen failed, result=" << WSAGetLastError() << '.' << std::endl;
} else {
GUID guidAcceptEx = WSAID_ACCEPTEX;
DWORD read = sizeof acceptEx;
if (acceptEx == nullptr && WSAIoctl(listener, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidAcceptEx, sizeof guidAcceptEx, &acceptEx, sizeof acceptEx, &read, NULL, NULL) != 0) {
std::cerr << "WSAIoctl failed, result=" << WSAGetLastError() << '.' << std::endl;
} else {
assert(read == sizeof acceptEx);
GUID guidGetAcceptExSockaddrs = WSAID_GETACCEPTEXSOCKADDRS;
read = sizeof getAcceptExSockaddrs;
if (getAcceptExSockaddrs == nullptr && WSAIoctl(listener, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidGetAcceptExSockaddrs, sizeof guidGetAcceptExSockaddrs, &getAcceptExSockaddrs, sizeof getAcceptExSockaddrs, &read, NULL, NULL) != 0) {
std::cerr << "WSAIoctl failed, result=" << WSAGetLastError() << '.' << std::endl;
} else {
assert(read == sizeof getAcceptExSockaddrs);
if (CreateIoCompletionPort(reinterpret_cast<HANDLE>(listener), dispatcher.getCompletionPort(), 0, 0) != dispatcher.getCompletionPort()) {
std::cerr << "CreateIoCompletionPort failed, result=" << GetLastError() << '.' << std::endl;
} else {
stopped = false;
context = nullptr;
return;
}
}
}
}
if (closesocket(listener) != 0) {
std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl;
}
}
throw std::runtime_error("TcpListener::TcpListener");
}
TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
listener = other.listener;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
TcpListener::~TcpListener() {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (closesocket(listener) != 0) {
std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl;
}
}
}
TcpListener& TcpListener::operator=(TcpListener&& other) {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (closesocket(listener) != 0) {
std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl;
throw std::runtime_error("TcpListener::operator=");
}
}
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
listener = other.listener;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void TcpListener::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
void TcpListener::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
Context* context2 = static_cast<Context*>(context);
if (!context2->interrupted) {
if (CancelIoEx(reinterpret_cast<HANDLE>(listener), context2) != TRUE) {
std::cerr << "CancelIoEx failed, result=" << GetLastError() << '.' << std::endl;
throw std::runtime_error("TcpListener::stop");
}
context2->interrupted = true;
}
}
stopped = true;
}
TcpConnection TcpListener::accept() {
assert(dispatcher != nullptr);
assert(context == nullptr);
if (stopped) {
throw InterruptedException();
}
SOCKET connection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (connection == INVALID_SOCKET) {
std::cerr << "socket failed, result=" << WSAGetLastError() << '.' << std::endl;
} else {
uint8_t addresses[sizeof sockaddr_in * 2 + 32];
DWORD received;
Context context2;
context2.hEvent = NULL;
if (acceptEx(listener, connection, addresses, 0, sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16, &received, &context2) == TRUE) {
std::cerr << "AcceptEx returned immediately, which is not supported." << std::endl;
} else {
int lastError = WSAGetLastError();
if (lastError != WSA_IO_PENDING) {
std::cerr << "AcceptEx failed, result=" << lastError << '.' << std::endl;
} else {
context2.context = GetCurrentFiber();
context2.interrupted = false;
context = &context2;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == GetCurrentFiber());
assert(context == &context2);
context = nullptr;
DWORD transferred;
DWORD flags;
if (WSAGetOverlappedResult(listener, &context2, &transferred, FALSE, &flags) != TRUE) {
lastError = WSAGetLastError();
if (lastError == ERROR_OPERATION_ABORTED) {
assert(context2.interrupted);
if (closesocket(connection) != 0) {
std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl;
}
throw InterruptedException();
}
std::cerr << "AcceptEx failed, result=" << lastError << '.' << std::endl;
} else {
assert(transferred == 0);
assert(flags == 0);
if (setsockopt(connection, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, reinterpret_cast<char*>(&listener), sizeof listener) != 0) {
std::cerr << "setsockopt failed, result=" << WSAGetLastError() << '.' << std::endl;
} else {
if (CreateIoCompletionPort(reinterpret_cast<HANDLE>(connection), dispatcher->getCompletionPort(), 0, 0) != dispatcher->getCompletionPort()) {
std::cerr << "CreateIoCompletionPort failed, result=" << GetLastError() << '.' << std::endl;
} else {
//sockaddr_in* local;
//int localSize;
//sockaddr_in* remote;
//int remoteSize;
//static_cast<LPFN_GETACCEPTEXSOCKADDRS>(getAcceptExSockaddrs)(addresses, 0, sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16, reinterpret_cast<sockaddr**>(&local), &localSize, reinterpret_cast<sockaddr**>(&remote), &remoteSize);
//assert(localSize == sizeof sockaddr_in);
//assert(remoteSize == sizeof sockaddr_in);
//std::cout << "Client connected from " << static_cast<unsigned int>(remote->sin_addr.S_un.S_un_b.s_b1) << '.' << static_cast<unsigned int>(remote->sin_addr.S_un.S_un_b.s_b2) << '.' << static_cast<unsigned int>(remote->sin_addr.S_un.S_un_b.s_b3) << '.' << static_cast<unsigned int>(remote->sin_addr.S_un.S_un_b.s_b4) << ':' << remote->sin_port << std::endl;
return TcpConnection(*dispatcher, connection);
}
}
}
}
}
if (closesocket(connection) != 0) {
std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl;
}
}
throw std::runtime_error("TcpListener::accept");
}

View file

@ -0,0 +1,48 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <cstdint>
#include <string>
namespace System {
class Dispatcher;
class TcpConnection;
class TcpListener {
public:
TcpListener();
TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port);
TcpListener(const TcpListener&) = delete;
TcpListener(TcpListener&& other);
~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;
void* context;
};
}

View file

@ -0,0 +1,146 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "Timer.h"
#include <cassert>
#include <iostream>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "InterruptedException.h"
#include "Dispatcher.h"
using namespace System;
namespace System {
class DispatcherAccessor {
public:
DispatcherAccessor(Dispatcher* dispatcher, void* context) {
dispatcher->pushContext(context);
}
};
}
namespace {
struct Context {
Dispatcher* dispatcher;
void* context;
bool interrupted;
};
void __stdcall callbackProcedure(void* lpArgToCompletionRoutine, DWORD dwTimerLowValue, DWORD dwTimerHighValue) {
Context* context = static_cast<Context*>(lpArgToCompletionRoutine);
assert(context->context != nullptr);
DispatcherAccessor(context->dispatcher, context->context);
context->context = nullptr;
}
}
Timer::Timer() : dispatcher(nullptr) {
}
Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr) {
timer = dispatcher.getTimer();
}
Timer::Timer(Timer&& other) : dispatcher(other.dispatcher) {
if (other.dispatcher != nullptr) {
timer = other.timer;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
}
Timer::~Timer() {
if (dispatcher != nullptr) {
assert(context == nullptr);
dispatcher->pushTimer(timer);
}
}
Timer& Timer::operator=(Timer&& other) {
if (dispatcher != nullptr) {
assert(context == nullptr);
dispatcher->pushTimer(timer);
}
dispatcher = other.dispatcher;
if (other.dispatcher != nullptr) {
timer = other.timer;
stopped = other.stopped;
context = other.context;
other.dispatcher = nullptr;
}
return *this;
}
void Timer::start() {
assert(dispatcher != nullptr);
assert(stopped);
stopped = false;
}
void Timer::stop() {
assert(dispatcher != nullptr);
assert(!stopped);
if (context != nullptr) {
Context* context2 = static_cast<Context*>(context);
if (context2->context != nullptr) {
if (CancelWaitableTimer(timer) != TRUE) {
std::cerr << "CancelWaitableTimer failed, result=" << GetLastError() << '.' << std::endl;
throw std::runtime_error("Timer::stop");
}
dispatcher->pushContext(context2->context);
context2->context = nullptr;
context2->interrupted = true;
}
}
stopped = true;
}
void Timer::sleep(std::chrono::nanoseconds duration) {
assert(dispatcher != nullptr);
assert(context == nullptr);
if (stopped) {
throw InterruptedException();
}
LARGE_INTEGER duration2;
duration2.QuadPart = static_cast<LONGLONG>(duration.count() / -100);
Context context2 = {dispatcher, GetCurrentFiber(), false};
if (SetWaitableTimer(timer, &duration2, 0, callbackProcedure, &context2, FALSE) != TRUE) {
std::cerr << "SetWaitableTimer failed, result=" << GetLastError() << '.' << std::endl;
throw std::runtime_error("Timer::sleep");
}
context = &context2;
dispatcher->yield();
assert(dispatcher != nullptr);
assert(context2.context == nullptr);
assert(context == &context2);
context = nullptr;
if (context2.interrupted) {
throw InterruptedException();
}
}

View file

@ -19,20 +19,28 @@
#include <chrono>
class System;
namespace System {
class Dispatcher;
class Timer {
public:
Timer();
explicit Timer(System& system);
explicit Timer(Dispatcher& dispatcher);
Timer(const Timer&) = delete;
Timer(Timer&& other);
~Timer();
Timer& operator=(const Timer&) = delete;
Timer& operator=(Timer&& other);
void sleep(std::chrono::milliseconds time);
void start();
void stop();
void sleep(std::chrono::nanoseconds duration);
private:
System* system;
Dispatcher* dispatcher;
void* timer;
bool stopped;
void* context;
};
}

View file

@ -1,133 +0,0 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "System.h"
#include <iostream>
#include <boost/asio/spawn.hpp>
#include <boost/context/fcontext.hpp>
namespace {
void contextProcedureStatic(intptr_t context) {
reinterpret_cast<System*>(context)->contextProcedure();
}
}
System::System() {
ioService = new boost::asio::io_service;
work = new boost::asio::io_service::work(*static_cast<boost::asio::io_service*>(ioService));
currentContext = new boost::context::fcontext_t;
}
System::~System() {
assert(procedures.empty());
assert(resumingContexts.empty());
while (!contexts.empty()) {
delete static_cast<boost::context::fcontext_t*>(contexts.top());
contexts.pop();
}
delete static_cast<boost::asio::io_service::work*>(work);
if (!static_cast<boost::asio::io_service*>(ioService)->stopped()) {
static_cast<boost::asio::io_service*>(ioService)->stop();
}
delete static_cast<boost::asio::io_service*>(ioService);
}
void* System::getCurrentContext() const {
return currentContext;
}
void* System::getIoService() {
return ioService;
}
void System::pushContext(void* context) {
resumingContexts.push(context);
}
void System::spawn(std::function<void()>&& procedure) {
procedures.emplace(std::move(procedure));
}
void System::wake() {
static_cast<boost::asio::io_service*>(ioService)->post([] {});
}
void System::yield() {
if (procedures.empty()) {
void* context;
for (;;) {
if (resumingContexts.empty()) {
boost::system::error_code errorCode;
static_cast<boost::asio::io_service*>(ioService)->run_one(errorCode);
if (errorCode) {
std::cerr << "boost::asio::io_service::run_onw failed, result=" << errorCode << '.' << std::endl;
throw std::runtime_error("System::yield");
}
} else {
context = resumingContexts.front();
resumingContexts.pop();
break;
}
}
if (context != currentContext) {
boost::context::fcontext_t* oldContext = static_cast<boost::context::fcontext_t*>(currentContext);
currentContext = context;
#if (BOOST_VERSION >= 105600)
boost::context::jump_fcontext(oldContext, *static_cast<boost::context::fcontext_t*>(context), reinterpret_cast<intptr_t>(this), false);
#else
boost::context::jump_fcontext(oldContext, static_cast<boost::context::fcontext_t*>(context), reinterpret_cast<intptr_t>(this), false);
#endif
}
} else {
void* context;
if (contexts.empty()) {
#if (BOOST_VERSION >= 105600)
context = new boost::context::fcontext_t(boost::context::make_fcontext(new uint8_t[65536] + 65536, 65536, contextProcedureStatic));
#else
context = new boost::context::fcontext_t(*boost::context::make_fcontext(new uint8_t[65536] + 65536, 65536, contextProcedureStatic));
#endif
} else {
context = contexts.top();
contexts.pop();
}
boost::context::fcontext_t* oldContext = static_cast<boost::context::fcontext_t*>(currentContext);
currentContext = context;
#if (BOOST_VERSION >= 105600)
boost::context::jump_fcontext(oldContext, *static_cast<boost::context::fcontext_t*>(context), reinterpret_cast<intptr_t>(this), false);
#else
boost::context::jump_fcontext(oldContext, static_cast<boost::context::fcontext_t*>(context), reinterpret_cast<intptr_t>(this), false);
#endif
}
}
void System::contextProcedure() {
void* context = currentContext;
for (;;) {
assert(!procedures.empty());
std::function<void()> procedure = std::move(procedures.front());
procedures.pop();
procedure();
contexts.push(context);
yield();
}
}

View file

@ -1,110 +0,0 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "TcpConnection.h"
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/write.hpp>
#include "System.h"
TcpConnection::TcpConnection() : system(nullptr) {
}
TcpConnection::TcpConnection(System& system, void* socket) : system(&system), socket(socket), stopped(false) {
}
TcpConnection::TcpConnection(TcpConnection&& other) : system(other.system) {
if (other.system != nullptr) {
socket = other.socket;
stopped = other.stopped;
other.system = nullptr;
}
}
TcpConnection::~TcpConnection() {
if (system != nullptr) {
delete static_cast<boost::asio::ip::tcp::socket*>(socket);
}
}
TcpConnection& TcpConnection::operator=(TcpConnection&& other) {
if (system != nullptr) {
delete static_cast<boost::asio::ip::tcp::socket*>(socket);
}
system = other.system;
if (other.system != nullptr) {
socket = other.socket;
stopped = other.stopped;
other.system = nullptr;
}
return *this;
}
void TcpConnection::start() {
stopped = false;
}
void TcpConnection::stop() {
stopped = true;
}
size_t TcpConnection::read(uint8_t* data, size_t size) {
assert(system != nullptr);
if (stopped) {
throw std::runtime_error("Stopped");
}
void* context = system->getCurrentContext();
boost::system::error_code errorCode;
std::size_t transferred;
static_cast<boost::asio::ip::tcp::socket*>(socket)->async_read_some(boost::asio::buffer(data, size), [&](const boost::system::error_code& callbackErrorCode, std::size_t callbackTransferred) {
errorCode = callbackErrorCode;
transferred = callbackTransferred;
system->pushContext(context);
});
system->yield();
if (errorCode) {
throw boost::system::system_error(errorCode);
}
return transferred;
}
void TcpConnection::write(const uint8_t* data, size_t size) {
assert(system != nullptr);
if (stopped) {
throw std::runtime_error("Stopped");
}
void* context = system->getCurrentContext();
boost::system::error_code errorCode;
std::size_t transferred;
boost::asio::async_write(*static_cast<boost::asio::ip::tcp::socket*>(socket), boost::asio::buffer(data, size), [&](const boost::system::error_code& callbackErrorCode, std::size_t callbackTransferred) {
errorCode = callbackErrorCode;
transferred = callbackTransferred;
system->pushContext(context);
});
system->yield();
if (errorCode) {
throw boost::system::system_error(errorCode);
}
assert(transferred == size);
}

View file

@ -1,67 +0,0 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "TcpConnector.h"
#include <boost/asio/ip/tcp.hpp>
#include "System.h"
#include "TcpConnection.h"
TcpConnector::TcpConnector() : system(nullptr) {
}
TcpConnector::TcpConnector(System& system, const std::string& address, uint16_t port) : system(&system), m_address(address), m_port(port) { }
TcpConnector::TcpConnector(TcpConnector&& other) : system(other.system) {
if (other.system != nullptr) {
m_address = other.m_address;
m_port = other.m_port;
other.system = nullptr;
}
}
TcpConnector::~TcpConnector() {
}
TcpConnector& TcpConnector::operator=(TcpConnector&& other) {
system = other.system;
if (other.system != nullptr) {
m_address = other.m_address;
m_port = other.m_port;
other.system = nullptr;
}
return *this;
}
TcpConnection TcpConnector::connect() {
assert(system != nullptr);
void* context = system->getCurrentContext();
boost::asio::ip::tcp::socket* socket = new boost::asio::ip::tcp::socket(*static_cast<boost::asio::io_service*>(system->getIoService()));
boost::system::error_code errorCode;
socket->async_connect(boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(m_address), m_port), [&](const boost::system::error_code& callbackErrorCode) {
errorCode = callbackErrorCode;
system->pushContext(context);
});
system->yield();
if (errorCode) {
delete socket;
throw boost::system::system_error(errorCode);
}
return TcpConnection(*system, socket);
}

View file

@ -1,88 +0,0 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "TcpListener.h"
#include <boost/asio/ip/tcp.hpp>
#include "System.h"
#include "TcpConnection.h"
TcpListener::TcpListener() : system(nullptr) {
}
TcpListener::TcpListener(System& system, const std::string& address, uint16_t port) : system(&system), stopped(false) {
listener = new boost::asio::ip::tcp::acceptor(*static_cast<boost::asio::io_service*>(system.getIoService()), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port), true);
}
TcpListener::TcpListener(TcpListener&& other) : system(other.system) {
if (other.system != nullptr) {
listener = other.listener;
stopped = other.stopped;
other.system = nullptr;
}
}
TcpListener::~TcpListener() {
if (system != nullptr) {
delete static_cast<boost::asio::ip::tcp::acceptor*>(listener);
}
}
TcpListener& TcpListener::operator=(TcpListener&& other) {
if (system != nullptr) {
delete static_cast<boost::asio::ip::tcp::acceptor*>(listener);
}
system = other.system;
if (other.system != nullptr) {
listener = other.listener;
stopped = other.stopped;
other.system = nullptr;
}
return *this;
}
void TcpListener::start() {
stopped = false;
}
void TcpListener::stop() {
stopped = true;
}
TcpConnection TcpListener::accept() {
assert(system != nullptr);
if (stopped) {
throw std::runtime_error("Stopped");
}
void* context = system->getCurrentContext();
boost::asio::ip::tcp::socket* socket = new boost::asio::ip::tcp::socket(*static_cast<boost::asio::io_service*>(system->getIoService()));
boost::system::error_code errorCode;
static_cast<boost::asio::ip::tcp::acceptor*>(listener)->async_accept(*socket, [&](const boost::system::error_code& callbackErrorCode) {
errorCode = callbackErrorCode;
system->pushContext(context);
});
system->yield();
if (errorCode) {
delete socket;
throw boost::system::system_error(errorCode);
}
return TcpConnection(*system, socket);
}

2
src/System/TcpStream.cpp Normal file → Executable file
View file

@ -19,6 +19,8 @@
#include <cstring>
using namespace System;
TcpStreambuf::TcpStreambuf(TcpConnection& connection) : connection(connection) {
setg(&readBuf.front(), &readBuf.front(), &readBuf.front());
setp(reinterpret_cast<char*>(&writeBuf.front()), reinterpret_cast<char *>(&writeBuf.front() + writeBuf.max_size()));

6
src/System/TcpStream.h Normal file → Executable file
View file

@ -20,7 +20,9 @@
#include <streambuf>
#include <array>
#include "TcpConnection.h"
#include <System/TcpConnection.h>
namespace System {
class TcpStreambuf : public std::streambuf {
public:
@ -41,3 +43,5 @@ private:
std::array<char, 4096> readBuf;
std::array<uint8_t, /*1024*/ 16> writeBuf;
};
}

View file

@ -1,70 +0,0 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "Timer.h"
#include <boost/asio/steady_timer.hpp>
#include "System.h"
Timer::Timer() : system(nullptr) {
}
Timer::Timer(System& system) : system(&system) {
timer = new boost::asio::steady_timer(*static_cast<boost::asio::io_service*>(system.getIoService()));
}
Timer::Timer(Timer&& other) : system(other.system) {
if (other.system != nullptr) {
timer = other.timer;
other.system = nullptr;
}
}
Timer::~Timer() {
if (system != nullptr) {
delete static_cast<boost::asio::steady_timer*>(timer);
}
}
Timer& Timer::operator=(Timer&& other) {
if (system != nullptr) {
delete static_cast<boost::asio::steady_timer*>(timer);
}
system = other.system;
if (other.system != nullptr) {
timer = other.timer;
other.system = nullptr;
}
return *this;
}
void Timer::sleep(std::chrono::milliseconds time) {
assert(system != nullptr);
static_cast<boost::asio::steady_timer*>(timer)->expires_from_now(time);
void* context = system->getCurrentContext();
boost::system::error_code errorCode;
static_cast<boost::asio::steady_timer*>(timer)->async_wait([&](const boost::system::error_code& callbackErrorCode) {
errorCode = callbackErrorCode;
system->pushContext(context);
});
system->yield();
if (errorCode) {
throw boost::system::system_error(errorCode);
}
}

View file

@ -106,6 +106,19 @@ public:
}
}
template<typename F, typename Arg0, typename Arg1, typename Arg2, typename Arg3>
void notify(F notification, const Arg0& arg0, const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) {
std::vector<T*> observersCopy;
{
std::unique_lock<std::mutex> lock(m_observersMutex);
observersCopy = m_observers;
}
for (T* observer : observersCopy) {
(observer->*notification)(arg0, arg1, arg2, arg3);
}
}
#else
template<typename F, typename... Args>

View file

@ -0,0 +1,70 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <unordered_map>
#include <random>
template <typename T, typename Gen>
class ShuffleGenerator {
public:
ShuffleGenerator(T n, const Gen& gen = Gen()) :
N(n), generator(gen), count(n) {}
T operator()() {
if (count == 0) {
throw std::runtime_error("shuffle sequence ended");
}
typedef typename std::uniform_int_distribution<T> distr_t;
typedef typename distr_t::param_type param_t;
distr_t distr;
T value = distr(generator, param_t(0, --count));
auto rvalIt = selected.find(count);
auto rval = rvalIt != selected.end() ? rvalIt->second : count;
auto lvalIt = selected.find(value);
if (lvalIt != selected.end()) {
value = lvalIt->second;
lvalIt->second = rval;
} else {
selected[value] = rval;
}
return value;
}
void reset() {
count = N;
selected.clear();
}
private:
std::unordered_map<T, T> selected;
T count;
const T N;
Gen generator;
};

View file

@ -26,6 +26,8 @@
#if defined(_MSC_VER)
#include <stdlib.h>
#define inline __inline
static inline uint32_t rol32(uint32_t x, int r) {
static_assert(sizeof(uint32_t) == sizeof(unsigned int), "this code assumes 32-bit integers");
return _rotl(x, r);

9
src/common/static_assert.h Executable file
View file

@ -0,0 +1,9 @@
#pragma once
#ifndef __cplusplus
#ifdef __clang__
#define static_assert _Static_assert
#endif
#endif

View file

@ -28,8 +28,8 @@ namespace boost
{
namespace serialization
{
template <class Archive, class h_key, class hval>
inline void save(Archive &a, const std::unordered_map<h_key, hval> &x, const boost::serialization::version_type ver)
template <class Archive, class h_key, class hval, class hfunc>
inline void save(Archive &a, const std::unordered_map<h_key, hval, hfunc> &x, const boost::serialization::version_type ver)
{
size_t s = x.size();
a << s;
@ -40,8 +40,8 @@ namespace boost
}
}
template <class Archive, class h_key, class hval>
inline void load(Archive &a, std::unordered_map<h_key, hval> &x, const boost::serialization::version_type ver)
template <class Archive, class h_key, class hval, class hfunc>
inline void load(Archive &a, std::unordered_map<h_key, hval, hfunc> &x, const boost::serialization::version_type ver)
{
x.clear();
size_t s = 0;
@ -128,6 +128,7 @@ namespace boost
x.clear();
size_t s = 0;
a >> s;
x.resize(s);
for(size_t i = 0; i != s; i++)
{
hval v;
@ -160,6 +161,7 @@ namespace boost
x.clear();
size_t s = 0;
a >> s;
x.resize(s);
for(size_t i = 0; i != s; i++)
{
h_key k;
@ -176,8 +178,8 @@ namespace boost
split_free(a, x, ver);
}
template <class Archive, class h_key, class hval>
inline void serialize(Archive &a, std::unordered_map<h_key, hval> &x, const boost::serialization::version_type ver)
template <class Archive, class h_key, class hval, class hfunc>
inline void serialize(Archive &a, std::unordered_map<h_key, hval, hfunc> &x, const boost::serialization::version_type ver)
{
split_free(a, x, ver);
}

View file

@ -32,7 +32,6 @@ static void ge_madd(ge_p1p1 *, const ge_p3 *, const ge_precomp *);
static void ge_msub(ge_p1p1 *, const ge_p3 *, const ge_precomp *);
static void ge_p2_0(ge_p2 *);
static void ge_p3_dbl(ge_p1p1 *, const ge_p3 *);
static void ge_sub(ge_p1p1 *, const ge_p3 *, const ge_cached *);
static void fe_divpowm1(fe, const fe, const fe);
/* Common functions */
@ -1578,7 +1577,7 @@ void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) {
r = p - q
*/
static void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) {
void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) {
fe t0;
fe_add(r->X, p->Y, p->X);
fe_sub(r->Y, p->Y, p->X);

View file

@ -103,6 +103,10 @@ void ge_p3_tobytes(unsigned char *, const ge_p3 *);
extern const ge_precomp ge_base[32][8];
void ge_scalarmult_base(ge_p3 *, const unsigned char *);
/* From ge_sub.c */
void ge_sub(ge_p1p1 *, const ge_p3 *, const ge_cached *);
/* From ge_tobytes.c */
void ge_tobytes(unsigned char *, const ge_p2 *);

View file

@ -33,12 +33,9 @@ namespace crypto {
using std::abort;
using std::int32_t;
using std::int64_t;
using std::lock_guard;
using std::mutex;
using std::size_t;
using std::uint32_t;
using std::uint64_t;
extern "C" {
#include "crypto-ops.h"
@ -153,6 +150,26 @@ namespace crypto {
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;
ge_p3 point1;
ge_p3 point2;
ge_cached point3;
ge_p1p1 point4;
ge_p2 point5;
if (ge_frombytes_vartime(&point1, &derived_key) != 0) {
return false;
}
derivation_to_scalar(derivation, output_index, scalar);
ge_scalarmult_base(&point2, &scalar);
ge_p3_to_cached(&point3, &point2);
ge_sub(&point4, &point1, &point3);
ge_p1p1_to_p2(&point5, &point4);
ge_tobytes(&base, &point5);
return true;
}
struct s_comm {
hash h;
ec_point key;

View file

@ -18,6 +18,7 @@
#pragma once
#include <cstddef>
#include <limits>
#include <mutex>
#include <vector>
@ -27,6 +28,8 @@
namespace crypto {
using std::size_t;
extern "C" {
#include "random.h"
}
@ -87,6 +90,8 @@ namespace crypto {
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 &);
@ -113,6 +118,35 @@ namespace crypto {
return res;
}
/* Random number engine based on crypto::rand()
*/
template <typename T>
class random_engine {
public:
typedef T result_type;
#ifdef __clang__
constexpr static T min() {
return (std::numeric_limits<T>::min)();
}
constexpr static T max() {
return (std::numeric_limits<T>::max)();
}
#else
static T(min)() {
return (std::numeric_limits<T>::min)();
}
static T(max)() {
return (std::numeric_limits<T>::max)();
}
#endif
typename std::enable_if<std::is_unsigned<T>::value, T>::type operator()() {
return rand<T>();
}
};
/* Generate a new key pair
*/
inline void generate_keys(public_key &pub, secret_key &sec) {
@ -133,8 +167,8 @@ namespace crypto {
/* To generate an ephemeral key used to send money to:
* * The sender generates a new key pair, which becomes the transaction key. The public transaction key is included in "extra" field.
* * Both the sender and the receiver generate key derivation from the transaction key, the receivers' "view" key and the output index.
* * The sender uses key derivation and the receivers' "spend" key to derive an ephemeral public key.
* * Both the sender and the receiver generate key derivation from the transaction key and the receivers' "view" key.
* * 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) {
@ -149,6 +183,13 @@ namespace crypto {
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) {
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) {
@ -194,6 +235,6 @@ namespace crypto {
}
}
CRYPTO_MAKE_COMPARABLE(public_key)
CRYPTO_MAKE_HASHABLE(public_key)
CRYPTO_MAKE_HASHABLE(key_image)
CRYPTO_MAKE_COMPARABLE(signature)

View file

@ -24,6 +24,7 @@
#include <stddef.h>
#include <stdint.h>
#include "../common/static_assert.h"
#include "common/int-util.h"
#include "warnings.h"

View file

@ -17,6 +17,8 @@
#pragma once
#if !defined(__cplusplus)
#include <stddef.h>
#endif
void generate_random_bytes(size_t n, void *result);

View file

@ -32,7 +32,7 @@ void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash) {
size_t i, j;
size_t cnt = count - 1;
char (*ints)[HASH_SIZE];
for (i = 1; i < 8 * sizeof(size_t); i <<= 1) {
for (i = 1; i < sizeof(size_t); i <<= 1) {
cnt |= cnt >> i;
}
cnt &= ~(cnt >> 1);

View file

@ -145,7 +145,14 @@ const CheckpointData CHECKPOINTS[] = {
{484500, "5cdf2101a0a62a0ab2a1ca0c15a6212b21f6dbdc42a0b7c0bcf65ca40b7a14fb"},
{506000, "3d54c1132f503d98d3f0d78bb46a4503c1a19447cb348361a2232e241cb45a3c"},
{544000, "f69dc61b6a63217f32fa64d5d0f9bd920873f57dfd79ebe1d7d6fb1345b56fe0"},
{553300, "f7a5076b887ce5f4bb95b2729c0edb6f077a463f04f1bffe7f5cb0b16bb8aa5f"}
{553300, "f7a5076b887ce5f4bb95b2729c0edb6f077a463f04f1bffe7f5cb0b16bb8aa5f"},
{580000, "93aea06936fa4dc0a84c9109c9d5f0e1b0815f96898171e42fd2973d262ed9ac"},
{602000, "a05fd2fccbb5f567ece940ebb62a82fdb1517ff5696551ae704e5f0ef8edb979"},
{623000, "7c92dd374efd0221065c7d98fce0568a1a1c130b5da28bb3f338cdc367b93d0b"},
{645000, "1eeba944c0dd6b9a1228a425a74076fbdbeaf9b657ba7ef02547d99f971de70d"},
{667000, "a020c8fcaa567845d04b520bb7ebe721e097a9bed2bdb8971081f933b5b42995"},
{689000, "212ec2698c5ebd15d6242d59f36c2d186d11bb47c58054f476dd8e6b1c7f0008"},
{713000, "a03f836c4a19f907cd6cac095eb6f56f5279ca2d1303fb7f826750dcb9025495"}
};
} // cryptonote

View file

@ -0,0 +1,36 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "CoreConfig.h"
#include "common/util.h"
#include "common/command_line.h"
namespace cryptonote {
CoreConfig::CoreConfig() {
configFolder = tools::get_default_data_dir();
}
void CoreConfig::init(const boost::program_options::variables_map& options) {
configFolder = command_line::get_arg(options, command_line::arg_data_dir);
}
void CoreConfig::initOptions(boost::program_options::options_description& desc) {
}
} //namespace cryptonote

View file

@ -0,0 +1,36 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <string>
#include <boost/program_options.hpp>
namespace cryptonote {
class CoreConfig {
public:
CoreConfig();
static void initOptions(boost::program_options::options_description& desc);
void init(const boost::program_options::variables_map& options);
std::string configFolder;
};
} //namespace cryptonote

View file

@ -0,0 +1,26 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
namespace cryptonote {
class IBlockchainStorageObserver {
public:
virtual ~IBlockchainStorageObserver() {
}
virtual void blockchainUpdated() = 0;
};
}

61
src/cryptonote_core/ICore.h Executable file
View file

@ -0,0 +1,61 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <cstdint>
#include <list>
#include <utility>
#include <vector>
#include "crypto/hash.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 Block;
struct Transaction;
struct i_cryptonote_protocol;
struct tx_verification_context;
struct BlockFullInfo;
class ICoreObserver;
class ICore {
public:
virtual ~ICore() {}
virtual bool addObserver(ICoreObserver* observer) = 0;
virtual bool removeObserver(ICoreObserver* observer) = 0;
virtual bool get_blockchain_top(uint64_t& height, crypto::hash& top_id) = 0;
virtual bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::list<std::pair<Block, std::list<Transaction> > >& blocks,
uint64_t& total_height, uint64_t& start_height, size_t max_count) = 0;
virtual bool find_blockchain_supplement(const std::list<crypto::hash>& 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<uint64_t>& 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 bool getPoolSymmetricDifference(const std::vector<crypto::hash>& known_pool_tx_ids, const crypto::hash& known_block_id, bool& isBcActual, std::vector<Transaction>& new_txs, std::vector<crypto::hash>& deleted_tx_ids) = 0;
virtual bool queryBlocks(const std::list<crypto::hash>& block_ids, uint64_t timestamp,
uint64_t& start_height, uint64_t& current_height, uint64_t& full_offset, std::list<BlockFullInfo>& entries) = 0;
virtual bool getBlockByHash(const crypto::hash &h, Block &blk) = 0;
};
} //namespace cryptonote

View file

@ -0,0 +1,29 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
namespace cryptonote {
class ICoreObserver {
public:
virtual ~ICoreObserver() {};
virtual void blockchainUpdated() {};
virtual void poolUpdated() {};
};
} //namespace cryptonote

View file

@ -0,0 +1,26 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
namespace cryptonote {
class ITxPoolObserver {
public:
virtual ~ITxPoolObserver() {
}
virtual void txDeletedFromPool() = 0;
};
}

View file

@ -0,0 +1,55 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "MinerConfig.h"
#include "common/command_line.h"
namespace cryptonote {
namespace {
const command_line::arg_descriptor<std::string> arg_extra_messages = {"extra-messages-file", "Specify file for extra messages to include into coinbase transactions", "", true};
const command_line::arg_descriptor<std::string> arg_start_mining = {"start-mining", "Specify wallet address to mining for", "", true};
const command_line::arg_descriptor<uint32_t> arg_mining_threads = {"mining-threads", "Specify mining threads count", 0, true};
}
MinerConfig::MinerConfig() {
miningThreads = 0;
}
void MinerConfig::initOptions(boost::program_options::options_description& desc) {
command_line::add_arg(desc, arg_extra_messages);
command_line::add_arg(desc, arg_start_mining);
command_line::add_arg(desc, arg_mining_threads);
}
void MinerConfig::init(const boost::program_options::variables_map& options) {
if(command_line::has_arg(options, arg_extra_messages)) {
extraMessages = command_line::get_arg(options, arg_extra_messages);
}
if (command_line::has_arg(options, arg_start_mining)) {
startMining = command_line::get_arg(options, arg_start_mining);
}
if (command_line::has_arg(options, arg_mining_threads)) {
miningThreads = command_line::get_arg(options, arg_mining_threads);
}
}
} //namespace cryptonote

View file

@ -0,0 +1,39 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <cstdint>
#include <string>
#include <boost/program_options.hpp>
namespace cryptonote {
class MinerConfig {
public:
MinerConfig();
static void initOptions(boost::program_options::options_description& desc);
void init(const boost::program_options::variables_map& options);
std::string extraMessages;
std::string startMining;
uint32_t miningThreads;
};
} //namespace cryptonote

View file

@ -0,0 +1,608 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#include "ITransaction.h"
#include "TransactionExtra.h"
#include "cryptonote_format_utils.h"
#include "account.h"
#include <boost/optional.hpp>
#include <numeric>
#include <unordered_set>
namespace {
using namespace cryptonote;
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<const crypto::public_key*>(&to.viewPublicKey), txKey, derivation);
crypto::derive_public_key(derivation, outputIndex, *reinterpret_cast<const crypto::public_key*>(&to.spendPublicKey), ephemeralKey);
}
bool checkInputsKeyimagesDiff(const cryptonote::Transaction& tx) {
std::unordered_set<crypto::key_image> ki;
for (const auto& in : tx.vin) {
if (in.type() == typeid(TransactionInputToKey)) {
if (!ki.insert(boost::get<TransactionInputToKey>(in).keyImage).second)
return false;
}
}
return true;
}
// TransactionInput helper functions
size_t getRequiredSignaturesCount(const TransactionInput& in) {
if (in.type() == typeid(TransactionInputToKey)) {
return boost::get<TransactionInputToKey>(in).keyOffsets.size();
}
if (in.type() == typeid(TransactionInputMultisignature)) {
return boost::get<TransactionInputMultisignature>(in).signatures;
}
return 0;
}
uint64_t getTransactionInputAmount(const TransactionInput& in) {
if (in.type() == typeid(TransactionInputToKey)) {
return boost::get<TransactionInputToKey>(in).amount;
}
if (in.type() == typeid(TransactionInputMultisignature)) {
return boost::get<TransactionInputMultisignature>(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 Transaction : public ITransaction {
public:
Transaction();
Transaction(const Blob& txblob);
Transaction(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;
// 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<uint32_t>& 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;
// Inputs/Outputs
virtual size_t addInput(const TransactionTypes::InputKey& input) override;
virtual size_t addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, 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<AccountAddress>& to, uint32_t requiredSignatures) 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;
// secret key
virtual bool getTransactionSecretKey(SecretKey& key) const override;
virtual void setTransactionSecretKey(const SecretKey& key) override;
private:
std::vector<crypto::signature>& getSignatures(size_t input);
const crypto::secret_key& txSecretKey() const {
if (!secretKey) {
throw std::runtime_error("Operation requires transaction secret key");
}
return *secretKey;
}
cryptonote::Transaction constructFinalTransaction() const {
cryptonote::Transaction tx(transaction);
tx.extra = extra.serialize();
return tx;
}
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<crypto::secret_key> secretKey;
TransactionExtra extra;
};
////////////////////////////////////////////////////////////////////////
// class Transaction implementation
////////////////////////////////////////////////////////////////////////
std::unique_ptr<ITransaction> createTransaction() {
return std::unique_ptr<ITransaction>(new Transaction());
}
std::unique_ptr<ITransaction> createTransaction(const Blob& transactionBlob) {
return std::unique_ptr<ITransaction>(new Transaction(transactionBlob));
}
std::unique_ptr<ITransaction> createTransaction(const cryptonote::Transaction& tx) {
return std::unique_ptr<ITransaction>(new Transaction(tx));
}
Transaction::Transaction() {
cryptonote::KeyPair txKeys(cryptonote::KeyPair::generate());
transaction.version = CURRENT_TRANSACTION_VERSION;
transaction.unlockTime = 0;
tx_extra_pub_key pk = { txKeys.pub };
extra.set(pk);
secretKey = txKeys.sec;
}
Transaction::Transaction(const Blob& data) {
cryptonote::blobdata blob(reinterpret_cast<const char*>(data.data()), data.size());
if (!cryptonote::parse_and_validate_tx_from_blob(blob, transaction)) {
throw std::runtime_error("Invalid transaction data");
}
extra.parse(transaction.extra);
}
Transaction::Transaction(const cryptonote::Transaction& tx) : transaction(tx) {
extra.parse(transaction.extra);
}
Hash Transaction::getTransactionHash() const {
auto hash = get_transaction_hash(constructFinalTransaction());
return reinterpret_cast<const Hash&>(hash);
}
Hash Transaction::getTransactionPrefixHash() const {
auto hash = get_transaction_prefix_hash(constructFinalTransaction());
return reinterpret_cast<const Hash&>(hash);
}
PublicKey Transaction::getTransactionPublicKey() const {
crypto::public_key pk(null_pkey);
extra.getPublicKey(pk);
return reinterpret_cast<const PublicKey&>(pk);
}
uint64_t Transaction::getUnlockTime() const {
return transaction.unlockTime;
}
void Transaction::setUnlockTime(uint64_t unlockTime) {
checkIfSigning();
transaction.unlockTime = unlockTime;
}
bool Transaction::getTransactionSecretKey(SecretKey& key) const {
if (!secretKey) {
return false;
}
key = reinterpret_cast<const SecretKey&>(secretKey.get());
return true;
}
void Transaction::setTransactionSecretKey(const SecretKey& key) {
const auto& sk = reinterpret_cast<const crypto::secret_key&>(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<const crypto::secret_key&>(key);
}
size_t Transaction::addInput(const InputKey& input) {
checkIfSigning();
TransactionInputToKey inKey = { input.amount, input.keyOffsets, *reinterpret_cast<const crypto::key_image*>(&input.keyImage) };
transaction.vin.emplace_back(inKey);
return transaction.vin.size() - 1;
}
size_t Transaction::addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, KeyPair& ephKeys) {
checkIfSigning();
InputKey input;
input.amount = info.amount;
generate_key_image_helper(
reinterpret_cast<const account_keys&>(senderKeys),
reinterpret_cast<const crypto::public_key&>(info.realOutput.transactionPublicKey),
info.realOutput.outputInTransaction,
reinterpret_cast<cryptonote::KeyPair&>(ephKeys),
reinterpret_cast<crypto::key_image&>(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 Transaction::addInput(const InputMultisignature& input) {
checkIfSigning();
TransactionInputMultisignature inMsig;
inMsig.amount = input.amount;
inMsig.outputIndex = input.outputIndex;
inMsig.signatures = input.signatures;
transaction.vin.push_back(inMsig);
return transaction.vin.size() - 1;
}
size_t Transaction::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);
return transaction.vout.size() - 1;
}
size_t Transaction::addOutput(uint64_t amount, const std::vector<AccountAddress>& 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 (int i = 0; i < to.size(); ++i) {
derivePublicKey(to[i], txKey, outputIndex, outMsig.keys[i]);
}
TransactionOutput out = { amount, outMsig };
transaction.vout.emplace_back(out);
return outputIndex;
}
void Transaction::signInputKey(size_t index, const TransactionTypes::InputKeyInfo& info, const KeyPair& ephKeys) {
const auto& input = boost::get<TransactionInputToKey>(getInputChecked(transaction, index, InputType::Key));
Hash prefixHash = getTransactionPrefixHash();
std::vector<crypto::signature> signatures;
std::vector<const crypto::public_key*> keysPtrs;
for (const auto& o : info.outputs) {
keysPtrs.push_back(reinterpret_cast<const crypto::public_key*>(&o.targetKey));
}
signatures.resize(keysPtrs.size());
generate_ring_signature(
reinterpret_cast<const crypto::hash&>(prefixHash),
reinterpret_cast<const crypto::key_image&>(input.keyImage),
keysPtrs,
reinterpret_cast<const crypto::secret_key&>(ephKeys.secretKey),
info.realOutput.transactionIndex,
signatures.data());
getSignatures(index) = signatures;
}
void Transaction::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<const crypto::public_key&>(sourceTransactionKey),
reinterpret_cast<const crypto::secret_key&>(accountKeys.viewSecretKey),
derivation);
crypto::derive_public_key(derivation, outputIndex,
reinterpret_cast<const crypto::public_key&>(accountKeys.address.spendPublicKey), ephemeralPublicKey);
crypto::derive_secret_key(derivation, outputIndex,
reinterpret_cast<const crypto::secret_key&>(accountKeys.spendSecretKey), ephemeralSecretKey);
crypto::signature signature;
auto txPrefixHash = getTransactionPrefixHash();
crypto::generate_signature(reinterpret_cast<const crypto::hash&>(txPrefixHash),
ephemeralPublicKey, ephemeralSecretKey, signature);
getSignatures(index).push_back(signature);
}
std::vector<crypto::signature>& Transaction::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<uint8_t> Transaction::getTransactionData() const {
return stringToVector(t_serializable_object_to_blob(constructFinalTransaction()));
}
void Transaction::setPaymentId(const Hash& hash) {
checkIfSigning();
blobdata paymentIdBlob;
set_payment_id_to_tx_extra_nonce(paymentIdBlob, reinterpret_cast<const crypto::hash&>(hash));
setExtraNonce(paymentIdBlob);
}
bool Transaction::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<const Hash&>(paymentId);
return true;
}
}
return false;
}
void Transaction::setExtraNonce(const std::string& nonce) {
checkIfSigning();
tx_extra_nonce extraNonce = { nonce };
extra.set(extraNonce);
}
bool Transaction::getExtraNonce(std::string& nonce) const {
tx_extra_nonce extraNonce;
if (extra.get(extraNonce)) {
nonce = extraNonce.nonce;
return true;
}
return false;
}
size_t Transaction::getInputCount() const {
return transaction.vin.size();
}
uint64_t Transaction::getInputTotalAmount() const {
return std::accumulate(transaction.vin.begin(), transaction.vin.end(), 0ULL, [](uint64_t val, const TransactionInput& in) {
return val + getTransactionInputAmount(in); });
}
TransactionTypes::InputType Transaction::getInputType(size_t index) const {
return getTransactionInputType(getInputChecked(transaction, index));
}
void Transaction::getInput(size_t index, InputKey& input) const {
const auto& k = boost::get<TransactionInputToKey>(getInputChecked(transaction, index, InputType::Key));
input.amount = k.amount;
input.keyImage = reinterpret_cast<const KeyImage&>(k.keyImage);
input.keyOffsets = k.keyOffsets;
}
void Transaction::getInput(size_t index, InputMultisignature& input) const {
const auto& m = boost::get<TransactionInputMultisignature>(getInputChecked(transaction, index, InputType::Multisignature));
input.amount = m.amount;
input.outputIndex = m.outputIndex;
input.signatures = m.signatures;
}
size_t Transaction::getOutputCount() const {
return transaction.vout.size();
}
uint64_t Transaction::getOutputTotalAmount() const {
return std::accumulate(transaction.vout.begin(), transaction.vout.end(), 0ULL, [](uint64_t val, const TransactionOutput& out) {
return val + out.amount; });
}
TransactionTypes::OutputType Transaction::getOutputType(size_t index) const {
return getTransactionOutputType(getOutputChecked(transaction, index).target);
}
void Transaction::getOutput(size_t index, OutputKey& output) const {
const auto& out = getOutputChecked(transaction, index, OutputType::Key);
const auto& k = boost::get<TransactionOutputToKey>(out.target);
output.amount = out.amount;
output.key = reinterpret_cast<const PublicKey&>(k.key);
}
void Transaction::getOutput(size_t index, OutputMultisignature& output) const {
const auto& out = getOutputChecked(transaction, index, OutputType::Multisignature);
const auto& m = boost::get<TransactionOutputMultisignature>(out.target);
output.amount = out.amount;
output.keys = reinterpret_cast<const std::vector<PublicKey>&>(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 Transaction::findOutputsToAccount(const AccountAddress& addr, const SecretKey& viewSecretKey, std::vector<uint32_t>& out, uint64_t& amount) const {
account_keys keys;
keys.m_account_address = reinterpret_cast<const AccountPublicAddress&>(addr);
// only view secret key is used, spend key is not needed
keys.m_view_secret_key = reinterpret_cast<const crypto::secret_key&>(viewSecretKey);
auto pk = getTransactionPublicKey();
crypto::public_key txPubKey = reinterpret_cast<const crypto::public_key&>(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<TransactionOutputToKey>(o.target), derivation, keyIndex)) {
out.push_back(outputIndex);
amount += o.amount;
}
++keyIndex;
} else if (o.target.type() == typeid(TransactionOutputMultisignature)) {
const auto& target = boost::get<TransactionOutputMultisignature>(o.target);
for (const auto& key : target.keys) {
if (isOutToKey(keys.m_account_address.m_spendPublicKey, key, derivation, static_cast<size_t>(outputIndex))) {
out.push_back(outputIndex);
}
++keyIndex;
}
}
++outputIndex;
}
return true;
}
size_t Transaction::getRequiredSignaturesCount(size_t index) const {
return ::getRequiredSignaturesCount(getInputChecked(transaction, index));
}
bool Transaction::validateInputs() const {
return
check_inputs_types_supported(transaction) &&
check_inputs_overflow(transaction) &&
checkInputsKeyimagesDiff(transaction) &&
checkMultisignatureInputsDiff(transaction);
}
bool Transaction::validateOutputs() const {
return
check_outs_valid(transaction) &&
check_outs_overflow(transaction);
}
bool Transaction::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;
}
}

View file

@ -17,30 +17,15 @@
#pragma once
#include "IWallet.h"
#include <memory>
#include "ITransaction.h"
#include <map>
#include <mutex>
namespace cryptonote {
struct Transaction;
}
namespace CryptoNote {
class WalletTxSendingState
{
public:
enum State
{
SENDING,
ERRORED,
NOT_FOUND
};
void sending(TransactionId id);
void sent(TransactionId id);
void error(TransactionId id);
State state(TransactionId id);
private:
std::map<TransactionId, State> m_states;
};
} //namespace CryptoNote
std::unique_ptr<ITransaction> createTransaction();
std::unique_ptr<ITransaction> createTransaction(const Blob& transactionBlob);
std::unique_ptr<ITransaction> createTransaction(const cryptonote::Transaction& tx);
}

View file

@ -0,0 +1,94 @@
// Copyright (c) 2012-2014, 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 <http://www.gnu.org/licenses/>.
#pragma once
#include "cryptonote_format_utils.h"
namespace CryptoNote {
inline std::vector<uint8_t> stringToVector(const std::string& s) {
std::vector<uint8_t> vec(
reinterpret_cast<const uint8_t*>(s.data()),
reinterpret_cast<const uint8_t*>(s.data()) + s.size());
return vec;
}
class TransactionExtra {
public:
TransactionExtra() {}
TransactionExtra(const std::vector<uint8_t>& extra) {
parse(extra);
}
bool parse(const std::vector<uint8_t>& extra) {
fields.clear();
return cryptonote::parse_tx_extra(extra, fields);
}
template <typename T>
bool get(T& value) const {
auto it = find(typeid(T));
if (it == fields.end()) {
return false;
}
value = boost::get<T>(*it);
return true;
}
template <typename T>
void set(const T& value) {
auto it = find(typeid(T));
if (it != fields.end()) {
*it = value;
} else {
fields.push_back(value);
}
}
bool getPublicKey(crypto::public_key& pk) const {
cryptonote::tx_extra_pub_key extraPk;
if (!get(extraPk)) {
return false;
}
pk = extraPk.pub_key;
return true;
}
std::vector<uint8_t> serialize() const {
std::ostringstream out;
binary_archive<true> ar(out);
for (const auto& f : fields) {
::do_serialize(ar, const_cast<cryptonote::tx_extra_field&>(f));
}
return stringToVector(out.str());
}
private:
std::vector<cryptonote::tx_extra_field>::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<cryptonote::tx_extra_field>::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<cryptonote::tx_extra_field> fields;
};
}

View file

@ -50,5 +50,9 @@ namespace cryptonote
{
return m_keys;
}
void account_base::set_keys(const account_keys& keys) {
m_keys = keys;
}
//-----------------------------------------------------------------
}

View file

@ -36,7 +36,9 @@ namespace cryptonote {
public:
account_base();
void generate();
const account_keys& get_keys() const;
void set_keys(const account_keys& keys);
uint64_t get_createtime() const { return m_creation_timestamp; }
void set_createtime(uint64_t val) { m_creation_timestamp = val; }

View file

@ -31,6 +31,7 @@
#include "time_helper.h"
#include "common/boost_serialization_helper.h"
#include "common/ShuffleGenerator.h"
#include "cryptonote_format_utils.h"
#include "cryptonote_boost_serialization.h"
#include "rpc/core_rpc_server_commands_defs.h"
@ -292,6 +293,14 @@ blockchain_storage::blockchain_storage(const Currency& currency, tx_memory_pool&
m_spent_keys.set_deleted_key(nullImage);
}
bool blockchain_storage::addObserver(IBlockchainStorageObserver* observer) {
return m_observerManager.add(observer);
}
bool blockchain_storage::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);
}
@ -399,14 +408,26 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi
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<TransactionInputToKey>(i).keyImage);
} else if (i.type() == typeid(TransactionInputMultisignature)) {
auto out = ::boost::get<TransactionInputMultisignature>(i);
m_multisignatureOutputs[out.amount][out.outputIndex].isUsed = true;
}
}
// process outputs
for (uint16_t o = 0; o < transaction.tx.vout.size(); ++o) {
m_outputs[transaction.tx.vout[o].amount].push_back(std::make_pair<>(transactionIndex, 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);
}
}
}
}
@ -490,6 +511,22 @@ crypto::hash blockchain_storage::get_tail_id() {
return m_blockIndex.getTailId();
}
bool blockchain_storage::getPoolSymmetricDifference(const std::vector<crypto::hash>& known_pool_tx_ids, const crypto::hash& known_block_id, std::vector<Transaction>& new_txs, std::vector<crypto::hash>& deleted_tx_ids) {
CRITICAL_REGION_LOCAL1(m_tx_pool);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
if (known_block_id != get_tail_id()) {
return false;
}
std::vector<crypto::hash> new_tx_ids;
m_tx_pool.get_difference(known_pool_tx_ids, new_tx_ids, deleted_tx_ids);
std::vector<crypto::hash> misses;
get_transactions(new_tx_ids, new_txs, misses, true);
assert(misses.empty());
return true;
}
bool blockchain_storage::get_short_chain_history(std::list<crypto::hash>& ids) {
CRITICAL_REGION_LOCAL(m_blockchain_lock);
return m_blockIndex.getShortChainHistory(ids);
@ -1166,8 +1203,8 @@ size_t blockchain_storage::find_end_of_allowed_index(const std::vector<std::pair
}
bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) {
srand(static_cast<unsigned int>(time(NULL)));
CRITICAL_REGION_LOCAL(m_blockchain_lock);
for (uint64_t amount : req.amounts) {
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs = *res.outs.insert(res.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount());
result_outs.amount = amount;
@ -1182,22 +1219,11 @@ bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDO
//lets find upper bound of not fresh outs
size_t up_index_limit = find_end_of_allowed_index(amount_outs);
CHECK_AND_ASSERT_MES(up_index_limit <= amount_outs.size(), false, "internal error: find_end_of_allowed_index returned wrong index=" << up_index_limit << ", with amount_outs.size = " << amount_outs.size());
if (amount_outs.size() > req.outs_count) {
std::set<size_t> used;
size_t try_count = 0;
for (uint64_t j = 0; j != req.outs_count && try_count < up_index_limit;) {
size_t i = rand() % up_index_limit;
if (used.count(i))
continue;
bool added = add_out_to_get_random_outs(amount_outs, result_outs, amount, i);
used.insert(i);
if (added)
++j;
++try_count;
}
} else {
for (size_t i = 0; i != up_index_limit; i++) {
add_out_to_get_random_outs(amount_outs, result_outs, amount, i);
if (up_index_limit > 0) {
ShuffleGenerator<size_t, crypto::random_engine<size_t>> 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());
}
}
}
@ -1621,8 +1647,9 @@ bool blockchain_storage::add_new_block(const Block& bl_, block_verification_cont
return false;
}
CRITICAL_REGION_LOCAL(m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process
CRITICAL_REGION_LOCAL1(m_blockchain_lock);
bool add_result;
CRITICAL_REGION_BEGIN(m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process
CRITICAL_REGION_BEGIN1(m_blockchain_lock);
if (have_block(id)) {
LOG_PRINT_L3("block with id = " << id << " already exists");
bvc.m_already_exists = true;
@ -1633,11 +1660,18 @@ bool blockchain_storage::add_new_block(const Block& bl_, block_verification_cont
if (!(bl.prevId == get_tail_id())) {
//chain switching or wrong block
bvc.m_added_to_main_chain = false;
return handle_alternative_block(bl, id, bvc);
//never relay alternative blocks
add_result = handle_alternative_block(bl, id, bvc);
} else {
add_result = pushBlock(bl, bvc);
}
CRITICAL_REGION_END();
CRITICAL_REGION_END();
if (add_result && bvc.m_added_to_main_chain) {
m_observerManager.notify(&IBlockchainStorageObserver::blockchainUpdated);
}
return pushBlock(bl, bvc);
return add_result;
}
const blockchain_storage::TransactionEntry& blockchain_storage::transactionByIndex(TransactionIndex index) {
@ -1864,7 +1898,8 @@ bool blockchain_storage::pushTransaction(BlockEntry& block, const crypto::hash&
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];
MultisignatureOutputUsage outputUsage = {transactionIndex, output, false};
transaction.m_global_output_indexes[output] = amountOutputs.size();
MultisignatureOutputUsage outputUsage = { transactionIndex, output, false };
amountOutputs.push_back(outputUsage);
}
}

View file

@ -19,19 +19,21 @@
#include <atomic>
#include "Currency.h"
#include "SwappedVector.h"
#include "UpgradeDetector.h"
#include "cryptonote_format_utils.h"
#include "tx_pool.h"
#include "common/util.h"
#include "checkpoints.h"
#include "google/sparse_hash_set"
#include "google/sparse_hash_map"
#include "ITransactionValidator.h"
#include "BlockIndex.h"
#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"
namespace cryptonote {
struct NOTIFY_RESPONSE_CHAIN_ENTRY_request;
@ -46,6 +48,9 @@ namespace cryptonote {
public:
blockchain_storage(const Currency& currency, tx_memory_pool& tx_pool);
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);
@ -94,6 +99,8 @@ namespace cryptonote {
uint64_t get_current_comulative_blocksize_limit();
bool is_storing_blockchain(){return m_is_blockchain_storing;}
uint64_t block_difficulty(size_t i);
bool getPoolSymmetricDifference(const std::vector<crypto::hash>& known_pool_tx_ids, const crypto::hash& known_block_id, std::vector<Transaction>& new_txs, std::vector<crypto::hash>& deleted_tx_ids);
template<class t_ids_container, class t_blocks_container, class t_missed_container>
bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) {
@ -195,6 +202,7 @@ namespace cryptonote {
tx_memory_pool& m_tx_pool;
epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock
crypto::cn_context m_cn_context;
tools::ObserverManager<IBlockchainStorageObserver> m_observerManager;
key_images_container m_spent_keys;
size_t m_current_block_cumul_sz_limit;

View file

@ -28,6 +28,7 @@
#include "cryptonote_core/tx_extra.h"
#include "serialization/binary_archive.h"
#include "serialization/crypto.h"
#include "serialization/keyvalue_serialization.h" // eepe named serialization
#include "serialization/debug_archive.h"
#include "serialization/json_archive.h"
#include "serialization/serialization.h"
@ -219,7 +220,6 @@ namespace cryptonote {
ar.end_array();
END_SERIALIZE()
private:
static size_t getSignatureSize(const TransactionInput& input) {
struct txin_signature_size_visitor : public boost::static_visitor<size_t> {
size_t operator()(const TransactionInputGenerate& txin) const { return 0; }
@ -318,7 +318,6 @@ namespace cryptonote {
}
END_SERIALIZE()
private:
ParentBlock& m_parentBlock;
uint64_t& m_timestamp;
uint32_t& m_nonce;

109
src/cryptonote_core/cryptonote_core.cpp Normal file → Executable file
View file

@ -32,6 +32,7 @@
#include "cryptonote_core/cryptonote_format_utils.h"
#include "cryptonote_core/cryptonote_stat_info.h"
#include "cryptonote_core/miner.h"
#include "cryptonote_core/CoreConfig.h"
#include "cryptonote_config.h"
#include "cryptonote_protocol/cryptonote_protocol_defs.h"
#include "rpc/core_rpc_server_commands_defs.h"
@ -53,9 +54,12 @@ namespace cryptonote
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)
@ -116,17 +120,15 @@ namespace cryptonote
return m_blockchain_storage.get_alternative_blocks_count();
}
//-----------------------------------------------------------------------------------------------
bool core::init(const boost::program_options::variables_map& vm, bool load_existing)
{
bool r = handle_command_line(vm);
r = m_mempool.init(m_config_folder);
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);
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool");
r = m_blockchain_storage.init(m_config_folder, load_existing);
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage");
r = m_miner->init(vm);
r = m_miner->init(minerConfig);
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage");
return load_state_data();
@ -203,6 +205,7 @@ namespace cryptonote
if (tvc.m_added_to_pool) {
LOG_PRINT_L1("tx added: " << tx_hash);
poolUpdated();
}
return r;
@ -390,6 +393,11 @@ namespace cryptonote
m_miner->on_synchronized();
}
//-----------------------------------------------------------------------------------------------
bool core::getPoolSymmetricDifference(const std::vector<crypto::hash>& known_pool_tx_ids, const crypto::hash& known_block_id, bool& isBcActual, std::vector<Transaction>& new_txs, std::vector<crypto::hash>& deleted_tx_ids) {
isBcActual = m_blockchain_storage.getPoolSymmetricDifference(known_pool_tx_ids, known_block_id, new_txs, deleted_tx_ids);
return true;
}
//-----------------------------------------------------------------------------------------------
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()) {
LOG_PRINT_L0("WRONG BLOCK BLOB, too big size " << block_blob.size() << ", rejected");
@ -485,6 +493,10 @@ namespace cryptonote
return m_blockchain_storage.handle_get_objects(arg, rsp);
}
//-----------------------------------------------------------------------------------------------
bool core::getBlockByHash(const crypto::hash &h, Block &blk) {
return core::get_block_by_hash(h, blk);
}
crypto::hash core::get_block_id_by_height(uint64_t height)
{
return m_blockchain_storage.get_block_id_by_height(height);
@ -530,4 +542,89 @@ namespace cryptonote
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<crypto::hash>& knownBlockIds, uint64_t timestamp,
uint64_t& resStartHeight, uint64_t& resCurrentHeight, uint64_t& resFullOffset, std::list<BlockFullInfo>& 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<crypto::hash> 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<Block> 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<Transaction> txs;
std::list<crypto::hash> 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;
}
}

View file

@ -26,9 +26,13 @@
#include "tx_pool.h"
#include "blockchain_storage.h"
#include "cryptonote_core/i_miner_handler.h"
#include "cryptonote_core/MinerConfig.h"
#include "connection_context.h"
#include "warnings.h"
#include "crypto/hash.h"
#include "ICore.h"
#include "ICoreObserver.h"
#include "common/ObserverManager.h"
PUSH_WARNINGS
DISABLE_VS_WARNINGS(4355)
@ -36,30 +40,33 @@ DISABLE_VS_WARNINGS(4355)
namespace cryptonote {
struct core_stat_info;
class miner;
class CoreConfig;
class core: public i_miner_handler {
class core : public ICore, public i_miner_handler, public IBlockchainStorageObserver, public ITxPoolObserver {
public:
core(const Currency& currency, i_cryptonote_protocol* pprotocol);
~core();
bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp, cryptonote_connection_context& context);
bool on_idle();
bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block);
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);
const Currency& currency() const { return m_currency; }
i_cryptonote_protocol* get_protocol(){return m_pprotocol;}
virtual i_cryptonote_protocol* get_protocol(){return m_pprotocol;}
//-------------------- i_miner_handler -----------------------
virtual bool handle_block_found(Block& b);
virtual bool get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint64_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 boost::program_options::variables_map& vm, bool load_existing);
bool init(const CoreConfig& config, const MinerConfig& minerConfig, bool load_existing);
bool set_genesis_block(const Block& b);
bool deinit();
uint64_t get_current_blockchain_height();
bool get_blockchain_top(uint64_t& heeight, crypto::hash& top_id);
virtual bool get_blockchain_top(uint64_t& heeight, crypto::hash& top_id);
bool get_blocks(uint64_t start_offset, size_t count, std::list<Block>& blocks, std::list<Transaction>& txs);
bool get_blocks(uint64_t start_offset, size_t count, std::list<Block>& blocks);
template<class t_ids_container, class t_blocks_container, class t_missed_container>
@ -67,11 +74,15 @@ namespace cryptonote {
{
return m_blockchain_storage.get_blocks(block_ids, blocks, missed_bs);
}
virtual bool queryBlocks(const std::list<crypto::hash>& block_ids, uint64_t timestamp,
uint64_t& start_height, uint64_t& current_height, uint64_t& full_offset, std::list<BlockFullInfo>& entries);
crypto::hash get_block_id_by_height(uint64_t height);
void get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<Transaction>& txs, std::list<crypto::hash>& missed_txs);
bool get_block_by_hash(const crypto::hash &h, Block &blk);
//void get_all_known_block_ids(std::list<crypto::hash> &main, std::list<crypto::hash> &alt, std::list<crypto::hash> &invalid);
virtual bool getBlockByHash(const crypto::hash &h, Block &blk) override;
bool get_alternative_blocks(std::list<Block>& blocks);
size_t get_alternative_blocks_count();
@ -84,13 +95,13 @@ namespace cryptonote {
//bool get_outs(uint64_t amount, std::list<crypto::public_key>& pkeys);
bool have_block(const crypto::hash& id);
bool get_short_chain_history(std::list<crypto::hash>& ids);
bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY_request& resp);
bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::list<std::pair<Block, std::list<Transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count);
virtual bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY_request& resp);
virtual bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::list<std::pair<Block, std::list<Transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count);
bool get_stat_info(core_stat_info& st_inf);
//bool get_backward_blocks_sizes(uint64_t from_height, std::vector<size_t>& sizes, size_t count);
bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs);
virtual bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs);
crypto::hash get_tail_id();
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);
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;}
@ -100,6 +111,7 @@ namespace cryptonote {
std::string print_pool(bool short_format);
void print_blockchain_outs(const std::string& file);
void on_synchronized();
virtual bool getPoolSymmetricDifference(const std::vector<crypto::hash>& known_pool_tx_ids, const crypto::hash& known_block_id, bool& isBcActual, std::vector<Transaction>& new_txs, std::vector<crypto::hash>& deleted_tx_ids) 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);
@ -121,6 +133,9 @@ namespace cryptonote {
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;
CryptoNote::RealTimeProvider m_timeProvider;
@ -133,6 +148,7 @@ namespace cryptonote {
cryptonote_protocol_stub m_protocol_stub;
friend class tx_validate_inputs;
std::atomic<bool> m_starter_message_showed;
tools::ObserverManager<cryptonote::ICoreObserver> m_observerManager;
};
}

Some files were not shown because too many files have changed in this diff Show more