Multisignature API, Low level and High level APIs
This commit is contained in:
parent
257a2bf339
commit
1743402759
277 changed files with 30264 additions and 9849 deletions
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/mpl/contains.hpp>
|
||||
#include <boost/mpl/vector.hpp>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "warnings.h"
|
||||
|
||||
#ifndef OUT
|
||||
|
|
|
@ -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
29
include/IObservable.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
30
include/IStreamSerializable.h
Normal file
30
include/IStreamSerializable.h
Normal 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
180
include/ITransaction.h
Normal 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() { }
|
||||
|
||||
};
|
||||
|
||||
}
|
105
include/ITransfersContainer.h
Normal file
105
include/ITransfersContainer.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
75
include/ITransfersSynchronizer.h
Normal file
75
include/ITransfersSynchronizer.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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
35
src/CMakeLists.txt
Normal file → Executable 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
0
src/System/HttpParser.cpp → src/HTTP/HttpParser.cpp
Normal file → Executable file
0
src/System/HttpParser.h → src/HTTP/HttpParser.h
Normal file → Executable file
0
src/System/HttpParser.h → src/HTTP/HttpParser.h
Normal file → Executable file
0
src/System/HttpRequest.cpp → src/HTTP/HttpRequest.cpp
Normal file → Executable file
0
src/System/HttpRequest.cpp → src/HTTP/HttpRequest.cpp
Normal file → Executable file
0
src/System/HttpRequest.h → src/HTTP/HttpRequest.h
Normal file → Executable file
0
src/System/HttpRequest.h → src/HTTP/HttpRequest.h
Normal file → Executable file
0
src/System/HttpResponse.cpp → src/HTTP/HttpResponse.cpp
Normal file → Executable file
0
src/System/HttpResponse.cpp → src/HTTP/HttpResponse.cpp
Normal file → Executable file
0
src/System/HttpResponse.h → src/HTTP/HttpResponse.h
Normal file → Executable file
0
src/System/HttpResponse.h → src/HTTP/HttpResponse.h
Normal file → Executable file
160
src/Platform/Linux/System/Dispatcher.cpp
Executable file
160
src/Platform/Linux/System/Dispatcher.cpp
Executable 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();
|
||||
}
|
||||
}
|
64
src/Platform/Linux/System/Dispatcher.h
Executable file
64
src/Platform/Linux/System/Dispatcher.h
Executable 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);
|
||||
};
|
||||
|
||||
}
|
60
src/System/Event.cpp → src/Platform/Linux/System/Event.cpp
Normal file → Executable file
60
src/System/Event.cpp → src/Platform/Linux/System/Event.cpp
Normal file → Executable 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);
|
||||
}
|
||||
}
|
16
src/System/Event.h → src/Platform/Linux/System/Event.h
Normal file → Executable file
16
src/System/Event.h → src/Platform/Linux/System/Event.h
Normal file → Executable 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;
|
||||
};
|
||||
|
||||
}
|
3
tests/functional_tests/transactions_generation_from_blockchain.h → src/Platform/Linux/System/InterruptedException.cpp
Normal file → Executable file
3
tests/functional_tests/transactions_generation_from_blockchain.h → src/Platform/Linux/System/InterruptedException.cpp
Normal file → Executable 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"
|
10
utils/test-static-assert.c → src/Platform/Linux/System/InterruptedException.h
Normal file → Executable file
10
utils/test-static-assert.c → src/Platform/Linux/System/InterruptedException.h
Normal file → Executable 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 {
|
||||
};
|
297
src/Platform/Linux/System/TcpConnection.cpp
Executable file
297
src/Platform/Linux/System/TcpConnection.cpp
Executable 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");
|
||||
}
|
||||
}
|
14
src/System/TcpConnection.h → src/Platform/Linux/System/TcpConnection.h
Normal file → Executable file
14
src/System/TcpConnection.h → src/Platform/Linux/System/TcpConnection.h
Normal file → Executable 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;
|
||||
};
|
||||
|
||||
}
|
213
src/Platform/Linux/System/TcpConnector.cpp
Executable file
213
src/Platform/Linux/System/TcpConnector.cpp
Executable 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;
|
||||
}
|
50
src/Platform/Linux/System/TcpConnector.h
Executable file
50
src/Platform/Linux/System/TcpConnector.h
Executable 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;
|
||||
};
|
||||
|
||||
}
|
207
src/Platform/Linux/System/TcpListener.cpp
Executable file
207
src/Platform/Linux/System/TcpListener.cpp
Executable 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;
|
||||
}
|
49
src/Platform/Linux/System/TcpListener.h
Executable file
49
src/Platform/Linux/System/TcpListener.h
Executable 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;
|
||||
};
|
||||
|
||||
}
|
160
src/Platform/Linux/System/Timer.cpp
Executable file
160
src/Platform/Linux/System/Timer.cpp
Executable 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;
|
||||
}
|
46
src/Platform/Linux/System/Timer.h
Executable file
46
src/Platform/Linux/System/Timer.h
Executable 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;
|
||||
};
|
||||
|
||||
}
|
169
src/Platform/OSX/System/Dispatcher.cpp
Executable file
169
src/Platform/OSX/System/Dispatcher.cpp
Executable 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();
|
||||
}
|
||||
}
|
66
src/Platform/OSX/System/Dispatcher.h
Executable file
66
src/Platform/OSX/System/Dispatcher.h
Executable 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
106
src/Platform/OSX/System/Event.cpp
Executable 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
45
src/Platform/OSX/System/Event.h
Executable 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;
|
||||
};
|
||||
|
||||
}
|
18
src/Platform/OSX/System/InterruptedException.cpp
Executable file
18
src/Platform/OSX/System/InterruptedException.cpp
Executable 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"
|
27
src/Platform/OSX/System/InterruptedException.h
Executable file
27
src/Platform/OSX/System/InterruptedException.h
Executable 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 {
|
||||
};
|
||||
|
||||
}
|
247
src/Platform/OSX/System/TcpConnection.cpp
Executable file
247
src/Platform/OSX/System/TcpConnection.cpp
Executable 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;
|
||||
}
|
53
src/Platform/OSX/System/TcpConnection.h
Executable file
53
src/Platform/OSX/System/TcpConnection.h
Executable 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;
|
||||
};
|
||||
|
||||
}
|
213
src/Platform/OSX/System/TcpConnector.cpp
Executable file
213
src/Platform/OSX/System/TcpConnector.cpp
Executable 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");
|
||||
}
|
18
src/System/TcpConnector.h → src/Platform/OSX/System/TcpConnector.h
Normal file → Executable file
18
src/System/TcpConnector.h → src/Platform/OSX/System/TcpConnector.h
Normal file → Executable 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;
|
||||
};
|
||||
|
||||
}
|
202
src/Platform/OSX/System/TcpListener.cpp
Executable file
202
src/Platform/OSX/System/TcpListener.cpp
Executable 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;
|
||||
}
|
13
src/System/TcpListener.h → src/Platform/OSX/System/TcpListener.h
Normal file → Executable file
13
src/System/TcpListener.h → src/Platform/OSX/System/TcpListener.h
Normal file → Executable 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
137
src/Platform/OSX/System/Timer.cpp
Executable 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
46
src/Platform/OSX/System/Timer.h
Executable 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;
|
||||
};
|
||||
|
||||
}
|
186
src/Platform/Windows/System/Dispatcher.cpp
Executable file
186
src/Platform/Windows/System/Dispatcher.cpp
Executable 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();
|
||||
}
|
46
src/System/System.h → src/Platform/Windows/System/Dispatcher.h
Normal file → Executable file
46
src/System/System.h → src/Platform/Windows/System/Dispatcher.h
Normal file → Executable 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);
|
||||
};
|
||||
|
||||
}
|
107
src/Platform/Windows/System/Event.cpp
Executable file
107
src/Platform/Windows/System/Event.cpp
Executable 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);
|
||||
}
|
||||
}
|
45
src/Platform/Windows/System/Event.h
Executable file
45
src/Platform/Windows/System/Event.h
Executable 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;
|
||||
};
|
||||
|
||||
}
|
18
src/Platform/Windows/System/InterruptedException.cpp
Executable file
18
src/Platform/Windows/System/InterruptedException.cpp
Executable 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"
|
27
src/Platform/Windows/System/InterruptedException.h
Executable file
27
src/Platform/Windows/System/InterruptedException.h
Executable 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 {
|
||||
};
|
||||
|
||||
}
|
240
src/Platform/Windows/System/TcpConnection.cpp
Executable file
240
src/Platform/Windows/System/TcpConnection.cpp
Executable 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);
|
||||
}
|
52
src/Platform/Windows/System/TcpConnection.h
Executable file
52
src/Platform/Windows/System/TcpConnection.h
Executable 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;
|
||||
};
|
||||
|
||||
}
|
204
src/Platform/Windows/System/TcpConnector.cpp
Executable file
204
src/Platform/Windows/System/TcpConnector.cpp
Executable 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");
|
||||
}
|
49
src/Platform/Windows/System/TcpConnector.h
Executable file
49
src/Platform/Windows/System/TcpConnector.h
Executable 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;
|
||||
};
|
||||
|
||||
}
|
226
src/Platform/Windows/System/TcpListener.cpp
Executable file
226
src/Platform/Windows/System/TcpListener.cpp
Executable 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");
|
||||
}
|
48
src/Platform/Windows/System/TcpListener.h
Executable file
48
src/Platform/Windows/System/TcpListener.h
Executable 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;
|
||||
};
|
||||
|
||||
}
|
146
src/Platform/Windows/System/Timer.cpp
Executable file
146
src/Platform/Windows/System/Timer.cpp
Executable 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();
|
||||
}
|
||||
}
|
16
src/System/Timer.h → src/Platform/Windows/System/Timer.h
Normal file → Executable file
16
src/System/Timer.h → src/Platform/Windows/System/Timer.h
Normal file → Executable 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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
2
src/System/TcpStream.cpp
Normal file → Executable 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
6
src/System/TcpStream.h
Normal file → Executable 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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
70
src/common/ShuffleGenerator.h
Normal file
70
src/common/ShuffleGenerator.h
Normal 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;
|
||||
};
|
||||
|
|
@ -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
9
src/common/static_assert.h
Executable file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef __cplusplus
|
||||
#ifdef __clang__
|
||||
|
||||
#define static_assert _Static_assert
|
||||
|
||||
#endif
|
||||
#endif
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../common/static_assert.h"
|
||||
#include "common/int-util.h"
|
||||
#include "warnings.h"
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#if !defined(__cplusplus)
|
||||
#include <stddef.h>
|
||||
#endif
|
||||
|
||||
void generate_random_bytes(size_t n, void *result);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
36
src/cryptonote_core/CoreConfig.cpp
Normal file
36
src/cryptonote_core/CoreConfig.cpp
Normal 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
|
||||
|
36
src/cryptonote_core/CoreConfig.h
Normal file
36
src/cryptonote_core/CoreConfig.h
Normal 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
|
26
src/cryptonote_core/IBlockchainStorageObserver.h
Normal file
26
src/cryptonote_core/IBlockchainStorageObserver.h
Normal 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
61
src/cryptonote_core/ICore.h
Executable 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
|
29
src/cryptonote_core/ICoreObserver.h
Normal file
29
src/cryptonote_core/ICoreObserver.h
Normal 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
|
26
src/cryptonote_core/ITxPoolObserver.h
Executable file
26
src/cryptonote_core/ITxPoolObserver.h
Executable 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;
|
||||
};
|
||||
}
|
55
src/cryptonote_core/MinerConfig.cpp
Normal file
55
src/cryptonote_core/MinerConfig.cpp
Normal 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
|
||||
|
39
src/cryptonote_core/MinerConfig.h
Normal file
39
src/cryptonote_core/MinerConfig.h
Normal 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
|
608
src/cryptonote_core/Transaction.cpp
Normal file
608
src/cryptonote_core/Transaction.cpp
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
94
src/cryptonote_core/TransactionExtra.h
Normal file
94
src/cryptonote_core/TransactionExtra.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
|
@ -50,5 +50,9 @@ namespace cryptonote
|
|||
{
|
||||
return m_keys;
|
||||
}
|
||||
|
||||
void account_base::set_keys(const account_keys& keys) {
|
||||
m_keys = keys;
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
109
src/cryptonote_core/cryptonote_core.cpp
Normal file → Executable 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue