Minimum fix increase, high level api
This commit is contained in:
parent
69a770823e
commit
76bb193b05
82 changed files with 5425 additions and 726 deletions
|
@ -7,7 +7,7 @@ set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
|||
set(CMAKE_CONFIGURATION_TYPES "Debug;Release")
|
||||
enable_testing()
|
||||
|
||||
include_directories(src contrib/epee/include external "${CMAKE_BINARY_DIR}/version")
|
||||
include_directories(include src contrib/epee/include external "${CMAKE_BINARY_DIR}/version")
|
||||
if(APPLE)
|
||||
include_directories(SYSTEM /usr/include/malloc)
|
||||
endif()
|
||||
|
@ -15,7 +15,7 @@ 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 /FIinline_c.h /D__SSE4_1__")
|
||||
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")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:10485760")
|
||||
if(STATIC)
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
Release notes 0.8.11
|
||||
|
||||
- Increased minimum transaction fee
|
||||
- Transaction pool optimizations
|
||||
- High level API implementation
|
||||
- CryptoNight hash function optimization
|
||||
- Improvements for wallet JSON RPC API
|
||||
|
||||
Release notes 0.8.10
|
||||
|
||||
- Optimized blockchain storage memory usage
|
||||
|
|
|
@ -5,33 +5,54 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <system_error>
|
||||
#include <vector>
|
||||
|
||||
#include "crypto/crypto.h"
|
||||
#include "cryptonote_core/cryptonote_basic.h"
|
||||
#include "cryptonote_protocol/cryptonote_protocol_defs.h"
|
||||
#include "rpc/core_rpc_server_commands_defs.h"
|
||||
|
||||
namespace CryptoNote {
|
||||
|
||||
class INodeObserver {
|
||||
public:
|
||||
virtual void initCompleted(std::error_code result) {}
|
||||
|
||||
virtual ~INodeObserver() {}
|
||||
virtual void peerCountUpdated(size_t count) {}
|
||||
virtual void lastLocalBlockHeightUpdated(uint64_t height) {}
|
||||
virtual void localBlockchainUpdated(uint64_t height) {}
|
||||
virtual void lastKnownBlockHeightUpdated(uint64_t height) {}
|
||||
};
|
||||
|
||||
virtual void blockchainReorganized(uint64_t height) {}
|
||||
struct OutEntry {
|
||||
uint64_t outGlobalIndex;
|
||||
crypto::public_key outKey;
|
||||
};
|
||||
|
||||
struct OutsForAmount {
|
||||
uint64_t amount;
|
||||
std::vector<OutEntry> outs;
|
||||
};
|
||||
|
||||
class INode {
|
||||
public:
|
||||
virtual ~INode() = 0;
|
||||
virtual void addObserver(INodeObserver* observer) = 0;
|
||||
virtual void removeObserver(INodeObserver* observer) = 0;
|
||||
typedef std::function<void(std::error_code)> Callback;
|
||||
|
||||
virtual void init() = 0;
|
||||
virtual void shutdown() = 0;
|
||||
virtual ~INode() {}
|
||||
virtual bool addObserver(INodeObserver* observer) = 0;
|
||||
virtual bool removeObserver(INodeObserver* observer) = 0;
|
||||
|
||||
virtual size_t getPeerCount() = 0;
|
||||
virtual uint64_t getLastLocalBlockHeight() = 0;
|
||||
virtual uint64_t getLastKnownBlockHeight() = 0;
|
||||
virtual void init(const Callback& callback) = 0;
|
||||
virtual bool shutdown() = 0;
|
||||
|
||||
virtual size_t getPeerCount() const = 0;
|
||||
virtual uint64_t getLastLocalBlockHeight() const = 0;
|
||||
virtual uint64_t getLastKnownBlockHeight() 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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace CryptoNote {
|
|||
|
||||
typedef size_t TransactionId;
|
||||
typedef size_t TransferId;
|
||||
typedef std::array<uint8_t, 32> TransacitonHash;
|
||||
typedef std::array<uint8_t, 32> TransactionHash;
|
||||
|
||||
struct Transfer {
|
||||
std::string address;
|
||||
|
@ -33,7 +33,7 @@ struct Transaction {
|
|||
size_t transferCount;
|
||||
int64_t totalAmount;
|
||||
uint64_t fee;
|
||||
TransacitonHash hash;
|
||||
TransactionHash hash;
|
||||
bool isCoinbase;
|
||||
uint64_t blockHeight;
|
||||
uint64_t timestamp;
|
||||
|
@ -44,7 +44,7 @@ class IWalletObserver {
|
|||
public:
|
||||
virtual void initCompleted(std::error_code result) {}
|
||||
virtual void saveCompleted(std::error_code result) {}
|
||||
virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total) {}
|
||||
virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total, std::error_code result) {}
|
||||
virtual void actualBalanceUpdated(uint64_t actualBalance) {}
|
||||
virtual void pendingBalanceUpdated(uint64_t pendingBalance) {}
|
||||
virtual void externalTransactionCreated(TransactionId transactionId) {}
|
||||
|
@ -54,7 +54,7 @@ public:
|
|||
|
||||
class IWallet {
|
||||
public:
|
||||
virtual ~IWallet() = 0;
|
||||
virtual ~IWallet() {} ;
|
||||
virtual void addObserver(IWalletObserver* observer) = 0;
|
||||
virtual void removeObserver(IWalletObserver* observer) = 0;
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ file(GLOB_RECURSE SIMPLEWALLET simplewallet/*)
|
|||
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/*)
|
||||
|
||||
source_group(common FILES ${COMMON})
|
||||
source_group(crypto FILES ${CRYPTO})
|
||||
|
@ -23,6 +24,7 @@ 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})
|
||||
|
||||
add_library(common ${COMMON})
|
||||
add_library(crypto ${CRYPTO})
|
||||
|
@ -37,10 +39,12 @@ add_library(rpc ${RPC})
|
|||
add_library(wallet ${WALLET})
|
||||
add_executable(simplewallet ${SIMPLEWALLET} )
|
||||
target_link_libraries(simplewallet wallet rpc cryptonote_core crypto common upnpc-static ${Boost_LIBRARIES})
|
||||
add_library(node_rpc_proxy ${NODE_RPC_PROXY})
|
||||
|
||||
add_dependencies(daemon version)
|
||||
add_dependencies(rpc version)
|
||||
add_dependencies(simplewallet version)
|
||||
|
||||
set_property(TARGET common crypto cryptonote_core rpc wallet PROPERTY FOLDER "libs")
|
||||
set_property(TARGET common crypto cryptonote_core rpc wallet node_rpc_proxy PROPERTY FOLDER "libs")
|
||||
set_property(TARGET daemon simplewallet connectivity_tool simpleminer PROPERTY FOLDER "prog")
|
||||
set_property(TARGET daemon PROPERTY OUTPUT_NAME "bytecoind")
|
||||
|
|
116
src/common/ObserverManager.h
Normal file
116
src/common/ObserverManager.h
Normal file
|
@ -0,0 +1,116 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace tools {
|
||||
|
||||
template<typename T>
|
||||
class ObserverManager {
|
||||
public:
|
||||
bool add(T* observer) {
|
||||
std::unique_lock<std::mutex> lock(m_observersMutex);
|
||||
auto it = std::find(m_observers.begin(), m_observers.end(), observer);
|
||||
if (m_observers.end() == it) {
|
||||
m_observers.push_back(observer);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool remove(T* observer) {
|
||||
std::unique_lock<std::mutex> lock(m_observersMutex);
|
||||
auto it = std::find(m_observers.begin(), m_observers.end(), observer);
|
||||
if (m_observers.end() == it) {
|
||||
return false;
|
||||
} else {
|
||||
m_observers.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
std::unique_lock<std::mutex> lock(m_observersMutex);
|
||||
m_observers.clear();
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
template<typename F>
|
||||
void notify(F notification) {
|
||||
std::vector<T*> observersCopy;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_observersMutex);
|
||||
observersCopy = m_observers;
|
||||
}
|
||||
|
||||
for (T* observer : observersCopy) {
|
||||
(observer->*notification)();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F, typename Arg0>
|
||||
void notify(F notification, const Arg0& arg0) {
|
||||
std::vector<T*> observersCopy;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_observersMutex);
|
||||
observersCopy = m_observers;
|
||||
}
|
||||
|
||||
for (T* observer : observersCopy) {
|
||||
(observer->*notification)(arg0);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F, typename Arg0, typename Arg1>
|
||||
void notify(F notification, const Arg0& arg0, const Arg1& arg1) {
|
||||
std::vector<T*> observersCopy;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_observersMutex);
|
||||
observersCopy = m_observers;
|
||||
}
|
||||
|
||||
for (T* observer : observersCopy) {
|
||||
(observer->*notification)(arg0, arg1);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F, typename Arg0, typename Arg1, typename Arg2>
|
||||
void notify(F notification, const Arg0& arg0, const Arg1& arg1, const Arg2& arg2) {
|
||||
std::vector<T*> observersCopy;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_observersMutex);
|
||||
observersCopy = m_observers;
|
||||
}
|
||||
|
||||
for (T* observer : observersCopy) {
|
||||
(observer->*notification)(arg0, arg1, arg2);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
template<typename F, typename... Args>
|
||||
void notify(F notification, Args... args) {
|
||||
std::vector<T*> observersCopy;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_observersMutex);
|
||||
observersCopy = m_observers;
|
||||
}
|
||||
|
||||
for (T* observer : observersCopy) {
|
||||
(observer->*notification)(args...);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
std::vector<T*> m_observers;
|
||||
std::mutex m_observersMutex;
|
||||
};
|
||||
|
||||
}
|
|
@ -110,7 +110,7 @@ namespace tools
|
|||
|
||||
void encode_block(const char* block, size_t size, char* res)
|
||||
{
|
||||
assert(1 <= size && size <= sizeof(full_block_size));
|
||||
assert(1 <= size && size <= full_block_size);
|
||||
|
||||
uint64_t num = uint_8be_to_64(reinterpret_cast<const uint8_t*>(block), size);
|
||||
int i = static_cast<int>(encoded_block_sizes[size]) - 1;
|
||||
|
|
|
@ -44,12 +44,12 @@ namespace crypto {
|
|||
chacha8(data, length, reinterpret_cast<const uint8_t*>(&key), reinterpret_cast<const uint8_t*>(&iv), cipher);
|
||||
}
|
||||
|
||||
inline void generate_chacha8_key(std::string password, chacha8_key& key) {
|
||||
inline void generate_chacha8_key(crypto::cn_context &context, std::string password, chacha8_key& key) {
|
||||
static_assert(sizeof(chacha8_key) <= sizeof(hash), "Size of hash must be at least that of chacha8_key");
|
||||
char pwd_hash[HASH_SIZE];
|
||||
crypto::cn_slow_hash(password.data(), password.size(), pwd_hash);
|
||||
memcpy(&key, pwd_hash, sizeof(key));
|
||||
memset(pwd_hash, 0, sizeof(pwd_hash));
|
||||
crypto::hash pwd_hash;
|
||||
crypto::cn_slow_hash(context, password.data(), password.size(), pwd_hash);
|
||||
memcpy(&key, &pwd_hash, sizeof(key));
|
||||
memset(&pwd_hash, 0, sizeof(pwd_hash));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,11 +49,13 @@ void hash_process(union hash_state *state, const uint8_t *buf, size_t count);
|
|||
|
||||
enum {
|
||||
HASH_SIZE = 32,
|
||||
HASH_DATA_AREA = 136
|
||||
HASH_DATA_AREA = 136,
|
||||
SLOW_HASH_CONTEXT_SIZE = 2097552
|
||||
};
|
||||
|
||||
void cn_fast_hash(const void *data, size_t length, char *hash);
|
||||
void cn_slow_hash(const void *data, size_t length, char *hash);
|
||||
|
||||
void cn_slow_hash_f(void *, const void *, size_t, void *);
|
||||
|
||||
void hash_extra_blake(const void *data, size_t length, char *hash);
|
||||
void hash_extra_groestl(const void *data, size_t length, char *hash);
|
||||
|
|
|
@ -37,8 +37,24 @@ namespace crypto {
|
|||
return h;
|
||||
}
|
||||
|
||||
inline void cn_slow_hash(const void *data, std::size_t length, hash &hash) {
|
||||
cn_slow_hash(data, length, reinterpret_cast<char *>(&hash));
|
||||
class cn_context {
|
||||
public:
|
||||
|
||||
cn_context();
|
||||
~cn_context();
|
||||
#if !defined(_MSC_VER) || _MSC_VER >= 1800
|
||||
cn_context(const cn_context &) = delete;
|
||||
void operator=(const cn_context &) = delete;
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
void *data;
|
||||
friend inline void cn_slow_hash(cn_context &, const void *, std::size_t, hash &);
|
||||
};
|
||||
|
||||
inline void cn_slow_hash(cn_context &context, const void *data, std::size_t length, hash &hash) {
|
||||
(*cn_slow_hash_f)(context.data, data, length, reinterpret_cast<void *>(&hash));
|
||||
}
|
||||
|
||||
inline void tree_hash(const hash *hashes, std::size_t count, hash &root_hash) {
|
||||
|
|
|
@ -2,257 +2,179 @@
|
|||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <emmintrin.h>
|
||||
#include <wmmintrin.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
#else
|
||||
#include <cpuid.h>
|
||||
#endif
|
||||
|
||||
#include "aesb.h"
|
||||
#include "initializer.h"
|
||||
#include "common/int-util.h"
|
||||
#include "hash-ops.h"
|
||||
#include "oaes_lib.h"
|
||||
|
||||
#include <emmintrin.h>
|
||||
void (*cn_slow_hash_fp)(void *, const void *, size_t, void *);
|
||||
|
||||
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
|
||||
#include <intrin.h>
|
||||
#define STATIC
|
||||
#define INLINE __inline
|
||||
#if !defined(RDATA_ALIGN16)
|
||||
#define RDATA_ALIGN16 __declspec(align(16))
|
||||
#endif
|
||||
void cn_slow_hash_f(void * a, const void * b, size_t c, void * d){
|
||||
(*cn_slow_hash_fp)(a, b, c, d);
|
||||
}
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define likely(x) (__builtin_expect(!!(x), 1))
|
||||
#define unlikely(x) (__builtin_expect(!!(x), 0))
|
||||
#else
|
||||
#include <wmmintrin.h>
|
||||
#define STATIC static
|
||||
#define INLINE inline
|
||||
#if !defined(RDATA_ALIGN16)
|
||||
#define RDATA_ALIGN16 __attribute__ ((aligned(16)))
|
||||
#endif
|
||||
#define likely(x) (x)
|
||||
#define unlikely(x) (x)
|
||||
#define __attribute__(x)
|
||||
#endif
|
||||
|
||||
#define MEMORY (1 << 21) // 2MB scratchpad
|
||||
#if defined(_MSC_VER)
|
||||
#define restrict
|
||||
#endif
|
||||
|
||||
#define MEMORY (1 << 21) /* 2 MiB */
|
||||
#define ITER (1 << 20)
|
||||
#define AES_BLOCK_SIZE 16
|
||||
#define AES_KEY_SIZE 32
|
||||
#define AES_KEY_SIZE 32 /*16*/
|
||||
#define INIT_SIZE_BLK 8
|
||||
#define INIT_SIZE_BYTE (INIT_SIZE_BLK * AES_BLOCK_SIZE)
|
||||
|
||||
#define U64(x) ((uint64_t *) (x))
|
||||
#define R128(x) ((__m128i *) (x))
|
||||
#define INIT_SIZE_BYTE (INIT_SIZE_BLK * AES_BLOCK_SIZE) // 128
|
||||
|
||||
#pragma pack(push, 1)
|
||||
union cn_slow_hash_state
|
||||
{
|
||||
union cn_slow_hash_state {
|
||||
union hash_state hs;
|
||||
struct
|
||||
{
|
||||
struct {
|
||||
uint8_t k[64];
|
||||
uint8_t init[INIT_SIZE_BYTE];
|
||||
};
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
|
||||
#define cpuid(info,x) __cpuidex(info,x,0)
|
||||
#else
|
||||
void cpuid(int CPUInfo[4], int InfoType)
|
||||
{
|
||||
__asm__ __volatile__
|
||||
(
|
||||
"cpuid":
|
||||
"=a" (CPUInfo[0]),
|
||||
"=b" (CPUInfo[1]),
|
||||
"=c" (CPUInfo[2]),
|
||||
"=d" (CPUInfo[3]) :
|
||||
"a" (InfoType), "c" (0)
|
||||
);
|
||||
}
|
||||
#if defined(_MSC_VER)
|
||||
#define ALIGNED_DATA(x) __declspec(align(x))
|
||||
#define ALIGNED_DECL(t, x) ALIGNED_DATA(x) t
|
||||
#elif defined(__GNUC__)
|
||||
#define ALIGNED_DATA(x) __attribute__((aligned(x)))
|
||||
#define ALIGNED_DECL(t, x) t ALIGNED_DATA(x)
|
||||
#endif
|
||||
|
||||
STATIC INLINE void mul(const uint8_t *a, const uint8_t *b, uint8_t *res)
|
||||
{
|
||||
uint64_t a0, b0;
|
||||
uint64_t hi, lo;
|
||||
|
||||
a0 = U64(a)[0];
|
||||
b0 = U64(b)[0];
|
||||
lo = mul128(a0, b0, &hi);
|
||||
U64(res)[0] = hi;
|
||||
U64(res)[1] = lo;
|
||||
}
|
||||
|
||||
STATIC INLINE void sum_half_blocks(uint8_t *a, const uint8_t *b)
|
||||
{
|
||||
uint64_t a0, a1, b0, b1;
|
||||
a0 = U64(a)[0];
|
||||
a1 = U64(a)[1];
|
||||
b0 = U64(b)[0];
|
||||
b1 = U64(b)[1];
|
||||
a0 += b0;
|
||||
a1 += b1;
|
||||
U64(a)[0] = a0;
|
||||
U64(a)[1] = a1;
|
||||
}
|
||||
|
||||
STATIC INLINE void swap_blocks(uint8_t *a, uint8_t *b)
|
||||
{
|
||||
uint64_t t[2];
|
||||
U64(t)[0] = U64(a)[0];
|
||||
U64(t)[1] = U64(a)[1];
|
||||
U64(a)[0] = U64(b)[0];
|
||||
U64(a)[1] = U64(b)[1];
|
||||
U64(b)[0] = U64(t)[0];
|
||||
U64(b)[1] = U64(t)[1];
|
||||
}
|
||||
|
||||
STATIC INLINE void xor_blocks(uint8_t *a, const uint8_t *b)
|
||||
{
|
||||
U64(a)[0] ^= U64(b)[0];
|
||||
U64(a)[1] ^= U64(b)[1];
|
||||
}
|
||||
|
||||
STATIC INLINE int check_aes_hw(void)
|
||||
{
|
||||
int cpuid_results[4];
|
||||
static int supported = -1;
|
||||
|
||||
if(supported >= 0)
|
||||
return supported;
|
||||
|
||||
cpuid(cpuid_results,1);
|
||||
return supported = cpuid_results[2] & (1 << 25);
|
||||
}
|
||||
|
||||
STATIC INLINE void aesni_pseudo_round(const uint8_t *in, uint8_t *out,
|
||||
const uint8_t *expandedKey)
|
||||
{
|
||||
__m128i *k = R128(expandedKey);
|
||||
__m128i d;
|
||||
|
||||
d = _mm_loadu_si128(R128(in));
|
||||
d = _mm_aesenc_si128(d, *R128(&k[0]));
|
||||
d = _mm_aesenc_si128(d, *R128(&k[1]));
|
||||
d = _mm_aesenc_si128(d, *R128(&k[2]));
|
||||
d = _mm_aesenc_si128(d, *R128(&k[3]));
|
||||
d = _mm_aesenc_si128(d, *R128(&k[4]));
|
||||
d = _mm_aesenc_si128(d, *R128(&k[5]));
|
||||
d = _mm_aesenc_si128(d, *R128(&k[6]));
|
||||
d = _mm_aesenc_si128(d, *R128(&k[7]));
|
||||
d = _mm_aesenc_si128(d, *R128(&k[8]));
|
||||
d = _mm_aesenc_si128(d, *R128(&k[9]));
|
||||
_mm_storeu_si128((R128(out)), d);
|
||||
}
|
||||
|
||||
void cn_slow_hash(const void *data, size_t length, char *hash)
|
||||
{
|
||||
uint8_t long_state[MEMORY];
|
||||
uint8_t text[INIT_SIZE_BYTE];
|
||||
uint8_t a[AES_BLOCK_SIZE];
|
||||
uint8_t b[AES_BLOCK_SIZE];
|
||||
uint8_t d[AES_BLOCK_SIZE];
|
||||
RDATA_ALIGN16 uint8_t expandedKey[256];
|
||||
|
||||
union cn_slow_hash_state state;
|
||||
|
||||
size_t i, j;
|
||||
uint8_t *p = NULL;
|
||||
struct cn_ctx {
|
||||
ALIGNED_DECL(uint8_t long_state[MEMORY], 16);
|
||||
ALIGNED_DECL(union cn_slow_hash_state state, 16);
|
||||
ALIGNED_DECL(uint8_t text[INIT_SIZE_BYTE], 16);
|
||||
ALIGNED_DECL(uint64_t a[AES_BLOCK_SIZE >> 3], 16);
|
||||
ALIGNED_DECL(uint64_t b[AES_BLOCK_SIZE >> 3], 16);
|
||||
ALIGNED_DECL(uint8_t c[AES_BLOCK_SIZE], 16);
|
||||
oaes_ctx* aes_ctx;
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct cn_ctx) == SLOW_HASH_CONTEXT_SIZE, "Invalid structure size");
|
||||
|
||||
static inline void ExpandAESKey256_sub1(__m128i *tmp1, __m128i *tmp2)
|
||||
{
|
||||
__m128i tmp4;
|
||||
*tmp2 = _mm_shuffle_epi32(*tmp2, 0xFF);
|
||||
tmp4 = _mm_slli_si128(*tmp1, 0x04);
|
||||
*tmp1 = _mm_xor_si128(*tmp1, tmp4);
|
||||
tmp4 = _mm_slli_si128(tmp4, 0x04);
|
||||
*tmp1 = _mm_xor_si128(*tmp1, tmp4);
|
||||
tmp4 = _mm_slli_si128(tmp4, 0x04);
|
||||
*tmp1 = _mm_xor_si128(*tmp1, tmp4);
|
||||
*tmp1 = _mm_xor_si128(*tmp1, *tmp2);
|
||||
}
|
||||
|
||||
static inline void ExpandAESKey256_sub2(__m128i *tmp1, __m128i *tmp3)
|
||||
{
|
||||
__m128i tmp2, tmp4;
|
||||
|
||||
tmp4 = _mm_aeskeygenassist_si128(*tmp1, 0x00);
|
||||
tmp2 = _mm_shuffle_epi32(tmp4, 0xAA);
|
||||
tmp4 = _mm_slli_si128(*tmp3, 0x04);
|
||||
*tmp3 = _mm_xor_si128(*tmp3, tmp4);
|
||||
tmp4 = _mm_slli_si128(tmp4, 0x04);
|
||||
*tmp3 = _mm_xor_si128(*tmp3, tmp4);
|
||||
tmp4 = _mm_slli_si128(tmp4, 0x04);
|
||||
*tmp3 = _mm_xor_si128(*tmp3, tmp4);
|
||||
*tmp3 = _mm_xor_si128(*tmp3, tmp2);
|
||||
}
|
||||
|
||||
// Special thanks to Intel for helping me
|
||||
// with ExpandAESKey256() and its subroutines
|
||||
static inline void ExpandAESKey256(uint8_t *keybuf)
|
||||
{
|
||||
__m128i tmp1, tmp2, tmp3, *keys;
|
||||
|
||||
keys = (__m128i *)keybuf;
|
||||
|
||||
tmp1 = _mm_load_si128((__m128i *)keybuf);
|
||||
tmp3 = _mm_load_si128((__m128i *)(keybuf+0x10));
|
||||
|
||||
tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x01);
|
||||
ExpandAESKey256_sub1(&tmp1, &tmp2);
|
||||
keys[2] = tmp1;
|
||||
ExpandAESKey256_sub2(&tmp1, &tmp3);
|
||||
keys[3] = tmp3;
|
||||
|
||||
tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x02);
|
||||
ExpandAESKey256_sub1(&tmp1, &tmp2);
|
||||
keys[4] = tmp1;
|
||||
ExpandAESKey256_sub2(&tmp1, &tmp3);
|
||||
keys[5] = tmp3;
|
||||
|
||||
tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x04);
|
||||
ExpandAESKey256_sub1(&tmp1, &tmp2);
|
||||
keys[6] = tmp1;
|
||||
ExpandAESKey256_sub2(&tmp1, &tmp3);
|
||||
keys[7] = tmp3;
|
||||
|
||||
tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x08);
|
||||
ExpandAESKey256_sub1(&tmp1, &tmp2);
|
||||
keys[8] = tmp1;
|
||||
ExpandAESKey256_sub2(&tmp1, &tmp3);
|
||||
keys[9] = tmp3;
|
||||
|
||||
tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x10);
|
||||
ExpandAESKey256_sub1(&tmp1, &tmp2);
|
||||
keys[10] = tmp1;
|
||||
ExpandAESKey256_sub2(&tmp1, &tmp3);
|
||||
keys[11] = tmp3;
|
||||
|
||||
tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x20);
|
||||
ExpandAESKey256_sub1(&tmp1, &tmp2);
|
||||
keys[12] = tmp1;
|
||||
ExpandAESKey256_sub2(&tmp1, &tmp3);
|
||||
keys[13] = tmp3;
|
||||
|
||||
tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x40);
|
||||
ExpandAESKey256_sub1(&tmp1, &tmp2);
|
||||
keys[14] = tmp1;
|
||||
}
|
||||
|
||||
int useAes = check_aes_hw();
|
||||
static void (*const extra_hashes[4])(const void *, size_t, char *) =
|
||||
{
|
||||
hash_extra_blake, hash_extra_groestl, hash_extra_jh, hash_extra_skein
|
||||
};
|
||||
|
||||
hash_process(&state.hs, data, length);
|
||||
memcpy(text, state.init, INIT_SIZE_BYTE);
|
||||
#include "slow-hash.inl"
|
||||
#define AESNI
|
||||
#include "slow-hash.inl"
|
||||
|
||||
aes_ctx = (oaes_ctx *) oaes_alloc();
|
||||
oaes_key_import_data(aes_ctx, state.hs.b, AES_KEY_SIZE);
|
||||
|
||||
// use aligned data
|
||||
memcpy(expandedKey, aes_ctx->key->exp_data, aes_ctx->key->exp_data_len);
|
||||
|
||||
if(useAes)
|
||||
{
|
||||
for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++)
|
||||
{
|
||||
for(j = 0; j < INIT_SIZE_BLK; j++)
|
||||
aesni_pseudo_round(&text[AES_BLOCK_SIZE * j], &text[AES_BLOCK_SIZE * j], expandedKey);
|
||||
memcpy(&long_state[i * INIT_SIZE_BYTE], text, INIT_SIZE_BYTE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++)
|
||||
{
|
||||
for(j = 0; j < INIT_SIZE_BLK; j++)
|
||||
aesb_pseudo_round(&text[AES_BLOCK_SIZE * j], &text[AES_BLOCK_SIZE * j], expandedKey);
|
||||
|
||||
memcpy(&long_state[i * INIT_SIZE_BYTE], text, INIT_SIZE_BYTE);
|
||||
}
|
||||
}
|
||||
|
||||
U64(a)[0] = U64(&state.k[0])[0] ^ U64(&state.k[32])[0];
|
||||
U64(a)[1] = U64(&state.k[0])[1] ^ U64(&state.k[32])[1];
|
||||
U64(b)[0] = U64(&state.k[16])[0] ^ U64(&state.k[48])[0];
|
||||
U64(b)[1] = U64(&state.k[16])[1] ^ U64(&state.k[48])[1];
|
||||
|
||||
for(i = 0; i < ITER / 2; i++)
|
||||
{
|
||||
#define TOTALBLOCKS (MEMORY / AES_BLOCK_SIZE)
|
||||
#define state_index(x) (((*((uint64_t *)x) >> 4) & (TOTALBLOCKS - 1)) << 4)
|
||||
|
||||
// Iteration 1
|
||||
p = &long_state[state_index(a)];
|
||||
|
||||
if(useAes)
|
||||
_mm_storeu_si128(R128(p), _mm_aesenc_si128(_mm_loadu_si128(R128(p)), _mm_loadu_si128(R128(a))));
|
||||
else
|
||||
aesb_single_round(p, p, a);
|
||||
|
||||
xor_blocks(b, p);
|
||||
swap_blocks(b, p);
|
||||
swap_blocks(a, b);
|
||||
|
||||
// Iteration 2
|
||||
p = &long_state[state_index(a)];
|
||||
|
||||
mul(a, p, d);
|
||||
sum_half_blocks(b, d);
|
||||
swap_blocks(b, p);
|
||||
xor_blocks(b, p);
|
||||
swap_blocks(a, b);
|
||||
}
|
||||
|
||||
memcpy(text, state.init, INIT_SIZE_BYTE);
|
||||
oaes_key_import_data(aes_ctx, &state.hs.b[32], AES_KEY_SIZE);
|
||||
memcpy(expandedKey, aes_ctx->key->exp_data, aes_ctx->key->exp_data_len);
|
||||
if(useAes)
|
||||
{
|
||||
for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++)
|
||||
{
|
||||
for(j = 0; j < INIT_SIZE_BLK; j++)
|
||||
{
|
||||
xor_blocks(&text[j * AES_BLOCK_SIZE], &long_state[i * INIT_SIZE_BYTE + j * AES_BLOCK_SIZE]);
|
||||
aesni_pseudo_round(&text[j * AES_BLOCK_SIZE], &text[j * AES_BLOCK_SIZE], expandedKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++)
|
||||
{
|
||||
for(j = 0; j < INIT_SIZE_BLK; j++)
|
||||
{
|
||||
xor_blocks(&text[j * AES_BLOCK_SIZE], &long_state[i * INIT_SIZE_BYTE + j * AES_BLOCK_SIZE]);
|
||||
aesb_pseudo_round(&text[AES_BLOCK_SIZE * j], &text[AES_BLOCK_SIZE * j], expandedKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
oaes_free((OAES_CTX **) &aes_ctx);
|
||||
memcpy(state.init, text, INIT_SIZE_BYTE);
|
||||
hash_permutation(&state.hs);
|
||||
extra_hashes[state.hs.b[0] & 3](&state, 200, hash);
|
||||
INITIALIZER(detect_aes) {
|
||||
int ecx;
|
||||
#if defined(_MSC_VER)
|
||||
int cpuinfo[4];
|
||||
__cpuid(cpuinfo, 1);
|
||||
ecx = cpuinfo[2];
|
||||
#else
|
||||
int a, b, d;
|
||||
__cpuid(1, a, b, ecx, d);
|
||||
#endif
|
||||
cn_slow_hash_fp = (ecx & (1 << 25)) ? &cn_slow_hash_aesni : &cn_slow_hash_noaesni;
|
||||
}
|
||||
|
|
60
src/crypto/slow-hash.cpp
Normal file
60
src/crypto/slow-hash.cpp
Normal file
|
@ -0,0 +1,60 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <new>
|
||||
|
||||
#include "hash.h"
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
using std::bad_alloc;
|
||||
|
||||
namespace crypto {
|
||||
|
||||
enum {
|
||||
MAP_SIZE = SLOW_HASH_CONTEXT_SIZE + ((-SLOW_HASH_CONTEXT_SIZE) & 0xfff)
|
||||
};
|
||||
|
||||
#if defined(WIN32)
|
||||
|
||||
cn_context::cn_context() {
|
||||
data = VirtualAlloc(nullptr, MAP_SIZE, MEM_COMMIT, PAGE_READWRITE);
|
||||
if (data == nullptr) {
|
||||
throw bad_alloc();
|
||||
}
|
||||
}
|
||||
|
||||
cn_context::~cn_context() {
|
||||
if (!VirtualFree(data, 0, MEM_RELEASE)) {
|
||||
throw bad_alloc();
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
cn_context::cn_context() {
|
||||
#if !defined(__APPLE__)
|
||||
data = mmap(nullptr, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
|
||||
#else
|
||||
data = mmap(nullptr, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||
#endif
|
||||
if (data == MAP_FAILED) {
|
||||
throw bad_alloc();
|
||||
}
|
||||
mlock(data, MAP_SIZE);
|
||||
}
|
||||
|
||||
cn_context::~cn_context() {
|
||||
if (munmap(data, MAP_SIZE) != 0) {
|
||||
throw bad_alloc();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
184
src/crypto/slow-hash.inl
Normal file
184
src/crypto/slow-hash.inl
Normal file
|
@ -0,0 +1,184 @@
|
|||
static void
|
||||
#if defined(AESNI)
|
||||
cn_slow_hash_aesni
|
||||
#else
|
||||
cn_slow_hash_noaesni
|
||||
#endif
|
||||
(void *restrict context, const void *restrict data, size_t length, void *restrict hash)
|
||||
{
|
||||
#define ctx ((struct cn_ctx *) context)
|
||||
uint8_t ExpandedKey[256];
|
||||
size_t i, j;
|
||||
__m128i *longoutput, *expkey, *xmminput, b_x;
|
||||
ALIGNED_DECL(uint64_t a[2], 16);
|
||||
hash_process(&ctx->state.hs, (const uint8_t*) data, length);
|
||||
|
||||
memcpy(ctx->text, ctx->state.init, INIT_SIZE_BYTE);
|
||||
#if defined(AESNI)
|
||||
memcpy(ExpandedKey, ctx->state.hs.b, AES_KEY_SIZE);
|
||||
ExpandAESKey256(ExpandedKey);
|
||||
#else
|
||||
ctx->aes_ctx = oaes_alloc();
|
||||
oaes_key_import_data(ctx->aes_ctx, ctx->state.hs.b, AES_KEY_SIZE);
|
||||
memcpy(ExpandedKey, ctx->aes_ctx->key->exp_data, ctx->aes_ctx->key->exp_data_len);
|
||||
#endif
|
||||
|
||||
longoutput = (__m128i *) ctx->long_state;
|
||||
expkey = (__m128i *) ExpandedKey;
|
||||
xmminput = (__m128i *) ctx->text;
|
||||
|
||||
//for (i = 0; likely(i < MEMORY); i += INIT_SIZE_BYTE)
|
||||
// aesni_parallel_noxor(&ctx->long_state[i], ctx->text, ExpandedKey);
|
||||
|
||||
for (i = 0; likely(i < MEMORY); i += INIT_SIZE_BYTE)
|
||||
{
|
||||
#if defined(AESNI)
|
||||
for(j = 0; j < 10; j++)
|
||||
{
|
||||
xmminput[0] = _mm_aesenc_si128(xmminput[0], expkey[j]);
|
||||
xmminput[1] = _mm_aesenc_si128(xmminput[1], expkey[j]);
|
||||
xmminput[2] = _mm_aesenc_si128(xmminput[2], expkey[j]);
|
||||
xmminput[3] = _mm_aesenc_si128(xmminput[3], expkey[j]);
|
||||
xmminput[4] = _mm_aesenc_si128(xmminput[4], expkey[j]);
|
||||
xmminput[5] = _mm_aesenc_si128(xmminput[5], expkey[j]);
|
||||
xmminput[6] = _mm_aesenc_si128(xmminput[6], expkey[j]);
|
||||
xmminput[7] = _mm_aesenc_si128(xmminput[7], expkey[j]);
|
||||
}
|
||||
#else
|
||||
aesb_pseudo_round((uint8_t *) &xmminput[0], (uint8_t *) &xmminput[0], (uint8_t *) expkey);
|
||||
aesb_pseudo_round((uint8_t *) &xmminput[1], (uint8_t *) &xmminput[1], (uint8_t *) expkey);
|
||||
aesb_pseudo_round((uint8_t *) &xmminput[2], (uint8_t *) &xmminput[2], (uint8_t *) expkey);
|
||||
aesb_pseudo_round((uint8_t *) &xmminput[3], (uint8_t *) &xmminput[3], (uint8_t *) expkey);
|
||||
aesb_pseudo_round((uint8_t *) &xmminput[4], (uint8_t *) &xmminput[4], (uint8_t *) expkey);
|
||||
aesb_pseudo_round((uint8_t *) &xmminput[5], (uint8_t *) &xmminput[5], (uint8_t *) expkey);
|
||||
aesb_pseudo_round((uint8_t *) &xmminput[6], (uint8_t *) &xmminput[6], (uint8_t *) expkey);
|
||||
aesb_pseudo_round((uint8_t *) &xmminput[7], (uint8_t *) &xmminput[7], (uint8_t *) expkey);
|
||||
#endif
|
||||
_mm_store_si128(&(longoutput[(i >> 4)]), xmminput[0]);
|
||||
_mm_store_si128(&(longoutput[(i >> 4) + 1]), xmminput[1]);
|
||||
_mm_store_si128(&(longoutput[(i >> 4) + 2]), xmminput[2]);
|
||||
_mm_store_si128(&(longoutput[(i >> 4) + 3]), xmminput[3]);
|
||||
_mm_store_si128(&(longoutput[(i >> 4) + 4]), xmminput[4]);
|
||||
_mm_store_si128(&(longoutput[(i >> 4) + 5]), xmminput[5]);
|
||||
_mm_store_si128(&(longoutput[(i >> 4) + 6]), xmminput[6]);
|
||||
_mm_store_si128(&(longoutput[(i >> 4) + 7]), xmminput[7]);
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
ctx->a[i] = ((uint64_t *)ctx->state.k)[i] ^ ((uint64_t *)ctx->state.k)[i+4];
|
||||
ctx->b[i] = ((uint64_t *)ctx->state.k)[i+2] ^ ((uint64_t *)ctx->state.k)[i+6];
|
||||
}
|
||||
|
||||
b_x = _mm_load_si128((__m128i *)ctx->b);
|
||||
a[0] = ctx->a[0];
|
||||
a[1] = ctx->a[1];
|
||||
|
||||
for(i = 0; likely(i < 0x80000); i++)
|
||||
{
|
||||
__m128i c_x = _mm_load_si128((__m128i *)&ctx->long_state[a[0] & 0x1FFFF0]);
|
||||
__m128i a_x = _mm_load_si128((__m128i *)a);
|
||||
ALIGNED_DECL(uint64_t c[2], 16);
|
||||
ALIGNED_DECL(uint64_t b[2], 16);
|
||||
uint64_t *nextblock, *dst;
|
||||
|
||||
#if defined(AESNI)
|
||||
c_x = _mm_aesenc_si128(c_x, a_x);
|
||||
#else
|
||||
aesb_single_round((uint8_t *) &c_x, (uint8_t *) &c_x, (uint8_t *) &a_x);
|
||||
#endif
|
||||
|
||||
_mm_store_si128((__m128i *)c, c_x);
|
||||
//__builtin_prefetch(&ctx->long_state[c[0] & 0x1FFFF0], 0, 1);
|
||||
|
||||
b_x = _mm_xor_si128(b_x, c_x);
|
||||
_mm_store_si128((__m128i *)&ctx->long_state[a[0] & 0x1FFFF0], b_x);
|
||||
|
||||
nextblock = (uint64_t *)&ctx->long_state[c[0] & 0x1FFFF0];
|
||||
b[0] = nextblock[0];
|
||||
b[1] = nextblock[1];
|
||||
|
||||
{
|
||||
uint64_t hi, lo;
|
||||
// hi,lo = 64bit x 64bit multiply of c[0] and b[0]
|
||||
|
||||
#if defined(__GNUC__) && defined(__x86_64__)
|
||||
__asm__("mulq %3\n\t"
|
||||
: "=d" (hi),
|
||||
"=a" (lo)
|
||||
: "%a" (c[0]),
|
||||
"rm" (b[0])
|
||||
: "cc" );
|
||||
#else
|
||||
lo = mul128(c[0], b[0], &hi);
|
||||
#endif
|
||||
|
||||
a[0] += hi;
|
||||
a[1] += lo;
|
||||
}
|
||||
dst = (uint64_t *) &ctx->long_state[c[0] & 0x1FFFF0];
|
||||
dst[0] = a[0];
|
||||
dst[1] = a[1];
|
||||
|
||||
a[0] ^= b[0];
|
||||
a[1] ^= b[1];
|
||||
b_x = c_x;
|
||||
//__builtin_prefetch(&ctx->long_state[a[0] & 0x1FFFF0], 0, 3);
|
||||
}
|
||||
|
||||
memcpy(ctx->text, ctx->state.init, INIT_SIZE_BYTE);
|
||||
#if defined(AESNI)
|
||||
memcpy(ExpandedKey, &ctx->state.hs.b[32], AES_KEY_SIZE);
|
||||
ExpandAESKey256(ExpandedKey);
|
||||
#else
|
||||
oaes_key_import_data(ctx->aes_ctx, &ctx->state.hs.b[32], AES_KEY_SIZE);
|
||||
memcpy(ExpandedKey, ctx->aes_ctx->key->exp_data, ctx->aes_ctx->key->exp_data_len);
|
||||
#endif
|
||||
|
||||
//for (i = 0; likely(i < MEMORY); i += INIT_SIZE_BYTE)
|
||||
// aesni_parallel_xor(&ctx->text, ExpandedKey, &ctx->long_state[i]);
|
||||
|
||||
for (i = 0; likely(i < MEMORY); i += INIT_SIZE_BYTE)
|
||||
{
|
||||
xmminput[0] = _mm_xor_si128(longoutput[(i >> 4)], xmminput[0]);
|
||||
xmminput[1] = _mm_xor_si128(longoutput[(i >> 4) + 1], xmminput[1]);
|
||||
xmminput[2] = _mm_xor_si128(longoutput[(i >> 4) + 2], xmminput[2]);
|
||||
xmminput[3] = _mm_xor_si128(longoutput[(i >> 4) + 3], xmminput[3]);
|
||||
xmminput[4] = _mm_xor_si128(longoutput[(i >> 4) + 4], xmminput[4]);
|
||||
xmminput[5] = _mm_xor_si128(longoutput[(i >> 4) + 5], xmminput[5]);
|
||||
xmminput[6] = _mm_xor_si128(longoutput[(i >> 4) + 6], xmminput[6]);
|
||||
xmminput[7] = _mm_xor_si128(longoutput[(i >> 4) + 7], xmminput[7]);
|
||||
|
||||
#if defined(AESNI)
|
||||
for(j = 0; j < 10; j++)
|
||||
{
|
||||
xmminput[0] = _mm_aesenc_si128(xmminput[0], expkey[j]);
|
||||
xmminput[1] = _mm_aesenc_si128(xmminput[1], expkey[j]);
|
||||
xmminput[2] = _mm_aesenc_si128(xmminput[2], expkey[j]);
|
||||
xmminput[3] = _mm_aesenc_si128(xmminput[3], expkey[j]);
|
||||
xmminput[4] = _mm_aesenc_si128(xmminput[4], expkey[j]);
|
||||
xmminput[5] = _mm_aesenc_si128(xmminput[5], expkey[j]);
|
||||
xmminput[6] = _mm_aesenc_si128(xmminput[6], expkey[j]);
|
||||
xmminput[7] = _mm_aesenc_si128(xmminput[7], expkey[j]);
|
||||
}
|
||||
#else
|
||||
aesb_pseudo_round((uint8_t *) &xmminput[0], (uint8_t *) &xmminput[0], (uint8_t *) expkey);
|
||||
aesb_pseudo_round((uint8_t *) &xmminput[1], (uint8_t *) &xmminput[1], (uint8_t *) expkey);
|
||||
aesb_pseudo_round((uint8_t *) &xmminput[2], (uint8_t *) &xmminput[2], (uint8_t *) expkey);
|
||||
aesb_pseudo_round((uint8_t *) &xmminput[3], (uint8_t *) &xmminput[3], (uint8_t *) expkey);
|
||||
aesb_pseudo_round((uint8_t *) &xmminput[4], (uint8_t *) &xmminput[4], (uint8_t *) expkey);
|
||||
aesb_pseudo_round((uint8_t *) &xmminput[5], (uint8_t *) &xmminput[5], (uint8_t *) expkey);
|
||||
aesb_pseudo_round((uint8_t *) &xmminput[6], (uint8_t *) &xmminput[6], (uint8_t *) expkey);
|
||||
aesb_pseudo_round((uint8_t *) &xmminput[7], (uint8_t *) &xmminput[7], (uint8_t *) expkey);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#if !defined(AESNI)
|
||||
oaes_free((OAES_CTX **) &ctx->aes_ctx);
|
||||
#endif
|
||||
|
||||
memcpy(ctx->state.init, ctx->text, INIT_SIZE_BYTE);
|
||||
hash_permutation(&ctx->state.hs);
|
||||
extra_hashes[ctx->state.hs.b[0] & 3](&ctx->state, 200, hash);
|
||||
}
|
|
@ -27,7 +27,8 @@
|
|||
#define CRYPTONOTE_DISPLAY_DECIMAL_POINT 8
|
||||
// COIN - number of smallest units in one coin
|
||||
#define COIN ((uint64_t)100000000) // pow(10, 8)
|
||||
#define DEFAULT_FEE ((uint64_t)1000000) // pow(10, 6)
|
||||
#define MINIMUM_FEE ((uint64_t)1000000000) // pow(10, 9)
|
||||
#define DEFAULT_DUST_THRESHOLD ((uint64_t)1000000) // pow(10, 6)
|
||||
|
||||
|
||||
#define DIFFICULTY_TARGET 120 // seconds
|
||||
|
|
|
@ -205,19 +205,22 @@ uint64_t blockchain_storage::get_current_blockchain_height() {
|
|||
return m_blocks.size();
|
||||
}
|
||||
|
||||
bool blockchain_storage::init(const std::string& config_folder) {
|
||||
bool blockchain_storage::init(const std::string& config_folder, bool load_existing) {
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
if (!tools::create_directories_if_necessary(config_folder)) {
|
||||
if (!config_folder.empty() && !tools::create_directories_if_necessary(config_folder)) {
|
||||
LOG_ERROR("Failed to create data directory: " << m_config_folder);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_config_folder = config_folder;
|
||||
LOG_PRINT_L0("Loading blockchain...");
|
||||
|
||||
if (!m_blocks.open(appendPath(config_folder, "blocks.dat"), appendPath(config_folder, "blockindexes.dat"), 1024)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (load_existing) {
|
||||
LOG_PRINT_L0("Loading blockchain...");
|
||||
|
||||
if (m_blocks.empty()) {
|
||||
const std::string filename = appendPath(m_config_folder, CRYPTONOTE_BLOCKCHAINDATA_FILENAME);
|
||||
if (!tools::unserialize_obj_from_file(*this, filename)) {
|
||||
|
@ -268,10 +271,13 @@ bool blockchain_storage::init(const std::string& config_folder) {
|
|||
LOG_PRINT_L0("Rebuilding internal structures took: " << duration.count());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_blocks.clear();
|
||||
}
|
||||
|
||||
if (m_blocks.empty()) {
|
||||
LOG_PRINT_L0("Blockchain not loaded, generating genesis block.");
|
||||
block bl = boost::value_initialized<block>();
|
||||
block bl = ::boost::value_initialized<block>();
|
||||
block_verification_context bvc = boost::value_initialized<block_verification_context>();
|
||||
generate_genesis_block(bl);
|
||||
add_new_block(bl, bvc);
|
||||
|
@ -876,7 +882,7 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto::
|
|||
difficulty_type current_diff = get_next_difficulty_for_alternative_chain(alt_chain, bei);
|
||||
CHECK_AND_ASSERT_MES(current_diff, false, "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!");
|
||||
crypto::hash proof_of_work = null_hash;
|
||||
get_block_longhash(bei.bl, proof_of_work, bei.height);
|
||||
get_block_longhash(m_cn_context, bei.bl, proof_of_work, bei.height);
|
||||
if (!check_hash(proof_of_work, current_diff)) {
|
||||
LOG_PRINT_RED_L0("Block with id: " << id
|
||||
<< ENDL << " for alternative chain, have not enough proof of work: " << proof_of_work
|
||||
|
@ -1497,7 +1503,7 @@ bool blockchain_storage::pushBlock(const block& blockData, block_verification_co
|
|||
return false;
|
||||
}
|
||||
} else {
|
||||
proof_of_work = get_block_longhash(blockData, m_blocks.size());
|
||||
proof_of_work = get_block_longhash(m_cn_context, blockData, m_blocks.size());
|
||||
if (!check_hash(proof_of_work, currentDifficulty)) {
|
||||
LOG_PRINT_L0("Block " << blockHash << ", has too weak proof of work: " << proof_of_work << ", expected difficulty: " << currentDifficulty);
|
||||
bvc.m_verifivation_failed = true;
|
||||
|
|
|
@ -17,8 +17,8 @@ namespace cryptonote {
|
|||
blockchain_storage(tx_memory_pool& tx_pool):m_tx_pool(tx_pool), m_current_block_cumul_sz_limit(0), m_is_in_checkpoint_zone(false), m_is_blockchain_storing(false)
|
||||
{};
|
||||
|
||||
bool init() { return init(tools::get_default_data_dir()); }
|
||||
bool init(const std::string& config_folder);
|
||||
bool init() { return init(tools::get_default_data_dir(), true); }
|
||||
bool init(const std::string& config_folder, bool load_existing);
|
||||
bool deinit();
|
||||
|
||||
void set_checkpoints(checkpoints&& chk_pts) { m_checkpoints = chk_pts; }
|
||||
|
@ -75,24 +75,17 @@ namespace cryptonote {
|
|||
}
|
||||
|
||||
template<class t_ids_container, class t_tx_container, class t_missed_container>
|
||||
bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) {
|
||||
void get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) {
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
|
||||
for (const auto& tx_id : txs_ids) {
|
||||
auto it = m_transactionMap.find(tx_id);
|
||||
if (it == m_transactionMap.end()) {
|
||||
transaction tx;
|
||||
if (!m_tx_pool.get_transaction(tx_id, tx)) {
|
||||
missed_txs.push_back(tx_id);
|
||||
} else {
|
||||
txs.push_back(tx);
|
||||
}
|
||||
} else {
|
||||
txs.push_back(transactionByIndex(it->second).tx);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//debug functions
|
||||
|
@ -146,6 +139,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;
|
||||
|
||||
key_images_container m_spent_keys;
|
||||
size_t m_current_block_cumul_sz_limit;
|
||||
|
|
|
@ -75,27 +75,9 @@ namespace cryptonote
|
|||
{
|
||||
return m_blockchain_storage.get_blocks(start_offset, count, blocks);
|
||||
} //-----------------------------------------------------------------------------------------------
|
||||
bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& missed_txs)
|
||||
void core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& missed_txs)
|
||||
{
|
||||
return m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_transaction(const crypto::hash &h, transaction &tx)
|
||||
{
|
||||
std::vector<crypto::hash> ids;
|
||||
ids.push_back(h);
|
||||
std::list<transaction> ltx;
|
||||
std::list<crypto::hash> missing;
|
||||
if (m_blockchain_storage.get_transactions(ids, ltx, missing))
|
||||
{
|
||||
if (ltx.size() > 0)
|
||||
{
|
||||
tx = *ltx.begin();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_alternative_blocks(std::list<block>& blocks)
|
||||
|
@ -108,14 +90,14 @@ namespace cryptonote
|
|||
return m_blockchain_storage.get_alternative_blocks_count();
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::init(const boost::program_options::variables_map& vm)
|
||||
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);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool");
|
||||
|
||||
r = m_blockchain_storage.init(m_config_folder);
|
||||
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);
|
||||
|
@ -288,17 +270,17 @@ namespace cryptonote
|
|||
// return m_blockchain_storage.get_outs(amount, pkeys);
|
||||
//}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::add_new_tx(const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block)
|
||||
{
|
||||
if(m_mempool.have_tx(tx_hash))
|
||||
{
|
||||
LOG_PRINT_L2("tx " << tx_hash << "already have transaction in tx_pool");
|
||||
bool core::add_new_tx(const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block) {
|
||||
if (m_blockchain_storage.have_tx(tx_hash)) {
|
||||
LOG_PRINT_L2("tx " << tx_hash << " is already in blockchain");
|
||||
return true;
|
||||
}
|
||||
|
||||
if(m_blockchain_storage.have_tx(tx_hash))
|
||||
{
|
||||
LOG_PRINT_L2("tx " << tx_hash << " already have transaction in blockchain");
|
||||
// It's not very good to lock on m_mempool here, because it's very hard to understand the order of locking
|
||||
// tx_memory_pool::m_transactions_lock, blockchain_storage::m_blockchain_lock, and core::m_incoming_tx_lock
|
||||
CRITICAL_REGION_LOCAL(m_mempool);
|
||||
if (m_mempool.have_tx(tx_hash)) {
|
||||
LOG_PRINT_L2("tx " << tx_hash << " is already in transaction pool");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -421,7 +403,14 @@ namespace cryptonote
|
|||
}
|
||||
|
||||
m_blockchain_storage.add_new_block(b, bvc);
|
||||
if (bvc.m_added_to_main_chain) {
|
||||
if (bvc.m_added_to_main_chain && update_miner_blocktemplate) {
|
||||
update_miner_block_template();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void core::notify_new_block(const block& b) {
|
||||
cryptonote_connection_context exclude_context = boost::value_initialized<cryptonote_connection_context>();
|
||||
NOTIFY_NEW_BLOCK::request arg = AUTO_VAL_INIT(arg);
|
||||
arg.hop = 0;
|
||||
|
@ -432,18 +421,14 @@ namespace cryptonote
|
|||
if (missed_txs.size() > 0 && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b)) {
|
||||
LOG_PRINT_L0("Block found but reorganize happened after that, block will not be relayed");
|
||||
} else {
|
||||
CHECK_AND_ASSERT_MES(txs.size() == b.tx_hashes.size() && !missed_txs.size(), false, "cant find some transactions in found block:" << get_block_hash(b) << " txs.size()=" << txs.size() << ", b.tx_hashes.size()=" << b.tx_hashes.size() << ", missed_txs.size()" << missed_txs.size());
|
||||
if (txs.size() != b.tx_hashes.size() || missed_txs.size()) {
|
||||
LOG_ERROR("cant find some transactions in found block:" << get_block_hash(b) << " txs.size()=" << txs.size() << ", b.tx_hashes.size()=" << b.tx_hashes.size() << ", missed_txs.size()" << missed_txs.size());
|
||||
return;
|
||||
}
|
||||
block_to_blob(b, arg.b.block);
|
||||
BOOST_FOREACH(auto& tx, txs) arg.b.txs.push_back(t_serializable_object_to_blob(tx));
|
||||
m_pprotocol->relay_block(arg, exclude_context);
|
||||
}
|
||||
|
||||
if (update_miner_blocktemplate) {
|
||||
update_miner_block_template();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
crypto::hash core::get_tail_id()
|
||||
|
@ -471,9 +456,9 @@ namespace cryptonote
|
|||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_pool_transactions(std::list<transaction>& txs)
|
||||
void core::get_pool_transactions(std::list<transaction>& txs)
|
||||
{
|
||||
return m_mempool.get_transactions(txs);
|
||||
m_mempool.get_transactions(txs);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_short_chain_history(std::list<crypto::hash>& ids)
|
||||
|
|
|
@ -43,7 +43,7 @@ namespace cryptonote
|
|||
|
||||
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 init(const boost::program_options::variables_map& vm, bool load_existing);
|
||||
bool set_genesis_block(const block& b);
|
||||
bool deinit();
|
||||
uint64_t get_current_blockchain_height();
|
||||
|
@ -56,8 +56,7 @@ namespace cryptonote
|
|||
return m_blockchain_storage.get_blocks(block_ids, blocks, missed_bs);
|
||||
}
|
||||
crypto::hash get_block_id_by_height(uint64_t height);
|
||||
bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& missed_txs);
|
||||
bool get_transaction(const crypto::hash &h, transaction &tx);
|
||||
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);
|
||||
|
||||
|
@ -67,7 +66,7 @@ namespace cryptonote
|
|||
void set_cryptonote_protocol(i_cryptonote_protocol* pprotocol);
|
||||
void set_checkpoints(checkpoints&& chk_pts);
|
||||
|
||||
bool get_pool_transactions(std::list<transaction>& txs);
|
||||
void get_pool_transactions(std::list<transaction>& txs);
|
||||
size_t get_pool_transactions_count();
|
||||
size_t get_blockchain_total_transactions();
|
||||
//bool get_outs(uint64_t amount, std::list<crypto::public_key>& pkeys);
|
||||
|
@ -89,6 +88,7 @@ namespace cryptonote
|
|||
std::string print_pool(bool short_format);
|
||||
void print_blockchain_outs(const std::string& file);
|
||||
void on_synchronized();
|
||||
void notify_new_block(const block& b);
|
||||
|
||||
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);
|
||||
|
|
|
@ -81,7 +81,7 @@ namespace cryptonote
|
|||
block_reward += fee;
|
||||
|
||||
std::vector<uint64_t> out_amounts;
|
||||
decompose_amount_into_digits(block_reward, DEFAULT_FEE,
|
||||
decompose_amount_into_digits(block_reward, DEFAULT_DUST_THRESHOLD,
|
||||
[&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); },
|
||||
[&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); });
|
||||
|
||||
|
@ -640,16 +640,16 @@ namespace cryptonote
|
|||
bl.minor_version = CURRENT_BLOCK_MINOR_VERSION;
|
||||
bl.timestamp = 0;
|
||||
bl.nonce = 70;
|
||||
miner::find_nonce_for_given_block(bl, 1, 0);
|
||||
//miner::find_nonce_for_given_block(bl, 1, 0);
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height)
|
||||
bool get_block_longhash(crypto::cn_context &context, const block& b, crypto::hash& res, uint64_t height)
|
||||
{
|
||||
blobdata bd;
|
||||
if(!get_block_hashing_blob(b, bd))
|
||||
return false;
|
||||
crypto::cn_slow_hash(bd.data(), bd.size(), res);
|
||||
crypto::cn_slow_hash(context, bd.data(), bd.size(), res);
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
|
@ -673,10 +673,10 @@ namespace cryptonote
|
|||
return res;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
crypto::hash get_block_longhash(const block& b, uint64_t height)
|
||||
crypto::hash get_block_longhash(crypto::cn_context &context, const block& b, uint64_t height)
|
||||
{
|
||||
crypto::hash p = null_hash;
|
||||
get_block_longhash(b, p, height);
|
||||
get_block_longhash(context, b, p, height);
|
||||
return p;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
|
|
|
@ -77,8 +77,8 @@ namespace cryptonote
|
|||
bool get_block_hashing_blob(const block& b, blobdata& blob);
|
||||
bool get_block_hash(const block& b, crypto::hash& res);
|
||||
crypto::hash get_block_hash(const block& b);
|
||||
bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height);
|
||||
crypto::hash get_block_longhash(const block& b, uint64_t height);
|
||||
bool get_block_longhash(crypto::cn_context &context, const block& b, crypto::hash& res, uint64_t height);
|
||||
crypto::hash get_block_longhash(crypto::cn_context &context, const block& b, uint64_t height);
|
||||
bool generate_genesis_block(block& bl);
|
||||
bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b);
|
||||
bool get_inputs_money_amount(const transaction& tx, uint64_t& money);
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include "cryptonote_format_utils.h"
|
||||
#include "file_io_utils.h"
|
||||
#include "common/command_line.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "crypto/random.h"
|
||||
#include "string_coding.h"
|
||||
#include "storages/portable_storage_template_helper.h"
|
||||
|
||||
|
@ -252,12 +254,12 @@ namespace cryptonote
|
|||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool miner::find_nonce_for_given_block(block& bl, const difficulty_type& diffic, uint64_t height)
|
||||
bool miner::find_nonce_for_given_block(crypto::cn_context &context, block& bl, const difficulty_type& diffic, uint64_t height)
|
||||
{
|
||||
for(; bl.nonce != std::numeric_limits<uint32_t>::max(); bl.nonce++)
|
||||
{
|
||||
crypto::hash h;
|
||||
get_block_longhash(bl, h, height);
|
||||
get_block_longhash(context, bl, h, height);
|
||||
|
||||
if(check_hash(h, diffic))
|
||||
{
|
||||
|
@ -308,6 +310,7 @@ namespace cryptonote
|
|||
uint64_t height = 0;
|
||||
difficulty_type local_diff = 0;
|
||||
uint32_t local_template_ver = 0;
|
||||
crypto::cn_context context;
|
||||
block b;
|
||||
while(!m_stop)
|
||||
{
|
||||
|
@ -338,7 +341,7 @@ namespace cryptonote
|
|||
|
||||
b.nonce = nonce;
|
||||
crypto::hash h;
|
||||
get_block_longhash(b, h, height);
|
||||
get_block_longhash(context, b, h, height);
|
||||
|
||||
if(!m_stop && check_hash(h, local_diff))
|
||||
{
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace cryptonote
|
|||
bool on_idle();
|
||||
void on_synchronized();
|
||||
//synchronous analog (for fast calls)
|
||||
static bool find_nonce_for_given_block(block& bl, const difficulty_type& diffic, uint64_t height);
|
||||
static bool find_nonce_for_given_block(crypto::cn_context &context, block& bl, const difficulty_type& diffic, uint64_t height);
|
||||
void pause();
|
||||
void resume();
|
||||
void do_print_hashrate(bool do_hr);
|
||||
|
|
|
@ -20,60 +20,53 @@
|
|||
|
||||
DISABLE_VS_WARNINGS(4244 4345 4503) //'boost::foreach_detail_::or_' : decorated name length exceeded, name was truncated
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
namespace cryptonote {
|
||||
//---------------------------------------------------------------------------------
|
||||
tx_memory_pool::tx_memory_pool(blockchain_storage& bchs): m_blockchain(bchs)
|
||||
{
|
||||
|
||||
tx_memory_pool::tx_memory_pool(blockchain_storage& bchs): m_blockchain(bchs) {
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::add_tx(const transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block)
|
||||
{
|
||||
|
||||
|
||||
if(!check_inputs_types_supported(tx))
|
||||
{
|
||||
bool tx_memory_pool::add_tx(const transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block) {
|
||||
if (!check_inputs_types_supported(tx)) {
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t inputs_amount = 0;
|
||||
if(!get_inputs_money_amount(tx, inputs_amount))
|
||||
{
|
||||
if (!get_inputs_money_amount(tx, inputs_amount)) {
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t outputs_amount = get_outs_money_amount(tx);
|
||||
|
||||
if(outputs_amount >= inputs_amount)
|
||||
{
|
||||
LOG_PRINT_L0("transaction use more money then it has: use " << outputs_amount << ", have " << inputs_amount);
|
||||
if (outputs_amount >= inputs_amount) {
|
||||
LOG_PRINT_L0("transaction use more money then it has: use " << print_money(outputs_amount) << ", have " << print_money(inputs_amount));
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t fee = inputs_amount - outputs_amount;
|
||||
if (!kept_by_block && fee < MINIMUM_FEE) {
|
||||
LOG_ERROR("transaction fee is not enought: " << print_money(fee) << ", minumim fee: " << print_money(MINIMUM_FEE));
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//check key images for transaction if it is not kept by block
|
||||
if(!kept_by_block)
|
||||
{
|
||||
if(have_tx_keyimges_as_spent(tx))
|
||||
{
|
||||
if (!kept_by_block) {
|
||||
if (have_tx_keyimges_as_spent(tx)) {
|
||||
LOG_ERROR("Transaction with id= "<< id << " used already spent key images");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
crypto::hash max_used_block_id = null_hash;
|
||||
uint64_t max_used_block_height = 0;
|
||||
bool ch_inp_res = m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id);
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
if(!ch_inp_res)
|
||||
{
|
||||
if(kept_by_block)
|
||||
{
|
||||
if (!ch_inp_res) {
|
||||
if (kept_by_block) {
|
||||
//anyway add this transaction to pool, because it related to block
|
||||
auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details()));
|
||||
CHECK_AND_ASSERT_MES(txd_p.second, false, "transaction already exists at inserting in memory pool");
|
||||
|
@ -85,14 +78,12 @@ namespace cryptonote
|
|||
txd_p.first->second.kept_by_block = kept_by_block;
|
||||
tvc.m_verifivation_impossible = true;
|
||||
tvc.m_added_to_pool = true;
|
||||
}else
|
||||
{
|
||||
} else {
|
||||
LOG_PRINT_L0("tx used wrong inputs, rejected");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
}else
|
||||
{
|
||||
} else {
|
||||
//update transactions container
|
||||
auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details()));
|
||||
CHECK_AND_ASSERT_MES(txd_p.second, false, "intrnal error: transaction already exists at inserting in memorypool");
|
||||
|
@ -106,14 +97,14 @@ namespace cryptonote
|
|||
txd_p.first->second.last_failed_id = null_hash;
|
||||
tvc.m_added_to_pool = true;
|
||||
|
||||
if(txd_p.first->second.fee > 0)
|
||||
if (txd_p.first->second.fee > 0) {
|
||||
tvc.m_should_be_relayed = true;
|
||||
}
|
||||
}
|
||||
|
||||
tvc.m_verifivation_failed = true;
|
||||
//update image_keys container, here should everything goes ok.
|
||||
BOOST_FOREACH(const auto& in, tx.vin)
|
||||
{
|
||||
for (const auto& in : tx.vin) {
|
||||
CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, txin, false);
|
||||
std::unordered_set<crypto::hash>& kei_image_set = m_spent_key_images[txin.k_image];
|
||||
CHECK_AND_ASSERT_MES(kept_by_block || kei_image_set.size() == 0, false, "internal error: keeped_by_block=" << kept_by_block
|
||||
|
@ -128,47 +119,43 @@ namespace cryptonote
|
|||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::add_tx(const transaction &tx, tx_verification_context& tvc, bool keeped_by_block)
|
||||
{
|
||||
bool tx_memory_pool::add_tx(const transaction &tx, tx_verification_context& tvc, bool keeped_by_block) {
|
||||
crypto::hash h = null_hash;
|
||||
size_t blob_size = 0;
|
||||
get_transaction_hash(tx, h, blob_size);
|
||||
return add_tx(tx, h, blob_size, tvc, keeped_by_block);
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::remove_transaction_keyimages(const transaction& tx)
|
||||
{
|
||||
bool tx_memory_pool::remove_transaction_keyimages(const transaction& tx) {
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
BOOST_FOREACH(const txin_v& vi, tx.vin)
|
||||
{
|
||||
crypto::hash tx_id = get_transaction_hash(tx);
|
||||
for (const txin_v& vi : tx.vin) {
|
||||
CHECKED_GET_SPECIFIC_VARIANT(vi, const txin_to_key, txin, false);
|
||||
auto it = m_spent_key_images.find(txin.k_image);
|
||||
CHECK_AND_ASSERT_MES(it != m_spent_key_images.end(), false, "failed to find transaction input in key images. img=" << txin.k_image << ENDL
|
||||
<< "transaction id = " << get_transaction_hash(tx));
|
||||
CHECK_AND_ASSERT_MES(it != m_spent_key_images.end(), false, "failed to find transaction input in key images. img=" << txin.k_image << std::endl
|
||||
<< "transaction id = " << tx_id);
|
||||
std::unordered_set<crypto::hash>& key_image_set = it->second;
|
||||
CHECK_AND_ASSERT_MES(key_image_set.size(), false, "empty key_image set, img=" << txin.k_image << ENDL
|
||||
<< "transaction id = " << get_transaction_hash(tx));
|
||||
CHECK_AND_ASSERT_MES(!key_image_set.empty(), false, "empty key_image set, img=" << txin.k_image << std::endl
|
||||
<< "transaction id = " << tx_id);
|
||||
|
||||
auto it_in_set = key_image_set.find(get_transaction_hash(tx));
|
||||
CHECK_AND_ASSERT_MES(key_image_set.size(), false, "transaction id not found in key_image set, img=" << txin.k_image << ENDL
|
||||
<< "transaction id = " << get_transaction_hash(tx));
|
||||
auto it_in_set = key_image_set.find(tx_id);
|
||||
CHECK_AND_ASSERT_MES(it_in_set != key_image_set.end(), false, "transaction id not found in key_image set, img=" << txin.k_image << std::endl
|
||||
<< "transaction id = " << tx_id);
|
||||
key_image_set.erase(it_in_set);
|
||||
if(!key_image_set.size())
|
||||
{
|
||||
if (key_image_set.empty()) {
|
||||
//it is now empty hash container for this key_image
|
||||
m_spent_key_images.erase(it);
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee)
|
||||
{
|
||||
bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee) {
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
auto it = m_transactions.find(id);
|
||||
if(it == m_transactions.end())
|
||||
if (it == m_transactions.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tx = it->second.tx;
|
||||
blob_size = it->second.blob_size;
|
||||
|
@ -178,134 +165,111 @@ namespace cryptonote
|
|||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
size_t tx_memory_pool::get_transactions_count()
|
||||
{
|
||||
size_t tx_memory_pool::get_transactions_count() const {
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
return m_transactions.size();
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::get_transactions(std::list<transaction>& txs)
|
||||
{
|
||||
void tx_memory_pool::get_transactions(std::list<transaction>& txs) const {
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
BOOST_FOREACH(const auto& tx_vt, m_transactions)
|
||||
for (const auto& tx_vt : m_transactions) {
|
||||
txs.push_back(tx_vt.second.tx);
|
||||
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id) {
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::get_transaction(const crypto::hash& id, transaction& tx)
|
||||
{
|
||||
bool tx_memory_pool::on_blockchain_dec(uint64_t new_block_height, const crypto::hash& top_block_id) {
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::have_tx(const crypto::hash &id) const {
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
auto it = m_transactions.find(id);
|
||||
if(it == m_transactions.end())
|
||||
return false;
|
||||
tx = it->second.tx;
|
||||
if (m_transactions.count(id)) {
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::on_blockchain_dec(uint64_t new_block_height, const crypto::hash& top_block_id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::have_tx(const crypto::hash &id)
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
if(m_transactions.count(id))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::have_tx_keyimges_as_spent(const transaction& tx)
|
||||
{
|
||||
bool tx_memory_pool::have_tx_keyimges_as_spent(const transaction& tx) const {
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
BOOST_FOREACH(const auto& in, tx.vin)
|
||||
{
|
||||
for (const auto& in : tx.vin) {
|
||||
CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, true);//should never fail
|
||||
if(have_tx_keyimg_as_spent(tokey_in.k_image))
|
||||
if (have_tx_keyimg_as_spent(tokey_in.k_image)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::have_tx_keyimg_as_spent(const crypto::key_image& key_im)
|
||||
{
|
||||
bool tx_memory_pool::have_tx_keyimg_as_spent(const crypto::key_image& key_im) const {
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
return m_spent_key_images.end() != m_spent_key_images.find(key_im);
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
void tx_memory_pool::lock()
|
||||
{
|
||||
void tx_memory_pool::lock() const {
|
||||
m_transactions_lock.lock();
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
void tx_memory_pool::unlock()
|
||||
{
|
||||
void tx_memory_pool::unlock() const {
|
||||
m_transactions_lock.unlock();
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::is_transaction_ready_to_go(tx_details& txd)
|
||||
{
|
||||
bool tx_memory_pool::is_transaction_ready_to_go(tx_details& txd) const {
|
||||
//not the best implementation at this time, sorry :(
|
||||
//check is ring_signature already checked ?
|
||||
if(txd.max_used_block_id == null_hash)
|
||||
{//not checked, lets try to check
|
||||
|
||||
if(txd.last_failed_id != null_hash && m_blockchain.get_current_blockchain_height() > txd.last_failed_height && txd.last_failed_id == m_blockchain.get_block_id_by_height(txd.last_failed_height))
|
||||
if (txd.max_used_block_id == null_hash) {
|
||||
//not checked, lets try to check
|
||||
if (txd.last_failed_id != null_hash && m_blockchain.get_current_blockchain_height() > txd.last_failed_height && txd.last_failed_id == m_blockchain.get_block_id_by_height(txd.last_failed_height)) {
|
||||
return false;//we already sure that this tx is broken for this height
|
||||
}
|
||||
|
||||
if(!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id))
|
||||
{
|
||||
if (!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id)) {
|
||||
txd.last_failed_height = m_blockchain.get_current_blockchain_height()-1;
|
||||
txd.last_failed_id = m_blockchain.get_block_id_by_height(txd.last_failed_height);
|
||||
return false;
|
||||
}
|
||||
}else
|
||||
{
|
||||
if(txd.max_used_block_height >= m_blockchain.get_current_blockchain_height())
|
||||
} else {
|
||||
if (txd.max_used_block_height >= m_blockchain.get_current_blockchain_height()) {
|
||||
return false;
|
||||
if(m_blockchain.get_block_id_by_height(txd.max_used_block_height) != txd.max_used_block_id)
|
||||
{
|
||||
}
|
||||
if (m_blockchain.get_block_id_by_height(txd.max_used_block_height) != txd.max_used_block_id) {
|
||||
//if we already failed on this height and id, skip actual ring signature check
|
||||
if(txd.last_failed_id == m_blockchain.get_block_id_by_height(txd.last_failed_height))
|
||||
if (txd.last_failed_id == m_blockchain.get_block_id_by_height(txd.last_failed_height)) {
|
||||
return false;
|
||||
}
|
||||
//check ring signature again, it is possible (with very small chance) that this transaction become again valid
|
||||
if(!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id))
|
||||
{
|
||||
if (!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id)) {
|
||||
txd.last_failed_height = m_blockchain.get_current_blockchain_height()-1;
|
||||
txd.last_failed_id = m_blockchain.get_block_id_by_height(txd.last_failed_height);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if we here, transaction seems valid, but, anyway, check for key_images collisions with blockchain, just to be sure
|
||||
if(m_blockchain.have_tx_keyimges_as_spent(txd.tx))
|
||||
if (m_blockchain.have_tx_keyimges_as_spent(txd.tx)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//transaction is ok.
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::have_key_images(const std::unordered_set<crypto::key_image>& k_images, const transaction& tx)
|
||||
{
|
||||
for(size_t i = 0; i!= tx.vin.size(); i++)
|
||||
{
|
||||
bool tx_memory_pool::have_key_images(const std::unordered_set<crypto::key_image>& k_images, const transaction& tx) {
|
||||
for (size_t i = 0; i!= tx.vin.size(); i++) {
|
||||
CHECKED_GET_SPECIFIC_VARIANT(tx.vin[i], const txin_to_key, itk, false);
|
||||
if(k_images.count(itk.k_image))
|
||||
if (k_images.count(itk.k_image)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::append_key_images(std::unordered_set<crypto::key_image>& k_images, const transaction& tx)
|
||||
{
|
||||
for(size_t i = 0; i!= tx.vin.size(); i++)
|
||||
{
|
||||
bool tx_memory_pool::append_key_images(std::unordered_set<crypto::key_image>& k_images, const transaction& tx) {
|
||||
for (size_t i = 0; i!= tx.vin.size(); i++) {
|
||||
CHECKED_GET_SPECIFIC_VARIANT(tx.vin[i], const txin_to_key, itk, false);
|
||||
auto i_res = k_images.insert(itk.k_image);
|
||||
CHECK_AND_ASSERT_MES(i_res.second, false, "internal error: key images pool cache - inserted duplicate image in set: " << itk.k_image);
|
||||
|
@ -313,57 +277,43 @@ namespace cryptonote
|
|||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
std::string tx_memory_pool::print_pool(bool short_format)
|
||||
{
|
||||
std::string tx_memory_pool::print_pool(bool short_format) const {
|
||||
std::stringstream ss;
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
BOOST_FOREACH(transactions_container::value_type& txe, m_transactions)
|
||||
{
|
||||
if(short_format)
|
||||
{
|
||||
tx_details& txd = txe.second;
|
||||
ss << "id: " << txe.first << ENDL
|
||||
<< "blob_size: " << txd.blob_size << ENDL
|
||||
<< "fee: " << txd.fee << ENDL
|
||||
<< "kept_by_block: " << txd.kept_by_block << ENDL
|
||||
<< "max_used_block_height: " << txd.max_used_block_height << ENDL
|
||||
<< "max_used_block_id: " << txd.max_used_block_id << ENDL
|
||||
<< "last_failed_height: " << txd.last_failed_height << ENDL
|
||||
<< "last_failed_id: " << txd.last_failed_id << ENDL;
|
||||
}else
|
||||
{
|
||||
tx_details& txd = txe.second;
|
||||
ss << "id: " << txe.first << ENDL
|
||||
<< obj_to_json_str(txd.tx) << ENDL
|
||||
<< "blob_size: " << txd.blob_size << ENDL
|
||||
<< "fee: " << txd.fee << ENDL
|
||||
<< "kept_by_block: " << txd.kept_by_block << ENDL
|
||||
<< "max_used_block_height: " << txd.max_used_block_height << ENDL
|
||||
<< "max_used_block_id: " << txd.max_used_block_id << ENDL
|
||||
<< "last_failed_height: " << txd.last_failed_height << ENDL
|
||||
<< "last_failed_id: " << txd.last_failed_id << ENDL;
|
||||
for (const transactions_container::value_type& txe : m_transactions) {
|
||||
const tx_details& txd = txe.second;
|
||||
ss << "id: " << txe.first << std::endl;
|
||||
if (!short_format) {
|
||||
ss << obj_to_json_str(*const_cast<transaction*>(&txd.tx)) << std::endl;
|
||||
}
|
||||
ss << "blob_size: " << txd.blob_size << std::endl
|
||||
<< "fee: " << print_money(txd.fee) << std::endl
|
||||
<< "kept_by_block: " << (txd.kept_by_block ? 'T' : 'F') << std::endl
|
||||
<< "max_used_block_height: " << txd.max_used_block_height << std::endl
|
||||
<< "max_used_block_id: " << txd.max_used_block_id << std::endl
|
||||
<< "last_failed_height: " << txd.last_failed_height << std::endl
|
||||
<< "last_failed_id: " << txd.last_failed_id << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee)
|
||||
{
|
||||
bool tx_memory_pool::fill_block_template(block& bl, size_t median_size, uint64_t already_generated_coins, size_t& total_size, uint64_t& fee) {
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
|
||||
total_size = 0;
|
||||
fee = 0;
|
||||
|
||||
size_t max_total_size = 2 * median_size - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
|
||||
size_t max_total_size = (125 * median_size) / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
|
||||
std::unordered_set<crypto::key_image> k_images;
|
||||
BOOST_FOREACH(transactions_container::value_type& tx, m_transactions)
|
||||
{
|
||||
if (max_total_size < total_size + tx.second.blob_size)
|
||||
for (transactions_container::value_type& tx : m_transactions) {
|
||||
if (max_total_size < total_size + tx.second.blob_size) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_transaction_ready_to_go(tx.second) || have_key_images(k_images, tx.second.tx))
|
||||
if (!is_transaction_ready_to_go(tx.second) || have_key_images(k_images, tx.second.tx)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bl.tx_hashes.push_back(tx.first);
|
||||
total_size += tx.second.blob_size;
|
||||
|
@ -374,34 +324,35 @@ namespace cryptonote
|
|||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::init(const std::string& config_folder)
|
||||
{
|
||||
bool tx_memory_pool::init(const std::string& config_folder) {
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
|
||||
m_config_folder = config_folder;
|
||||
std::string state_file_path = config_folder + "/" + CRYPTONOTE_POOLDATA_FILENAME;
|
||||
boost::system::error_code ec;
|
||||
if(!boost::filesystem::exists(state_file_path, ec))
|
||||
if (!boost::filesystem::exists(state_file_path, ec)) {
|
||||
return true;
|
||||
}
|
||||
bool res = tools::unserialize_obj_from_file(*this, state_file_path);
|
||||
if(!res)
|
||||
{
|
||||
LOG_PRINT_L0("Failed to load memory pool from file " << state_file_path);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
if (!res) {
|
||||
LOG_ERROR("Failed to load memory pool from file " << state_file_path);
|
||||
|
||||
m_transactions.clear();
|
||||
m_spent_key_images.clear();
|
||||
}
|
||||
// Ignore deserialization error
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::deinit()
|
||||
{
|
||||
if (!tools::create_directories_if_necessary(m_config_folder))
|
||||
{
|
||||
bool tx_memory_pool::deinit() {
|
||||
if (!tools::create_directories_if_necessary(m_config_folder)) {
|
||||
LOG_PRINT_L0("Failed to create data directory: " << m_config_folder);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string state_file_path = m_config_folder + "/" + CRYPTONOTE_POOLDATA_FILENAME;
|
||||
bool res = tools::serialize_obj_to_file(*this, state_file_path);
|
||||
if(!res)
|
||||
{
|
||||
if (!res) {
|
||||
LOG_PRINT_L0("Failed to serialize memory pool to file " << state_file_path);
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -18,61 +18,52 @@
|
|||
#include "crypto/hash.h"
|
||||
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
namespace cryptonote {
|
||||
class blockchain_storage;
|
||||
|
||||
/************************************************************************/
|
||||
/* */
|
||||
/************************************************************************/
|
||||
|
||||
class tx_memory_pool: boost::noncopyable
|
||||
{
|
||||
class tx_memory_pool: boost::noncopyable {
|
||||
public:
|
||||
tx_memory_pool(blockchain_storage& bchs);
|
||||
|
||||
// load/store operations
|
||||
bool init(const std::string& config_folder);
|
||||
bool deinit();
|
||||
|
||||
bool have_tx(const crypto::hash &id) const;
|
||||
bool add_tx(const transaction &tx, const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block);
|
||||
bool add_tx(const transaction &tx, tx_verification_context& tvc, bool keeped_by_block);
|
||||
//gets tx and remove it from pool
|
||||
bool take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee);
|
||||
|
||||
bool have_tx(const crypto::hash &id);
|
||||
bool have_tx_keyimg_as_spent(const crypto::key_image& key_im);
|
||||
bool have_tx_keyimges_as_spent(const transaction& tx);
|
||||
|
||||
bool on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id);
|
||||
bool on_blockchain_dec(uint64_t new_block_height, const crypto::hash& top_block_id);
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
void lock() const;
|
||||
void unlock() const;
|
||||
|
||||
// load/store operations
|
||||
bool init(const std::string& config_folder);
|
||||
bool deinit();
|
||||
bool fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee);
|
||||
bool get_transactions(std::list<transaction>& txs);
|
||||
bool get_transaction(const crypto::hash& h, transaction& tx);
|
||||
size_t get_transactions_count();
|
||||
bool remove_transaction_keyimages(const transaction& tx);
|
||||
bool have_key_images(const std::unordered_set<crypto::key_image>& kic, const transaction& tx);
|
||||
bool append_key_images(std::unordered_set<crypto::key_image>& kic, const transaction& tx);
|
||||
std::string print_pool(bool short_format);
|
||||
|
||||
/*bool flush_pool(const std::strig& folder);
|
||||
bool inflate_pool(const std::strig& folder);*/
|
||||
void get_transactions(std::list<transaction>& txs) const;
|
||||
size_t get_transactions_count() const;
|
||||
std::string print_pool(bool short_format) const;
|
||||
|
||||
#define CURRENT_MEMPOOL_ARCHIVE_VER 7
|
||||
|
||||
template<class archive_t>
|
||||
void serialize(archive_t & a, const unsigned int version)
|
||||
{
|
||||
if(version < CURRENT_MEMPOOL_ARCHIVE_VER )
|
||||
void serialize(archive_t & a, const unsigned int version) {
|
||||
if (version < CURRENT_MEMPOOL_ARCHIVE_VER) {
|
||||
return;
|
||||
}
|
||||
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
a & m_transactions;
|
||||
a & m_spent_key_images;
|
||||
}
|
||||
|
||||
struct tx_details
|
||||
{
|
||||
struct tx_details {
|
||||
transaction tx;
|
||||
size_t blob_size;
|
||||
uint64_t fee;
|
||||
|
@ -85,56 +76,38 @@ namespace cryptonote
|
|||
};
|
||||
|
||||
private:
|
||||
bool is_transaction_ready_to_go(tx_details& txd);
|
||||
bool have_tx_keyimg_as_spent(const crypto::key_image& key_im) const;
|
||||
bool have_tx_keyimges_as_spent(const transaction& tx) const;
|
||||
bool remove_transaction_keyimages(const transaction& tx);
|
||||
static bool have_key_images(const std::unordered_set<crypto::key_image>& kic, const transaction& tx);
|
||||
static bool append_key_images(std::unordered_set<crypto::key_image>& kic, const transaction& tx);
|
||||
|
||||
bool is_transaction_ready_to_go(tx_details& txd) const;
|
||||
|
||||
typedef std::unordered_map<crypto::hash, tx_details > transactions_container;
|
||||
typedef std::unordered_map<crypto::key_image, std::unordered_set<crypto::hash> > key_images_container;
|
||||
|
||||
epee::critical_section m_transactions_lock;
|
||||
mutable epee::critical_section m_transactions_lock;
|
||||
transactions_container m_transactions;
|
||||
key_images_container m_spent_key_images;
|
||||
|
||||
//transactions_container m_alternative_transactions;
|
||||
|
||||
std::string m_config_folder;
|
||||
blockchain_storage& m_blockchain;
|
||||
|
||||
/************************************************************************/
|
||||
/* */
|
||||
/************************************************************************/
|
||||
/*class inputs_visitor: public boost::static_visitor<bool>
|
||||
{
|
||||
key_images_container& m_spent_keys;
|
||||
class amount_visitor: public boost::static_visitor<uint64_t> {
|
||||
public:
|
||||
inputs_visitor(key_images_container& spent_keys): m_spent_keys(spent_keys)
|
||||
{}
|
||||
bool operator()(const txin_to_key& tx) const
|
||||
{
|
||||
auto pr = m_spent_keys.insert(tx.k_image);
|
||||
CHECK_AND_ASSERT_MES(pr.second, false, "Tried to insert transaction with input seems already spent, input: " << epee::string_tools::pod_to_hex(tx.k_image));
|
||||
return true;
|
||||
}
|
||||
bool operator()(const txin_gen& tx) const
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(false, false, "coinbase transaction in memory pool");
|
||||
return false;
|
||||
}
|
||||
bool operator()(const txin_to_script& tx) const {return false;}
|
||||
bool operator()(const txin_to_scripthash& tx) const {return false;}
|
||||
}; */
|
||||
/************************************************************************/
|
||||
/* */
|
||||
/************************************************************************/
|
||||
class amount_visitor: public boost::static_visitor<uint64_t>
|
||||
{
|
||||
public:
|
||||
uint64_t operator()(const txin_to_key& tx) const
|
||||
{
|
||||
uint64_t operator()(const txin_to_key& tx) const {
|
||||
return tx.amount;
|
||||
}
|
||||
uint64_t operator()(const txin_gen& tx) const
|
||||
{
|
||||
|
||||
uint64_t operator()(const txin_gen& tx) const {
|
||||
CHECK_AND_ASSERT_MES(false, false, "coinbase transaction in memory pool");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t operator()(const txin_to_script& tx) const { return 0; }
|
||||
uint64_t operator()(const txin_to_scripthash& tx) const { return 0; }
|
||||
};
|
||||
|
@ -145,13 +118,10 @@ namespace cryptonote
|
|||
};
|
||||
}
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace serialization
|
||||
{
|
||||
namespace boost {
|
||||
namespace serialization {
|
||||
template<class archive_t>
|
||||
void serialize(archive_t & ar, cryptonote::tx_memory_pool::tx_details& td, const unsigned int version)
|
||||
{
|
||||
void serialize(archive_t & ar, cryptonote::tx_memory_pool::tx_details& td, const unsigned int version) {
|
||||
ar & td.blob_size;
|
||||
ar & td.fee;
|
||||
ar & td.tx;
|
||||
|
@ -159,11 +129,8 @@ namespace boost
|
|||
ar & td.max_used_block_id;
|
||||
ar & td.last_failed_height;
|
||||
ar & td.last_failed_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
BOOST_CLASS_VERSION(cryptonote::tx_memory_pool, CURRENT_MEMPOOL_ARCHIVE_VER)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -160,7 +160,7 @@ int main(int argc, char* argv[])
|
|||
|
||||
//initialize core here
|
||||
LOG_PRINT_L0("Initializing core...");
|
||||
res = ccore.init(vm);
|
||||
res = ccore.init(vm, true);
|
||||
CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core");
|
||||
LOG_PRINT_L0("Core initialized OK");
|
||||
|
||||
|
|
|
@ -101,6 +101,7 @@ namespace mining
|
|||
std::string pool_session_id;
|
||||
simpleminer::job_details_native job = AUTO_VAL_INIT(job);
|
||||
uint64_t last_job_ticks = 0;
|
||||
crypto::cn_context context;
|
||||
|
||||
while(true)
|
||||
{
|
||||
|
@ -158,7 +159,7 @@ namespace mining
|
|||
//uint32_t c = (*((uint32_t*)&job.blob.data()[39]));
|
||||
++(*((uint32_t*)&job.blob.data()[39]));
|
||||
crypto::hash h = cryptonote::null_hash;
|
||||
crypto::cn_slow_hash(job.blob.data(), job.blob.size(), h);
|
||||
crypto::cn_slow_hash(context, job.blob.data(), job.blob.size(), h);
|
||||
if( ((uint32_t*)&h)[7] < job.target )
|
||||
{
|
||||
//found!
|
||||
|
|
83
src/node_rpc_proxy/InitState.h
Normal file
83
src/node_rpc_proxy/InitState.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "include_base_utils.h"
|
||||
|
||||
namespace tools {
|
||||
|
||||
class InitState {
|
||||
public:
|
||||
InitState() : m_state(STATE_NOT_INITIALIZED) {
|
||||
}
|
||||
|
||||
bool initialized() const volatile {
|
||||
return STATE_INITIALIZED == m_state.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
bool beginInit() volatile {
|
||||
State state = STATE_NOT_INITIALIZED;
|
||||
if (!m_state.compare_exchange_strong(state, STATE_INITIALIZING, std::memory_order_seq_cst)) {
|
||||
LOG_ERROR("object has been already initialized");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool endInit() volatile {
|
||||
State expectedState = STATE_INITIALIZING;
|
||||
if (!m_state.compare_exchange_strong(expectedState, STATE_INITIALIZED, std::memory_order_seq_cst)) {
|
||||
LOG_ERROR("Unexpected state: " << expectedState);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool beginShutdown() volatile {
|
||||
while (true) {
|
||||
State state = m_state.load(std::memory_order_relaxed);
|
||||
if (STATE_NOT_INITIALIZED == state) {
|
||||
return true;
|
||||
} else if (STATE_INITIALIZING == state) {
|
||||
LOG_ERROR("Object is being initialized");
|
||||
return false;
|
||||
} else if (STATE_INITIALIZED == state) {
|
||||
if (m_state.compare_exchange_strong(state, STATE_SHUTTING_DOWN, std::memory_order_seq_cst)) {
|
||||
return true;
|
||||
}
|
||||
} else if (STATE_SHUTTING_DOWN == state) {
|
||||
LOG_ERROR("Object is being shutting down");
|
||||
return false;
|
||||
} else {
|
||||
LOG_ERROR("Unknown state " << state);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool endShutdown() volatile {
|
||||
State expectedState = STATE_SHUTTING_DOWN;
|
||||
if (!m_state.compare_exchange_strong(expectedState, STATE_NOT_INITIALIZED, std::memory_order_seq_cst)) {
|
||||
LOG_ERROR("Unexpected state: " << expectedState);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
enum State {
|
||||
STATE_NOT_INITIALIZED,
|
||||
STATE_INITIALIZING,
|
||||
STATE_INITIALIZED,
|
||||
STATE_SHUTTING_DOWN
|
||||
};
|
||||
|
||||
private:
|
||||
std::atomic<State> m_state;
|
||||
};
|
||||
|
||||
}
|
13
src/node_rpc_proxy/NodeErrors.cpp
Normal file
13
src/node_rpc_proxy/NodeErrors.cpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "NodeErrors.h"
|
||||
|
||||
namespace cryptonote {
|
||||
namespace error {
|
||||
|
||||
NodeErrorCategory NodeErrorCategory::INSTANCE;
|
||||
|
||||
}
|
||||
}
|
56
src/node_rpc_proxy/NodeErrors.h
Normal file
56
src/node_rpc_proxy/NodeErrors.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
namespace cryptonote {
|
||||
namespace error {
|
||||
|
||||
// custom error conditions enum type:
|
||||
enum NodeErrorCodes {
|
||||
NOT_INITIALIZED = 1,
|
||||
ALREADY_INITIALIZED,
|
||||
NETWORK_ERROR,
|
||||
NODE_BUSY,
|
||||
INTERNAL_NODE_ERROR,
|
||||
};
|
||||
|
||||
// custom category:
|
||||
class NodeErrorCategory : public std::error_category {
|
||||
public:
|
||||
static NodeErrorCategory INSTANCE;
|
||||
|
||||
virtual const char* name() const throw() {
|
||||
return "NodeErrorCategory";
|
||||
}
|
||||
|
||||
virtual std::error_condition default_error_condition(int ev) const throw() {
|
||||
return std::error_condition(ev, *this);
|
||||
}
|
||||
|
||||
virtual std::string message(int ev) const {
|
||||
switch (ev) {
|
||||
case NOT_INITIALIZED: return "Object was not initialized";
|
||||
case ALREADY_INITIALIZED: return "Object has been already initialized";
|
||||
case NETWORK_ERROR: return "Network error";
|
||||
case NODE_BUSY: return "Node is busy";
|
||||
case INTERNAL_NODE_ERROR: return "Internal node error";
|
||||
default: return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
NodeErrorCategory() {
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
inline std::error_code make_error_code(cryptonote::error::NodeErrorCodes e) {
|
||||
return std::error_code(static_cast<int>(e), cryptonote::error::NodeErrorCategory::INSTANCE);
|
||||
}
|
255
src/node_rpc_proxy/NodeRpcProxy.cpp
Normal file
255
src/node_rpc_proxy/NodeRpcProxy.cpp
Normal file
|
@ -0,0 +1,255 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "NodeRpcProxy.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <system_error>
|
||||
#include <thread>
|
||||
|
||||
#include "cryptonote_core/cryptonote_format_utils.h"
|
||||
#include "rpc/core_rpc_server_commands_defs.h"
|
||||
#include "storages/http_abstract_invoke.h"
|
||||
#include "NodeErrors.h"
|
||||
|
||||
namespace cryptonote {
|
||||
|
||||
using namespace CryptoNote;
|
||||
|
||||
namespace {
|
||||
std::error_code interpretJsonRpcResponse(bool ok, const std::string& status) {
|
||||
if (!ok) {
|
||||
return make_error_code(error::NETWORK_ERROR);
|
||||
} else if (CORE_RPC_STATUS_BUSY == status) {
|
||||
return make_error_code(error::NODE_BUSY);
|
||||
} else if (CORE_RPC_STATUS_OK != status) {
|
||||
return make_error_code(error::INTERNAL_NODE_ERROR);
|
||||
}
|
||||
return std::error_code();
|
||||
}
|
||||
}
|
||||
|
||||
NodeRpcProxy::NodeRpcProxy(const std::string& nodeHost, unsigned short nodePort)
|
||||
: m_nodeAddress("http://" + nodeHost + ":" + std::to_string(nodePort))
|
||||
, m_rpcTimeout(10000)
|
||||
, m_pullTimer(m_ioService)
|
||||
, m_pullInterval(10000) {
|
||||
resetInternalState();
|
||||
}
|
||||
|
||||
NodeRpcProxy::~NodeRpcProxy() {
|
||||
shutdown();
|
||||
}
|
||||
|
||||
void NodeRpcProxy::resetInternalState() {
|
||||
m_ioService.reset();
|
||||
m_observerManager.clear();
|
||||
|
||||
m_peerCount = 0;
|
||||
m_nodeHeight = 0;
|
||||
m_networkHeight = 0;
|
||||
m_lastKnowHash = cryptonote::null_hash;
|
||||
}
|
||||
|
||||
void NodeRpcProxy::init(const INode::Callback& callback) {
|
||||
if (!m_initState.beginInit()) {
|
||||
callback(make_error_code(error::ALREADY_INITIALIZED));
|
||||
return;
|
||||
}
|
||||
|
||||
resetInternalState();
|
||||
m_workerThread = std::thread(std::bind(&NodeRpcProxy::workerThread, this, callback));
|
||||
}
|
||||
|
||||
bool NodeRpcProxy::shutdown() {
|
||||
if (!m_initState.beginShutdown()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boost::system::error_code ignored_ec;
|
||||
m_pullTimer.cancel(ignored_ec);
|
||||
m_ioService.stop();
|
||||
m_workerThread.join();
|
||||
|
||||
m_initState.endShutdown();
|
||||
return true;
|
||||
}
|
||||
|
||||
void NodeRpcProxy::workerThread(const INode::Callback& initialized_callback) {
|
||||
if (!m_initState.endInit()) {
|
||||
return;
|
||||
}
|
||||
|
||||
initialized_callback(std::error_code());
|
||||
|
||||
pullNodeStatusAndScheduleTheNext();
|
||||
|
||||
while (!m_ioService.stopped()) {
|
||||
m_ioService.run_one();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeRpcProxy::pullNodeStatusAndScheduleTheNext() {
|
||||
updateNodeStatus();
|
||||
|
||||
m_pullTimer.expires_from_now(boost::posix_time::milliseconds(m_pullInterval));
|
||||
m_pullTimer.async_wait([=](const boost::system::error_code& ec) {
|
||||
if (ec != boost::asio::error::operation_aborted) {
|
||||
pullNodeStatusAndScheduleTheNext();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void NodeRpcProxy::updateNodeStatus() {
|
||||
cryptonote::COMMAND_RPC_GET_LAST_BLOCK_HEADER::request req = AUTO_VAL_INIT(req);
|
||||
cryptonote::COMMAND_RPC_GET_LAST_BLOCK_HEADER::response rsp = AUTO_VAL_INIT(rsp);
|
||||
bool r = epee::net_utils::invoke_http_json_rpc(m_nodeAddress + "/json_rpc", "getlastblockheader", req, rsp, m_httpClient, m_rpcTimeout);
|
||||
std::error_code ec = interpretJsonRpcResponse(r, rsp.status);
|
||||
if (!ec) {
|
||||
crypto::hash blockHash;
|
||||
if (!parse_hash256(rsp.block_header.hash, blockHash)) {
|
||||
LOG_ERROR("Invalid block hash format: " << rsp.block_header.hash);
|
||||
return;
|
||||
}
|
||||
|
||||
if (blockHash != m_lastKnowHash) {
|
||||
m_lastKnowHash = blockHash;
|
||||
m_nodeHeight = rsp.block_header.height;
|
||||
// TODO request and update network height
|
||||
m_networkHeight = m_nodeHeight;
|
||||
m_observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, m_networkHeight);
|
||||
//if (m_networkHeight != rsp.block_header.network_height) {
|
||||
// m_networkHeight = rsp.block_header.network_height;
|
||||
// m_observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, m_networkHeight);
|
||||
//}
|
||||
m_observerManager.notify(&INodeObserver::localBlockchainUpdated, m_nodeHeight);
|
||||
}
|
||||
} else {
|
||||
LOG_PRINT_L2("Failed to invoke getlastblockheader: " << ec.message() << ':' << ec.value());
|
||||
}
|
||||
|
||||
updatePeerCount();
|
||||
}
|
||||
|
||||
void NodeRpcProxy::updatePeerCount() {
|
||||
cryptonote::COMMAND_RPC_GET_INFO::request req = AUTO_VAL_INIT(req);
|
||||
cryptonote::COMMAND_RPC_GET_INFO::response rsp = AUTO_VAL_INIT(rsp);
|
||||
bool r = epee::net_utils::invoke_http_json_remote_command2(m_nodeAddress + "/getinfo", req, rsp, m_httpClient, m_rpcTimeout);
|
||||
std::error_code ec = interpretJsonRpcResponse(r, rsp.status);
|
||||
if (!ec) {
|
||||
size_t peerCount = rsp.incoming_connections_count + rsp.outgoing_connections_count;
|
||||
if (peerCount != m_peerCount) {
|
||||
m_peerCount = peerCount;
|
||||
m_observerManager.notify(&INodeObserver::peerCountUpdated, m_peerCount);
|
||||
}
|
||||
} else {
|
||||
LOG_PRINT_L2("Failed to invoke getinfo: " << ec.message() << ':' << ec.value());
|
||||
}
|
||||
}
|
||||
|
||||
bool NodeRpcProxy::addObserver(INodeObserver* observer) {
|
||||
return m_observerManager.add(observer);
|
||||
}
|
||||
|
||||
bool NodeRpcProxy::removeObserver(INodeObserver* observer) {
|
||||
return m_observerManager.remove(observer);
|
||||
}
|
||||
|
||||
size_t NodeRpcProxy::getPeerCount() const {
|
||||
return m_peerCount;
|
||||
}
|
||||
|
||||
uint64_t NodeRpcProxy::getLastLocalBlockHeight() const {
|
||||
return m_nodeHeight;
|
||||
}
|
||||
|
||||
uint64_t NodeRpcProxy::getLastKnownBlockHeight() const {
|
||||
return m_networkHeight;
|
||||
}
|
||||
|
||||
void NodeRpcProxy::relayTransaction(const cryptonote::transaction& transaction, const Callback& callback) {
|
||||
if (!m_initState.initialized()) {
|
||||
callback(make_error_code(error::NOT_INITIALIZED));
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: m_ioService.stop() won't inkove callback(aborted). Fix it
|
||||
m_ioService.post(std::bind(&NodeRpcProxy::doRelayTransaction, this, transaction, callback));
|
||||
}
|
||||
|
||||
void NodeRpcProxy::getRandomOutsByAmounts(std::vector<uint64_t>&& amounts, uint64_t outsCount, std::vector<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount>& outs, const Callback& callback) {
|
||||
if (!m_initState.initialized()) {
|
||||
callback(make_error_code(error::NOT_INITIALIZED));
|
||||
return;
|
||||
}
|
||||
|
||||
m_ioService.post(std::bind(&NodeRpcProxy::doGetRandomOutsByAmounts, this, std::move(amounts), outsCount, std::ref(outs), callback));
|
||||
}
|
||||
|
||||
void NodeRpcProxy::getNewBlocks(std::list<crypto::hash>&& knownBlockIds, std::list<cryptonote::block_complete_entry>& newBlocks, uint64_t& startHeight, const Callback& callback) {
|
||||
if (!m_initState.initialized()) {
|
||||
callback(make_error_code(error::NOT_INITIALIZED));
|
||||
return;
|
||||
}
|
||||
|
||||
m_ioService.post(std::bind(&NodeRpcProxy::doGetNewBlocks, this, std::move(knownBlockIds), std::ref(newBlocks), std::ref(startHeight), callback));
|
||||
}
|
||||
|
||||
void NodeRpcProxy::getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector<uint64_t>& outsGlobalIndices, const Callback& callback) {
|
||||
if (!m_initState.initialized()) {
|
||||
callback(make_error_code(error::NOT_INITIALIZED));
|
||||
return;
|
||||
}
|
||||
|
||||
m_ioService.post(std::bind(&NodeRpcProxy::doGetTransactionOutsGlobalIndices, this, transactionHash, std::ref(outsGlobalIndices), callback));
|
||||
}
|
||||
|
||||
void NodeRpcProxy::doRelayTransaction(const cryptonote::transaction& transaction, const Callback& callback) {
|
||||
COMMAND_RPC_SEND_RAW_TX::request req;
|
||||
COMMAND_RPC_SEND_RAW_TX::response rsp;
|
||||
req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(cryptonote::tx_to_blob(transaction));
|
||||
bool r = epee::net_utils::invoke_http_json_remote_command2(m_nodeAddress + "/sendrawtransaction", req, rsp, m_httpClient, m_rpcTimeout);
|
||||
std::error_code ec = interpretJsonRpcResponse(r, rsp.status);
|
||||
callback(ec);
|
||||
}
|
||||
|
||||
void NodeRpcProxy::doGetRandomOutsByAmounts(std::vector<uint64_t>& amounts, uint64_t outsCount, std::vector<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount>& outs, const Callback& callback) {
|
||||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req = AUTO_VAL_INIT(req);
|
||||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response rsp = AUTO_VAL_INIT(rsp);
|
||||
req.amounts = std::move(amounts);
|
||||
req.outs_count = outsCount;
|
||||
bool r = epee::net_utils::invoke_http_bin_remote_command2(m_nodeAddress + "/getrandom_outs.bin", req, rsp, m_httpClient, m_rpcTimeout);
|
||||
std::error_code ec = interpretJsonRpcResponse(r, rsp.status);
|
||||
if (!ec) {
|
||||
outs = std::move(rsp.outs);
|
||||
}
|
||||
callback(ec);
|
||||
}
|
||||
|
||||
void NodeRpcProxy::doGetNewBlocks(std::list<crypto::hash>& knownBlockIds, std::list<cryptonote::block_complete_entry>& newBlocks, uint64_t& startHeight, const Callback& callback) {
|
||||
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req);
|
||||
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response rsp = AUTO_VAL_INIT(rsp);
|
||||
req.block_ids = std::move(knownBlockIds);
|
||||
bool r = epee::net_utils::invoke_http_bin_remote_command2(m_nodeAddress + "/getblocks.bin", req, rsp, m_httpClient, m_rpcTimeout);
|
||||
std::error_code ec = interpretJsonRpcResponse(r, rsp.status);
|
||||
if (!ec) {
|
||||
newBlocks = std::move(rsp.blocks);
|
||||
startHeight = rsp.start_height;
|
||||
}
|
||||
callback(ec);
|
||||
}
|
||||
|
||||
void NodeRpcProxy::doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector<uint64_t>& outsGlobalIndices, const Callback& callback) {
|
||||
cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request req = AUTO_VAL_INIT(req);
|
||||
cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response rsp = AUTO_VAL_INIT(rsp);
|
||||
req.txid = transactionHash;
|
||||
bool r = epee::net_utils::invoke_http_bin_remote_command2(m_nodeAddress + "/get_o_indexes.bin", req, rsp, m_httpClient, m_rpcTimeout);
|
||||
std::error_code ec = interpretJsonRpcResponse(r, rsp.status);
|
||||
if (!ec) {
|
||||
outsGlobalIndices = std::move(rsp.o_indexes);
|
||||
}
|
||||
callback(ec);
|
||||
}
|
||||
|
||||
}
|
76
src/node_rpc_proxy/NodeRpcProxy.h
Normal file
76
src/node_rpc_proxy/NodeRpcProxy.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <boost/asio/io_service.hpp>
|
||||
|
||||
#include "common/ObserverManager.h"
|
||||
#include "include_base_utils.h"
|
||||
#include "net/http_client.h"
|
||||
#include "InitState.h"
|
||||
#include "INode.h"
|
||||
|
||||
namespace cryptonote {
|
||||
|
||||
class NodeRpcProxy : public CryptoNote::INode {
|
||||
public:
|
||||
NodeRpcProxy(const std::string& nodeHost, unsigned short nodePort);
|
||||
virtual ~NodeRpcProxy();
|
||||
|
||||
virtual bool addObserver(CryptoNote::INodeObserver* observer);
|
||||
virtual bool removeObserver(CryptoNote::INodeObserver* observer);
|
||||
|
||||
virtual void init(const Callback& callback);
|
||||
virtual bool shutdown();
|
||||
|
||||
virtual size_t getPeerCount() const;
|
||||
virtual uint64_t getLastLocalBlockHeight() const;
|
||||
virtual uint64_t getLastKnownBlockHeight() const;
|
||||
|
||||
virtual void relayTransaction(const cryptonote::transaction& transaction, const Callback& callback);
|
||||
virtual void getRandomOutsByAmounts(std::vector<uint64_t>&& amounts, uint64_t outsCount, std::vector<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount>& result, const Callback& callback);
|
||||
virtual void getNewBlocks(std::list<crypto::hash>&& knownBlockIds, std::list<cryptonote::block_complete_entry>& newBlocks, uint64_t& startHeight, const Callback& callback);
|
||||
virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector<uint64_t>& outsGlobalIndices, const Callback& callback);
|
||||
|
||||
unsigned int rpcTimeout() const { return m_rpcTimeout; }
|
||||
void rpcTimeout(unsigned int val) { m_rpcTimeout = val; }
|
||||
|
||||
private:
|
||||
void resetInternalState();
|
||||
void workerThread(const Callback& initialized_callback);
|
||||
|
||||
void pullNodeStatusAndScheduleTheNext();
|
||||
void updateNodeStatus();
|
||||
void updatePeerCount();
|
||||
|
||||
void doRelayTransaction(const cryptonote::transaction& transaction, const Callback& callback);
|
||||
void doGetRandomOutsByAmounts(std::vector<uint64_t>& amounts, uint64_t outsCount, std::vector<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount>& result, const Callback& callback);
|
||||
void doGetNewBlocks(std::list<crypto::hash>& knownBlockIds, std::list<cryptonote::block_complete_entry>& newBlocks, uint64_t& startHeight, const Callback& callback);
|
||||
void doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector<uint64_t>& outsGlobalIndices, const Callback& callback);
|
||||
|
||||
private:
|
||||
tools::InitState m_initState;
|
||||
std::thread m_workerThread;
|
||||
boost::asio::io_service m_ioService;
|
||||
tools::ObserverManager<CryptoNote::INodeObserver> m_observerManager;
|
||||
|
||||
std::string m_nodeAddress;
|
||||
unsigned int m_rpcTimeout;
|
||||
epee::net_utils::http::http_simple_client m_httpClient;
|
||||
|
||||
boost::asio::deadline_timer m_pullTimer;
|
||||
uint64_t m_pullInterval;
|
||||
|
||||
// Internal state
|
||||
size_t m_peerCount;
|
||||
uint64_t m_nodeHeight;
|
||||
uint64_t m_networkHeight;
|
||||
crypto::hash m_lastKnowHash;
|
||||
};
|
||||
|
||||
}
|
|
@ -175,12 +175,7 @@ namespace cryptonote
|
|||
}
|
||||
std::list<crypto::hash> missed_txs;
|
||||
std::list<transaction> txs;
|
||||
bool r = m_core.get_transactions(vh, txs, missed_txs);
|
||||
if(!r)
|
||||
{
|
||||
res.status = "Failed";
|
||||
return true;
|
||||
}
|
||||
m_core.get_transactions(vh, txs, missed_txs);
|
||||
|
||||
BOOST_FOREACH(auto& tx, txs)
|
||||
{
|
||||
|
@ -415,12 +410,17 @@ namespace cryptonote
|
|||
}
|
||||
cryptonote::block_verification_context bvc = AUTO_VAL_INIT(bvc);
|
||||
m_core.handle_incoming_block(blockblob, bvc);
|
||||
if(!bvc.m_added_to_main_chain)
|
||||
{
|
||||
if (bvc.m_added_to_main_chain){
|
||||
block b = AUTO_VAL_INIT(b);
|
||||
parse_and_validate_block_from_blob(blockblob, b);
|
||||
|
||||
m_core.notify_new_block(b);
|
||||
} else {
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED;
|
||||
error_resp.message = "Block not accepted";
|
||||
return false;
|
||||
}
|
||||
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@ namespace cryptonote
|
|||
{
|
||||
struct request
|
||||
{
|
||||
std::list<uint64_t> amounts;
|
||||
std::vector<uint64_t> amounts;
|
||||
uint64_t outs_count;
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(amounts)
|
||||
|
@ -363,7 +363,11 @@ namespace cryptonote
|
|||
|
||||
struct COMMAND_RPC_GET_LAST_BLOCK_HEADER
|
||||
{
|
||||
typedef std::list<std::string> request;
|
||||
struct request
|
||||
{
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct response
|
||||
{
|
||||
|
|
|
@ -64,19 +64,6 @@ namespace
|
|||
return err;
|
||||
}
|
||||
|
||||
bool parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id)
|
||||
{
|
||||
blobdata payment_id_data;
|
||||
if(!string_tools::parse_hexstr_to_binbuff(payment_id_str, payment_id_data))
|
||||
return false;
|
||||
|
||||
if(sizeof(crypto::hash) != payment_id_data.size())
|
||||
return false;
|
||||
|
||||
payment_id = *reinterpret_cast<const crypto::hash*>(payment_id_data.data());
|
||||
return true;
|
||||
}
|
||||
|
||||
class message_writer
|
||||
{
|
||||
public:
|
||||
|
@ -448,9 +435,9 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args)
|
|||
}
|
||||
else if (1 == args.size())
|
||||
{
|
||||
uint16_t num;
|
||||
uint16_t num = 1;
|
||||
ok = string_tools::get_xtype_from_string(num, args[0]);
|
||||
ok &= (1 <= num && num <= max_mining_threads_count);
|
||||
ok = ok && (1 <= num && num <= max_mining_threads_count);
|
||||
req.threads_count = num;
|
||||
}
|
||||
else
|
||||
|
@ -663,7 +650,7 @@ bool simple_wallet::show_payments(const std::vector<std::string> &args)
|
|||
for(std::string arg : args)
|
||||
{
|
||||
crypto::hash payment_id;
|
||||
if(parse_payment_id(arg, payment_id))
|
||||
if (tools::wallet2::parse_payment_id(arg, payment_id))
|
||||
{
|
||||
std::list<tools::wallet2::payment_details> payments;
|
||||
m_wallet->get_payments(payment_id, payments);
|
||||
|
@ -746,7 +733,7 @@ bool simple_wallet::transfer(const std::vector<std::string> &args_)
|
|||
local_args.pop_back();
|
||||
|
||||
crypto::hash payment_id;
|
||||
bool r = parse_payment_id(payment_id_str, payment_id);
|
||||
bool r = tools::wallet2::parse_payment_id(payment_id_str, payment_id);
|
||||
if(r)
|
||||
{
|
||||
std::string extra_nonce;
|
||||
|
@ -785,7 +772,7 @@ bool simple_wallet::transfer(const std::vector<std::string> &args_)
|
|||
try
|
||||
{
|
||||
cryptonote::transaction tx;
|
||||
m_wallet->transfer(dsts, fake_outs_count, 0, DEFAULT_FEE, extra, tx);
|
||||
m_wallet->transfer(dsts, fake_outs_count, 0, MINIMUM_FEE, extra, tx);
|
||||
success_msg_writer(true) << "Money successfully sent, transaction " << get_transaction_hash(tx);
|
||||
}
|
||||
catch (const tools::error::daemon_busy&)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#define BUILD_COMMIT_ID "@VERSION@"
|
||||
#define PROJECT_VERSION "0.8.10"
|
||||
#define PROJECT_VERSION_BUILD_NO "71"
|
||||
#define PROJECT_VERSION "0.8.11"
|
||||
#define PROJECT_VERSION_BUILD_NO "65"
|
||||
#define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")"
|
||||
|
|
491
src/wallet/Wallet.cpp
Normal file
491
src/wallet/Wallet.cpp
Normal file
|
@ -0,0 +1,491 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "Wallet.h"
|
||||
#include "wallet_errors.h"
|
||||
#include "string_tools.h"
|
||||
#include "serialization/binary_utils.h"
|
||||
#include "storages/portable_storage_template_helper.h"
|
||||
#include "WalletUtils.h"
|
||||
|
||||
#include <exception>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "WalletSerialization.h"
|
||||
#include <boost/archive/binary_oarchive.hpp>
|
||||
#include <boost/archive/binary_iarchive.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
void throwNotDefined() {
|
||||
throw std::runtime_error("The behavior is not defined!");
|
||||
}
|
||||
|
||||
bool verifyKeys(const crypto::secret_key& sec, const crypto::public_key& expected_pub) {
|
||||
crypto::public_key pub;
|
||||
bool r = crypto::secret_key_to_public_key(sec, pub);
|
||||
return r && expected_pub == pub;
|
||||
}
|
||||
|
||||
void throwIfKeysMissmatch(const crypto::secret_key& sec, const crypto::public_key& expected_pub) {
|
||||
if (!verifyKeys(sec, expected_pub))
|
||||
throw std::system_error(make_error_code(cryptonote::error::WRONG_PASSWORD));
|
||||
}
|
||||
|
||||
class ContextCounterHolder
|
||||
{
|
||||
public:
|
||||
ContextCounterHolder(CryptoNote::WalletAsyncContextCounter& shutdowner) : m_shutdowner(shutdowner) {}
|
||||
~ContextCounterHolder() { m_shutdowner.delAsyncContext(); }
|
||||
|
||||
private:
|
||||
CryptoNote::WalletAsyncContextCounter& m_shutdowner;
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
void runAtomic(std::mutex& mutex, F f) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
f();
|
||||
}
|
||||
} //namespace
|
||||
|
||||
namespace CryptoNote {
|
||||
|
||||
Wallet::Wallet(INode& node) :
|
||||
m_state(NOT_INITIALIZED),
|
||||
m_node(node),
|
||||
m_isSynchronizing(false),
|
||||
m_isStopping(false),
|
||||
m_transferDetails(m_blockchain),
|
||||
m_transactionsCache(m_sendingTxsStates),
|
||||
m_synchronizer(m_account, m_node, m_blockchain, m_transferDetails, m_unconfirmedTransactions, m_transactionsCache),
|
||||
m_sender(m_transactionsCache, m_sendingTxsStates, m_transferDetails, m_unconfirmedTransactions) {
|
||||
m_autoRefresher.reset(new WalletNodeObserver(this));
|
||||
}
|
||||
|
||||
void Wallet::addObserver(IWalletObserver* observer) {
|
||||
m_observerManager.add(observer);
|
||||
}
|
||||
|
||||
void Wallet::removeObserver(IWalletObserver* observer) {
|
||||
m_observerManager.remove(observer);
|
||||
}
|
||||
|
||||
void Wallet::initAndGenerate(const std::string& password) {
|
||||
{
|
||||
std::unique_lock<std::mutex> stateLock(m_cacheMutex);
|
||||
|
||||
if (m_state != NOT_INITIALIZED) {
|
||||
throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED));
|
||||
}
|
||||
|
||||
m_node.addObserver(m_autoRefresher.get());
|
||||
|
||||
m_account.generate();
|
||||
m_password = password;
|
||||
|
||||
m_sender.init(m_account.get_keys());
|
||||
|
||||
storeGenesisBlock();
|
||||
|
||||
m_state = INITIALIZED;
|
||||
}
|
||||
|
||||
m_observerManager.notify(&IWalletObserver::initCompleted, std::error_code());
|
||||
refresh();
|
||||
}
|
||||
|
||||
void Wallet::storeGenesisBlock() {
|
||||
cryptonote::block b;
|
||||
cryptonote::generate_genesis_block(b);
|
||||
m_blockchain.push_back(get_block_hash(b));
|
||||
}
|
||||
|
||||
void Wallet::initAndLoad(std::istream& source, const std::string& password) {
|
||||
std::unique_lock<std::mutex> stateLock(m_cacheMutex);
|
||||
|
||||
if (m_state != NOT_INITIALIZED) {
|
||||
throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED));
|
||||
}
|
||||
|
||||
m_node.addObserver(m_autoRefresher.get());
|
||||
|
||||
m_password = password;
|
||||
m_state = LOADING;
|
||||
|
||||
std::thread loader(&Wallet::doLoad, this, std::ref(source));
|
||||
loader.detach();
|
||||
}
|
||||
|
||||
void Wallet::doLoad(std::istream& source) {
|
||||
try
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_cacheMutex);
|
||||
|
||||
boost::archive::binary_iarchive ar(source);
|
||||
|
||||
crypto::chacha8_iv iv;
|
||||
std::string chacha_str;;
|
||||
ar >> chacha_str;
|
||||
|
||||
::serialization::parse_binary(chacha_str, iv);
|
||||
|
||||
std::string cipher;
|
||||
ar >> cipher;
|
||||
|
||||
std::string plain;
|
||||
decrypt(cipher, plain, iv, m_password);
|
||||
|
||||
std::stringstream restore(plain);
|
||||
|
||||
try
|
||||
{
|
||||
//boost archive ctor throws an exception if password is wrong (i.e. there's garbage in a stream)
|
||||
boost::archive::binary_iarchive dataArchive(restore);
|
||||
|
||||
dataArchive >> m_account;
|
||||
|
||||
throwIfKeysMissmatch(m_account.get_keys().m_view_secret_key, m_account.get_keys().m_account_address.m_view_public_key);
|
||||
throwIfKeysMissmatch(m_account.get_keys().m_spend_secret_key, m_account.get_keys().m_account_address.m_spend_public_key);
|
||||
|
||||
dataArchive >> m_blockchain;
|
||||
|
||||
m_transferDetails.load(dataArchive);
|
||||
m_unconfirmedTransactions.load(dataArchive);
|
||||
m_transactionsCache.load(dataArchive);
|
||||
}
|
||||
catch (std::exception&) {
|
||||
throw std::system_error(make_error_code(cryptonote::error::WRONG_PASSWORD));
|
||||
}
|
||||
|
||||
m_sender.init(m_account.get_keys());
|
||||
}
|
||||
catch (std::system_error& e) {
|
||||
runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::NOT_INITIALIZED;} );
|
||||
m_observerManager.notify(&IWalletObserver::initCompleted, e.code());
|
||||
return;
|
||||
}
|
||||
catch (std::exception&) {
|
||||
runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::NOT_INITIALIZED;} );
|
||||
m_observerManager.notify(&IWalletObserver::initCompleted, make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::INITIALIZED;} );
|
||||
|
||||
m_observerManager.notify(&IWalletObserver::initCompleted, std::error_code());
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
void Wallet::decrypt(const std::string& cipher, std::string& plain, crypto::chacha8_iv iv, const std::string& password) {
|
||||
crypto::chacha8_key key;
|
||||
crypto::cn_context context;
|
||||
crypto::generate_chacha8_key(context, password, key);
|
||||
|
||||
plain.resize(cipher.size());
|
||||
|
||||
crypto::chacha8(cipher.data(), cipher.size(), key, iv, &plain[0]);
|
||||
}
|
||||
|
||||
void Wallet::shutdown() {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_cacheMutex);
|
||||
|
||||
if (m_isStopping)
|
||||
throwNotDefined();
|
||||
|
||||
m_isStopping = true;
|
||||
|
||||
if (m_state == NOT_INITIALIZED)
|
||||
throwNotDefined();
|
||||
|
||||
m_sender.stop();
|
||||
m_synchronizer.stop();
|
||||
}
|
||||
|
||||
m_asyncContextCounter.waitAsyncContextsFinish();
|
||||
m_node.removeObserver(m_autoRefresher.get());
|
||||
}
|
||||
|
||||
void Wallet::save(std::ostream& destination, bool saveDetailed, bool saveCache) {
|
||||
if(m_isStopping) {
|
||||
m_observerManager.notify(&IWalletObserver::saveCompleted, make_error_code(cryptonote::error::OPERATION_CANCELLED));
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_cacheMutex);
|
||||
|
||||
throwIf(m_state != INITIALIZED, cryptonote::error::WRONG_STATE);
|
||||
|
||||
m_state = SAVING;
|
||||
}
|
||||
|
||||
m_asyncContextCounter.addAsyncContext();
|
||||
std::thread saver(&Wallet::doSave, this, std::ref(destination), saveDetailed, saveCache);
|
||||
saver.detach();
|
||||
}
|
||||
|
||||
void Wallet::doSave(std::ostream& destination, bool saveDetailed, bool saveCache) {
|
||||
ContextCounterHolder counterHolder(m_asyncContextCounter);
|
||||
|
||||
try {
|
||||
//TODO: exception safety: leave destination stream empty in case of errors
|
||||
boost::archive::binary_oarchive ar(destination);
|
||||
|
||||
std::stringstream original;
|
||||
std::unique_lock<std::mutex> lock(m_cacheMutex);
|
||||
|
||||
boost::archive::binary_oarchive archive(original);
|
||||
|
||||
archive << m_account;
|
||||
|
||||
const BlockchainContainer& blockchain = saveCache ? m_blockchain : BlockchainContainer();
|
||||
|
||||
archive << blockchain;
|
||||
|
||||
m_transferDetails.save(archive, saveCache);
|
||||
m_unconfirmedTransactions.save(archive, saveCache);
|
||||
m_transactionsCache.save(archive, saveDetailed, saveCache);
|
||||
|
||||
std::string plain = original.str();
|
||||
std::string cipher;
|
||||
|
||||
crypto::chacha8_iv iv = encrypt(plain, cipher);
|
||||
|
||||
std::string chacha_str;
|
||||
::serialization::dump_binary(iv, chacha_str);
|
||||
ar << chacha_str;
|
||||
ar << cipher;
|
||||
|
||||
m_state = INITIALIZED;
|
||||
}
|
||||
catch (std::system_error& e) {
|
||||
runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::INITIALIZED;} );
|
||||
m_observerManager.notify(&IWalletObserver::saveCompleted, e.code());
|
||||
return;
|
||||
}
|
||||
catch (std::exception&) {
|
||||
runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::INITIALIZED;} );
|
||||
m_observerManager.notify(&IWalletObserver::saveCompleted, make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
m_observerManager.notify(&IWalletObserver::saveCompleted, std::error_code());
|
||||
}
|
||||
|
||||
crypto::chacha8_iv Wallet::encrypt(const std::string& plain, std::string& cipher) {
|
||||
crypto::chacha8_key key;
|
||||
crypto::cn_context context;
|
||||
crypto::generate_chacha8_key(context, m_password, key);
|
||||
|
||||
cipher.resize(plain.size());
|
||||
|
||||
crypto::chacha8_iv iv = crypto::rand<crypto::chacha8_iv>();
|
||||
crypto::chacha8(plain.data(), plain.size(), key, iv, &cipher[0]);
|
||||
|
||||
return iv;
|
||||
}
|
||||
|
||||
std::error_code Wallet::changePassword(const std::string& oldPassword, const std::string& newPassword) {
|
||||
std::unique_lock<std::mutex> passLock(m_cacheMutex);
|
||||
|
||||
throwIfNotInitialised();
|
||||
|
||||
if (m_password.compare(oldPassword))
|
||||
return make_error_code(cryptonote::error::WRONG_PASSWORD);
|
||||
|
||||
//we don't let the user to change the password while saving
|
||||
m_password = newPassword;
|
||||
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
std::string Wallet::getAddress() {
|
||||
std::unique_lock<std::mutex> lock(m_cacheMutex);
|
||||
throwIfNotInitialised();
|
||||
|
||||
return m_account.get_public_address_str();
|
||||
}
|
||||
|
||||
uint64_t Wallet::actualBalance() {
|
||||
std::unique_lock<std::mutex> lock(m_cacheMutex);
|
||||
throwIfNotInitialised();
|
||||
|
||||
return m_transferDetails.countActualBalance();
|
||||
}
|
||||
|
||||
uint64_t Wallet::pendingBalance() {
|
||||
std::unique_lock<std::mutex> lock(m_cacheMutex);
|
||||
throwIfNotInitialised();
|
||||
|
||||
return doPendingBalance();
|
||||
}
|
||||
|
||||
uint64_t Wallet::doPendingBalance() {
|
||||
uint64_t amount = 0;
|
||||
amount = m_transferDetails.countPendingBalance();
|
||||
amount += m_unconfirmedTransactions.countPendingBalance();
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
size_t Wallet::getTransactionCount() {
|
||||
std::unique_lock<std::mutex> lock(m_cacheMutex);
|
||||
throwIfNotInitialised();
|
||||
|
||||
return m_transactionsCache.getTransactionCount();
|
||||
}
|
||||
|
||||
size_t Wallet::getTransferCount() {
|
||||
std::unique_lock<std::mutex> lock(m_cacheMutex);
|
||||
throwIfNotInitialised();
|
||||
|
||||
return m_transactionsCache.getTransferCount();
|
||||
}
|
||||
|
||||
TransactionId Wallet::findTransactionByTransferId(TransferId transferId) {
|
||||
std::unique_lock<std::mutex> lock(m_cacheMutex);
|
||||
throwIfNotInitialised();
|
||||
|
||||
return m_transactionsCache.findTransactionByTransferId(transferId);
|
||||
}
|
||||
|
||||
bool Wallet::getTransaction(TransactionId transactionId, Transaction& transaction) {
|
||||
std::unique_lock<std::mutex> lock(m_cacheMutex);
|
||||
throwIfNotInitialised();
|
||||
|
||||
return m_transactionsCache.getTransaction(transactionId, transaction);
|
||||
}
|
||||
|
||||
bool Wallet::getTransfer(TransferId transferId, Transfer& transfer) {
|
||||
std::unique_lock<std::mutex> lock(m_cacheMutex);
|
||||
throwIfNotInitialised();
|
||||
|
||||
return m_transactionsCache.getTransfer(transferId, transfer);
|
||||
}
|
||||
|
||||
TransactionId Wallet::sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) {
|
||||
std::vector<Transfer> transfers;
|
||||
transfers.push_back(transfer);
|
||||
|
||||
return sendTransaction(transfers, fee, extra, mixIn, unlockTimestamp);
|
||||
}
|
||||
|
||||
TransactionId Wallet::sendTransaction(const std::vector<Transfer>& transfers, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) {
|
||||
TransactionId txId = 0;
|
||||
std::shared_ptr<WalletRequest> request;
|
||||
std::deque<std::shared_ptr<WalletEvent> > events;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_cacheMutex);
|
||||
request = m_sender.makeSendRequest(txId, events, transfers, fee, extra, mixIn, unlockTimestamp);
|
||||
}
|
||||
|
||||
notifyClients(events);
|
||||
|
||||
if (request) {
|
||||
m_asyncContextCounter.addAsyncContext();
|
||||
request->perform(m_node, std::bind(&Wallet::sendTransactionCallback, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
return txId;
|
||||
}
|
||||
|
||||
void Wallet::sendTransactionCallback(WalletRequest::Callback callback, std::error_code ec) {
|
||||
ContextCounterHolder counterHolder(m_asyncContextCounter);
|
||||
std::deque<std::shared_ptr<WalletEvent> > events;
|
||||
|
||||
boost::optional<std::shared_ptr<WalletRequest> > nextRequest;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_cacheMutex);
|
||||
callback(events, nextRequest, ec);
|
||||
}
|
||||
|
||||
notifyClients(events);
|
||||
|
||||
if (nextRequest) {
|
||||
m_asyncContextCounter.addAsyncContext();
|
||||
(*nextRequest)->perform(m_node, std::bind(&Wallet::synchronizationCallback, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Wallet::synchronizationCallback(WalletRequest::Callback callback, std::error_code ec) {
|
||||
ContextCounterHolder counterHolder(m_asyncContextCounter);
|
||||
|
||||
std::deque<std::shared_ptr<WalletEvent> > events;
|
||||
boost::optional<std::shared_ptr<WalletRequest> > nextRequest;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_cacheMutex);
|
||||
callback(events, nextRequest, ec);
|
||||
|
||||
if (!nextRequest)
|
||||
m_isSynchronizing = false;
|
||||
}
|
||||
|
||||
notifyClients(events);
|
||||
|
||||
if (nextRequest) {
|
||||
m_asyncContextCounter.addAsyncContext();
|
||||
(*nextRequest)->perform(m_node, std::bind(&Wallet::synchronizationCallback, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
}
|
||||
|
||||
std::error_code Wallet::cancelTransaction(size_t transactionId) {
|
||||
return make_error_code(cryptonote::error::TX_CANCEL_IMPOSSIBLE);
|
||||
}
|
||||
|
||||
void Wallet::throwIfNotInitialised() {
|
||||
if (m_state == NOT_INITIALIZED || m_state == LOADING)
|
||||
throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED));
|
||||
}
|
||||
|
||||
void Wallet::startRefresh() {
|
||||
refresh();
|
||||
}
|
||||
|
||||
void Wallet::refresh() {
|
||||
if (m_isStopping)
|
||||
return;
|
||||
|
||||
std::shared_ptr<WalletRequest> req;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_cacheMutex);
|
||||
|
||||
if (m_state != INITIALIZED) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_isSynchronizing) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_isSynchronizing = true;
|
||||
|
||||
req = m_synchronizer.makeStartRefreshRequest();
|
||||
}
|
||||
|
||||
m_asyncContextCounter.addAsyncContext();
|
||||
req->perform(m_node, std::bind(&Wallet::synchronizationCallback, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void Wallet::notifyClients(std::deque<std::shared_ptr<WalletEvent> >& events) {
|
||||
while (!events.empty()) {
|
||||
std::shared_ptr<WalletEvent> event = events.front();
|
||||
event->notify(m_observerManager);
|
||||
events.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace CryptoNote
|
124
src/wallet/Wallet.h
Normal file
124
src/wallet/Wallet.h
Normal file
|
@ -0,0 +1,124 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "IWallet.h"
|
||||
#include "INode.h"
|
||||
#include "WalletErrors.h"
|
||||
#include "WalletAsyncContextCounter.h"
|
||||
#include "WalletTxSendingState.h"
|
||||
#include "common/ObserverManager.h"
|
||||
#include "cryptonote_core/account.h"
|
||||
#include "cryptonote_core/tx_extra.h"
|
||||
#include "cryptonote_core/cryptonote_format_utils.h"
|
||||
#include "WalletTransferDetails.h"
|
||||
#include "WalletUserTransactionsCache.h"
|
||||
#include "WalletUnconfirmedTransactions.h"
|
||||
#include "WalletSynchronizer.h"
|
||||
#include "WalletTransactionSender.h"
|
||||
#include "WalletRequest.h"
|
||||
|
||||
namespace CryptoNote {
|
||||
|
||||
class Wallet : public IWallet {
|
||||
public:
|
||||
Wallet(INode& node);
|
||||
~Wallet() {};
|
||||
|
||||
virtual void addObserver(IWalletObserver* observer);
|
||||
virtual void removeObserver(IWalletObserver* observer);
|
||||
|
||||
virtual void initAndGenerate(const std::string& password);
|
||||
virtual void initAndLoad(std::istream& source, const std::string& password);
|
||||
virtual void shutdown();
|
||||
|
||||
virtual void save(std::ostream& destination, bool saveDetailed = true, bool saveCache = true);
|
||||
|
||||
virtual std::error_code changePassword(const std::string& oldPassword, const std::string& newPassword);
|
||||
|
||||
virtual std::string getAddress();
|
||||
|
||||
virtual uint64_t actualBalance();
|
||||
virtual uint64_t pendingBalance();
|
||||
|
||||
virtual size_t getTransactionCount();
|
||||
virtual size_t getTransferCount();
|
||||
|
||||
virtual TransactionId findTransactionByTransferId(TransferId transferId);
|
||||
|
||||
virtual bool getTransaction(TransactionId transactionId, Transaction& transaction);
|
||||
virtual bool getTransfer(TransferId transferId, Transfer& transfer);
|
||||
|
||||
virtual TransactionId sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0);
|
||||
virtual TransactionId sendTransaction(const std::vector<Transfer>& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0);
|
||||
virtual std::error_code cancelTransaction(size_t transactionId);
|
||||
|
||||
void startRefresh();
|
||||
|
||||
private:
|
||||
void throwIfNotInitialised();
|
||||
void refresh();
|
||||
uint64_t doPendingBalance();
|
||||
|
||||
void doSave(std::ostream& destination, bool saveDetailed, bool saveCache);
|
||||
void doLoad(std::istream& source);
|
||||
|
||||
crypto::chacha8_iv encrypt(const std::string& plain, std::string& cipher);
|
||||
void decrypt(const std::string& cipher, std::string& plain, crypto::chacha8_iv iv, const std::string& password);
|
||||
|
||||
void synchronizationCallback(WalletRequest::Callback callback, std::error_code ec);
|
||||
void sendTransactionCallback(WalletRequest::Callback callback, std::error_code ec);
|
||||
void notifyClients(std::deque<std::shared_ptr<WalletEvent> >& events);
|
||||
|
||||
void storeGenesisBlock();
|
||||
|
||||
enum WalletState
|
||||
{
|
||||
NOT_INITIALIZED = 0,
|
||||
INITIALIZED,
|
||||
LOADING,
|
||||
SAVING
|
||||
};
|
||||
|
||||
WalletState m_state;
|
||||
std::mutex m_cacheMutex;
|
||||
cryptonote::account_base m_account;
|
||||
std::string m_password;
|
||||
INode& m_node;
|
||||
bool m_isSynchronizing;
|
||||
bool m_isStopping;
|
||||
|
||||
typedef std::vector<crypto::hash> BlockchainContainer;
|
||||
|
||||
BlockchainContainer m_blockchain;
|
||||
WalletTransferDetails m_transferDetails;
|
||||
WalletUnconfirmedTransactions m_unconfirmedTransactions;
|
||||
tools::ObserverManager<CryptoNote::IWalletObserver> m_observerManager;
|
||||
|
||||
WalletTxSendingState m_sendingTxsStates;
|
||||
WalletUserTransactionsCache m_transactionsCache;
|
||||
|
||||
struct WalletNodeObserver: public INodeObserver
|
||||
{
|
||||
WalletNodeObserver(Wallet* wallet) : m_wallet(wallet) {}
|
||||
virtual void lastKnownBlockHeightUpdated(uint64_t height) { m_wallet->startRefresh(); }
|
||||
|
||||
Wallet* m_wallet;
|
||||
};
|
||||
|
||||
std::unique_ptr<WalletNodeObserver> m_autoRefresher;
|
||||
WalletAsyncContextCounter m_asyncContextCounter;
|
||||
|
||||
WalletSynchronizer m_synchronizer;
|
||||
WalletTransactionSender m_sender;
|
||||
};
|
||||
|
||||
} //namespace CryptoNote
|
29
src/wallet/WalletAsyncContextCounter.cpp
Normal file
29
src/wallet/WalletAsyncContextCounter.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "WalletAsyncContextCounter.h"
|
||||
|
||||
namespace CryptoNote {
|
||||
|
||||
void WalletAsyncContextCounter::addAsyncContext() {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
m_asyncContexts++;
|
||||
}
|
||||
|
||||
void WalletAsyncContextCounter::delAsyncContext() {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
m_asyncContexts--;
|
||||
|
||||
if (!m_asyncContexts) m_cv.notify_one();
|
||||
}
|
||||
|
||||
void WalletAsyncContextCounter::waitAsyncContextsFinish() {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
while (m_asyncContexts)
|
||||
m_cv.wait(lock);
|
||||
}
|
||||
|
||||
} //namespace CryptoNote
|
||||
|
||||
|
30
src/wallet/WalletAsyncContextCounter.h
Normal file
30
src/wallet/WalletAsyncContextCounter.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace CryptoNote {
|
||||
|
||||
class WalletAsyncContextCounter
|
||||
{
|
||||
public:
|
||||
WalletAsyncContextCounter() : m_asyncContexts(0) {}
|
||||
|
||||
void addAsyncContext();
|
||||
void delAsyncContext();
|
||||
|
||||
//returns true if contexts are finished before timeout
|
||||
void waitAsyncContextsFinish();
|
||||
|
||||
private:
|
||||
uint32_t m_asyncContexts;
|
||||
std::condition_variable m_cv;
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
|
||||
} //namespace CryptoNote
|
13
src/wallet/WalletErrors.cpp
Normal file
13
src/wallet/WalletErrors.cpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "WalletErrors.h"
|
||||
|
||||
namespace cryptonote {
|
||||
namespace error {
|
||||
|
||||
WalletErrorCategory WalletErrorCategory::INSTANCE;
|
||||
|
||||
}
|
||||
}
|
73
src/wallet/WalletErrors.h
Normal file
73
src/wallet/WalletErrors.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
namespace cryptonote {
|
||||
namespace error {
|
||||
|
||||
// custom error conditions enum type:
|
||||
enum WalletErrorCodes {
|
||||
NOT_INITIALIZED = 1,
|
||||
ALREADY_INITIALIZED,
|
||||
WRONG_STATE,
|
||||
WRONG_PASSWORD,
|
||||
INTERNAL_WALLET_ERROR,
|
||||
MIXIN_COUNT_TOO_BIG,
|
||||
BAD_ADDRESS,
|
||||
TRANSACTION_SIZE_TOO_BIG,
|
||||
WRONG_AMOUNT,
|
||||
SUM_OVERFLOW,
|
||||
ZERO_DESTINATION,
|
||||
TX_CANCEL_IMPOSSIBLE,
|
||||
TX_CANCELLED,
|
||||
OPERATION_CANCELLED
|
||||
};
|
||||
|
||||
// custom category:
|
||||
class WalletErrorCategory : public std::error_category {
|
||||
public:
|
||||
static WalletErrorCategory INSTANCE;
|
||||
|
||||
virtual const char* name() const throw() {
|
||||
return "WalletErrorCategory";
|
||||
}
|
||||
|
||||
virtual std::error_condition default_error_condition(int ev) const throw() {
|
||||
return std::error_condition(ev, *this);
|
||||
}
|
||||
|
||||
virtual std::string message(int ev) const {
|
||||
switch (ev) {
|
||||
case NOT_INITIALIZED: return "Object was not initialized";
|
||||
case WRONG_PASSWORD: return "The password is wrong";
|
||||
case ALREADY_INITIALIZED: return "The object is already initialized";
|
||||
case INTERNAL_WALLET_ERROR: return "Internal error occured";
|
||||
case MIXIN_COUNT_TOO_BIG: return "MixIn count is too big";
|
||||
case BAD_ADDRESS: return "Bad address";
|
||||
case TRANSACTION_SIZE_TOO_BIG: return "Transaction size is too big";
|
||||
case WRONG_AMOUNT: return "Wrong amount";
|
||||
case SUM_OVERFLOW: return "Sum overflow";
|
||||
case ZERO_DESTINATION: return "The destination is empty";
|
||||
case TX_CANCEL_IMPOSSIBLE: return "Impossible to cancel transaction";
|
||||
case WRONG_STATE: return "The wallet is in wrong state (maybe loading or saving), try again later";
|
||||
case OPERATION_CANCELLED: return "The operation you've requested has been cancelled";
|
||||
default: return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
WalletErrorCategory() {
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
inline std::error_code make_error_code(cryptonote::error::WalletErrorCodes e) {
|
||||
return std::error_code(static_cast<int>(e), cryptonote::error::WalletErrorCategory::INSTANCE);
|
||||
}
|
113
src/wallet/WalletEvent.h
Normal file
113
src/wallet/WalletEvent.h
Normal file
|
@ -0,0 +1,113 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IWallet.h"
|
||||
#include "common/ObserverManager.h"
|
||||
|
||||
namespace CryptoNote
|
||||
{
|
||||
|
||||
class WalletEvent
|
||||
{
|
||||
public:
|
||||
virtual ~WalletEvent() {};
|
||||
|
||||
virtual void notify(tools::ObserverManager<CryptoNote::IWalletObserver>& observer) = 0;
|
||||
};
|
||||
|
||||
class WalletTransactionUpdatedEvent : public WalletEvent
|
||||
{
|
||||
public:
|
||||
WalletTransactionUpdatedEvent(TransactionId transactionId) : m_id(transactionId) {};
|
||||
virtual ~WalletTransactionUpdatedEvent() {};
|
||||
|
||||
virtual void notify(tools::ObserverManager<CryptoNote::IWalletObserver>& observer)
|
||||
{
|
||||
observer.notify(&IWalletObserver::transactionUpdated, m_id);
|
||||
}
|
||||
|
||||
private:
|
||||
TransactionId m_id;
|
||||
};
|
||||
|
||||
class WalletSendTransactionCompletedEvent : public WalletEvent
|
||||
{
|
||||
public:
|
||||
WalletSendTransactionCompletedEvent(TransactionId transactionId, std::error_code result) : m_id(transactionId), m_error(result) {};
|
||||
virtual ~WalletSendTransactionCompletedEvent() {};
|
||||
|
||||
virtual void notify(tools::ObserverManager<CryptoNote::IWalletObserver>& observer)
|
||||
{
|
||||
observer.notify(&IWalletObserver::sendTransactionCompleted, m_id, m_error);
|
||||
}
|
||||
|
||||
private:
|
||||
TransactionId m_id;
|
||||
std::error_code m_error;
|
||||
};
|
||||
|
||||
class WalletExternalTransactionCreatedEvent : public WalletEvent
|
||||
{
|
||||
public:
|
||||
WalletExternalTransactionCreatedEvent(TransactionId transactionId) : m_id(transactionId) {};
|
||||
virtual ~WalletExternalTransactionCreatedEvent() {};
|
||||
|
||||
virtual void notify(tools::ObserverManager<CryptoNote::IWalletObserver>& observer)
|
||||
{
|
||||
observer.notify(&IWalletObserver::externalTransactionCreated, m_id);
|
||||
}
|
||||
private:
|
||||
TransactionId m_id;
|
||||
};
|
||||
|
||||
class WalletSynchronizationProgressUpdatedEvent : public WalletEvent
|
||||
{
|
||||
public:
|
||||
WalletSynchronizationProgressUpdatedEvent(uint64_t current, uint64_t total, std::error_code result) : m_current(current), m_total(total), m_ec(result) {};
|
||||
virtual ~WalletSynchronizationProgressUpdatedEvent() {};
|
||||
|
||||
virtual void notify(tools::ObserverManager<CryptoNote::IWalletObserver>& observer)
|
||||
{
|
||||
observer.notify(&IWalletObserver::synchronizationProgressUpdated, m_current, m_total, m_ec);
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t m_current;
|
||||
uint64_t m_total;
|
||||
std::error_code m_ec;
|
||||
};
|
||||
|
||||
|
||||
class WalletActualBalanceUpdatedEvent : public WalletEvent
|
||||
{
|
||||
public:
|
||||
WalletActualBalanceUpdatedEvent(uint64_t balance) : m_balance(balance) {};
|
||||
virtual ~WalletActualBalanceUpdatedEvent() {};
|
||||
|
||||
virtual void notify(tools::ObserverManager<CryptoNote::IWalletObserver>& observer)
|
||||
{
|
||||
observer.notify(&IWalletObserver::actualBalanceUpdated, m_balance);
|
||||
}
|
||||
private:
|
||||
uint64_t m_balance;
|
||||
};
|
||||
|
||||
class WalletPendingBalanceUpdatedEvent : public WalletEvent
|
||||
{
|
||||
public:
|
||||
WalletPendingBalanceUpdatedEvent(uint64_t balance) : m_balance(balance) {};
|
||||
virtual ~WalletPendingBalanceUpdatedEvent() {};
|
||||
|
||||
virtual void notify(tools::ObserverManager<CryptoNote::IWalletObserver>& observer)
|
||||
{
|
||||
observer.notify(&IWalletObserver::pendingBalanceUpdated, m_balance);
|
||||
}
|
||||
private:
|
||||
uint64_t m_balance;
|
||||
};
|
||||
|
||||
} /* namespace CryptoNote */
|
||||
|
95
src/wallet/WalletRequest.h
Normal file
95
src/wallet/WalletRequest.h
Normal file
|
@ -0,0 +1,95 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "INode.h"
|
||||
|
||||
#include "WalletSynchronizationContext.h"
|
||||
#include "WalletSendTransactionContext.h"
|
||||
#include "WalletEvent.h"
|
||||
|
||||
namespace CryptoNote {
|
||||
|
||||
class WalletRequest
|
||||
{
|
||||
public:
|
||||
typedef std::function<void (std::deque<std::shared_ptr<WalletEvent> >& events, boost::optional<std::shared_ptr<WalletRequest> >& nextRequest, std::error_code ec)> Callback;
|
||||
|
||||
virtual ~WalletRequest() {};
|
||||
|
||||
virtual void perform(INode& node, std::function<void (WalletRequest::Callback, std::error_code)> cb) = 0;
|
||||
};
|
||||
|
||||
class WalletGetNewBlocksRequest: public WalletRequest
|
||||
{
|
||||
public:
|
||||
WalletGetNewBlocksRequest(const std::list<crypto::hash>& knownBlockIds, std::shared_ptr<SynchronizationContext> context, Callback cb) : m_ids(knownBlockIds), m_context(context), m_cb(cb) {};
|
||||
virtual ~WalletGetNewBlocksRequest() {};
|
||||
|
||||
virtual void perform(INode& node, std::function<void (WalletRequest::Callback, std::error_code)> cb)
|
||||
{
|
||||
node.getNewBlocks(std::move(m_ids), std::ref(m_context->newBlocks), std::ref(m_context->startHeight), std::bind(cb, m_cb, std::placeholders::_1));
|
||||
};
|
||||
|
||||
private:
|
||||
std::shared_ptr<SynchronizationContext> m_context;
|
||||
std::list<crypto::hash> m_ids;
|
||||
Callback m_cb;
|
||||
};
|
||||
|
||||
class WalletGetTransactionOutsGlobalIndicesRequest: public WalletRequest
|
||||
{
|
||||
public:
|
||||
WalletGetTransactionOutsGlobalIndicesRequest(const crypto::hash& transactionHash, std::vector<uint64_t>& outsGlobalIndices, Callback cb) : m_hash(transactionHash), m_outs(outsGlobalIndices), m_cb(cb) {};
|
||||
virtual ~WalletGetTransactionOutsGlobalIndicesRequest() {};
|
||||
|
||||
virtual void perform(INode& node, std::function<void (WalletRequest::Callback, std::error_code)> cb)
|
||||
{
|
||||
node.getTransactionOutsGlobalIndices(m_hash, std::ref(m_outs), std::bind(cb, m_cb, std::placeholders::_1));
|
||||
};
|
||||
|
||||
private:
|
||||
crypto::hash m_hash;
|
||||
std::vector<uint64_t>& m_outs;
|
||||
Callback m_cb;
|
||||
};
|
||||
|
||||
class WalletGetRandomOutsByAmountsRequest: public WalletRequest
|
||||
{
|
||||
public:
|
||||
WalletGetRandomOutsByAmountsRequest(const std::vector<uint64_t>& amounts, uint64_t outsCount, std::shared_ptr<SendTransactionContext> context, Callback cb) :
|
||||
m_amounts(amounts), m_outsCount(outsCount), m_context(context), m_cb(cb) {};
|
||||
|
||||
virtual ~WalletGetRandomOutsByAmountsRequest() {};
|
||||
|
||||
virtual void perform(INode& node, std::function<void (WalletRequest::Callback, std::error_code)> cb)
|
||||
{
|
||||
node.getRandomOutsByAmounts(std::move(m_amounts), m_outsCount, std::ref(m_context->outs), std::bind(cb, m_cb, std::placeholders::_1));
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<uint64_t> m_amounts;
|
||||
uint64_t m_outsCount;
|
||||
std::shared_ptr<SendTransactionContext> m_context;
|
||||
Callback m_cb;
|
||||
};
|
||||
|
||||
class WalletRelayTransactionRequest: public WalletRequest
|
||||
{
|
||||
public:
|
||||
WalletRelayTransactionRequest(const cryptonote::transaction& tx, Callback cb) : m_tx(tx), m_cb(cb) {};
|
||||
virtual ~WalletRelayTransactionRequest() {};
|
||||
|
||||
virtual void perform(INode& node, std::function<void (WalletRequest::Callback, std::error_code)> cb)
|
||||
{
|
||||
node.relayTransaction(m_tx, std::bind(cb, m_cb, std::placeholders::_1));
|
||||
}
|
||||
|
||||
private:
|
||||
cryptonote::transaction m_tx;
|
||||
Callback m_cb;
|
||||
};
|
||||
|
||||
} //namespace CryptoNote
|
40
src/wallet/WalletSendTransactionContext.h
Normal file
40
src/wallet/WalletSendTransactionContext.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
#include "cryptonote_core/cryptonote_basic.h"
|
||||
#include "IWallet.h"
|
||||
|
||||
namespace CryptoNote {
|
||||
|
||||
struct TxDustPolicy
|
||||
{
|
||||
uint64_t dustThreshold;
|
||||
bool addToFee;
|
||||
cryptonote::account_public_address addrForDust;
|
||||
|
||||
TxDustPolicy(uint64_t a_dust_threshold = 0, bool an_add_to_fee = true, cryptonote::account_public_address an_addr_for_dust = cryptonote::account_public_address())
|
||||
: dustThreshold(a_dust_threshold)
|
||||
, addToFee(an_add_to_fee)
|
||||
, addrForDust(an_addr_for_dust)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct SendTransactionContext
|
||||
{
|
||||
TransactionId transactionId;
|
||||
std::vector<cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount> outs;
|
||||
uint64_t foundMoney;
|
||||
std::list<size_t> selectedTransfers;
|
||||
uint64_t unlockTimestamp;
|
||||
TxDustPolicy dustPolicy;
|
||||
uint64_t mixIn;
|
||||
};
|
||||
|
||||
} //namespace CryptoNote
|
80
src/wallet/WalletSerialization.h
Normal file
80
src/wallet/WalletSerialization.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/serialization/split_free.hpp>
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include <boost/serialization/utility.hpp>
|
||||
#include <boost/serialization/array.hpp>
|
||||
|
||||
#include "cryptonote_core/cryptonote_boost_serialization.h"
|
||||
#include "common/unordered_containers_boost_serialization.h"
|
||||
#include "storages/portable_storage_template_helper.h"
|
||||
|
||||
BOOST_SERIALIZATION_SPLIT_FREE(cryptonote::account_base);
|
||||
|
||||
namespace boost {
|
||||
namespace serialization {
|
||||
|
||||
template<class Archive>
|
||||
inline void load(Archive & ar, cryptonote::account_base& account, const unsigned int version)
|
||||
{
|
||||
std::string data;
|
||||
ar >> data;
|
||||
epee::serialization::load_t_from_binary(account, data);
|
||||
}
|
||||
|
||||
template<class Archive>
|
||||
inline void save(Archive & ar, const cryptonote::account_base& account, const unsigned int version)
|
||||
{
|
||||
std::string data;
|
||||
epee::serialization::store_t_to_binary(account, data);
|
||||
ar << data;
|
||||
}
|
||||
|
||||
template<class Archive>
|
||||
inline void serialize(Archive & ar, CryptoNote::Transaction& tx, const unsigned int version)
|
||||
{
|
||||
ar & tx.firstTransferId;
|
||||
ar & tx.transferCount;
|
||||
ar & tx.totalAmount;
|
||||
ar & tx.fee;
|
||||
ar & make_array(tx.hash.data(), tx.hash.size());
|
||||
ar & tx.isCoinbase;
|
||||
ar & tx.blockHeight;
|
||||
ar & tx.timestamp;
|
||||
ar & tx.extra;
|
||||
}
|
||||
|
||||
template<class Archive>
|
||||
inline void serialize(Archive & ar, CryptoNote::Transfer& tr, const unsigned int version)
|
||||
{
|
||||
ar & tr.address;
|
||||
ar & tr.amount;
|
||||
}
|
||||
|
||||
template<class Archive>
|
||||
inline void serialize(Archive & ar, CryptoNote::TransferDetails& details, const unsigned int version)
|
||||
{
|
||||
ar & details.blockHeight;
|
||||
ar & details.tx;
|
||||
ar & details.internalOutputIndex;
|
||||
ar & details.globalOutputIndex;
|
||||
ar & details.spent;
|
||||
ar & details.keyImage;
|
||||
}
|
||||
|
||||
template<class Archive>
|
||||
inline void serialize(Archive & ar, CryptoNote::UnconfirmedTransferDetails& details, const unsigned int version)
|
||||
{
|
||||
ar & details.tx;
|
||||
ar & details.change;
|
||||
ar & details.sentTime;
|
||||
ar & details.transactionId;
|
||||
}
|
||||
|
||||
} // namespace serialization
|
||||
} // namespace boost
|
42
src/wallet/WalletSynchronizationContext.h
Normal file
42
src/wallet/WalletSynchronizationContext.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "crypto/crypto.h"
|
||||
#include "cryptonote_core/cryptonote_basic.h"
|
||||
|
||||
namespace CryptoNote {
|
||||
|
||||
struct TransactionContextInfo
|
||||
{
|
||||
std::vector<size_t> requestedOuts;
|
||||
std::vector<uint64_t> globalIndices;
|
||||
cryptonote::transaction transaction;
|
||||
crypto::public_key transactionPubKey;
|
||||
};
|
||||
|
||||
struct SynchronizationState
|
||||
{
|
||||
SynchronizationState() : blockIdx(0), transactionIdx(0), minersTxProcessed(false) {}
|
||||
size_t blockIdx; //block index within context->new_blocks array to be processed
|
||||
size_t transactionIdx; //tx index within the block to be processed
|
||||
bool minersTxProcessed; //is miner's tx in the block processed
|
||||
};
|
||||
|
||||
struct SynchronizationContext
|
||||
{
|
||||
std::list<cryptonote::block_complete_entry> newBlocks;
|
||||
uint64_t startHeight;
|
||||
std::unordered_map<crypto::hash, TransactionContextInfo> transactionContext;
|
||||
SynchronizationState progress;
|
||||
};
|
||||
|
||||
} //namespace CryptoNote
|
475
src/wallet/WalletSynchronizer.cpp
Normal file
475
src/wallet/WalletSynchronizer.cpp
Normal file
|
@ -0,0 +1,475 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "WalletSynchronizer.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "WalletErrors.h"
|
||||
#include "WalletUtils.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void throwIf(bool expr, cryptonote::error::WalletErrorCodes ec) {
|
||||
if (expr)
|
||||
throw std::system_error(make_error_code(ec));
|
||||
}
|
||||
|
||||
bool getTxPubKey(const cryptonote::transaction& tx, crypto::public_key& key) {
|
||||
std::vector<cryptonote::tx_extra_field> extraFields;
|
||||
cryptonote::parse_tx_extra(tx.extra, extraFields);
|
||||
|
||||
cryptonote::tx_extra_pub_key pubKeyField;
|
||||
if(!cryptonote::find_tx_extra_field_by_type(extraFields, pubKeyField)) {
|
||||
//Public key wasn't found in the transaction extra. Skipping transaction
|
||||
return false;
|
||||
}
|
||||
|
||||
key = pubKeyField.pub_key;
|
||||
return true;
|
||||
}
|
||||
|
||||
void findMyOuts(const cryptonote::account_keys& acc, const cryptonote::transaction& tx, const crypto::public_key& txPubKey, std::vector<size_t>& outs, uint64_t& moneyTransfered) {
|
||||
bool r = cryptonote::lookup_acc_outs(acc, tx, txPubKey, outs, moneyTransfered);
|
||||
throwIf(!r, cryptonote::error::INTERNAL_WALLET_ERROR);
|
||||
}
|
||||
|
||||
uint64_t countOverallTxOutputs(const cryptonote::transaction& tx) {
|
||||
uint64_t amount = 0;
|
||||
for (const cryptonote::tx_out& o: tx.vout) {
|
||||
amount += o.amount;
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
uint64_t countOverallTxInputs(const cryptonote::transaction& tx) {
|
||||
uint64_t amount = 0;
|
||||
for (auto& in: tx.vin) {
|
||||
if(in.type() != typeid(cryptonote::txin_to_key))
|
||||
continue;
|
||||
|
||||
amount += boost::get<cryptonote::txin_to_key>(in).amount;
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
void fillTransactionHash(const cryptonote::transaction& tx, CryptoNote::TransactionHash& hash) {
|
||||
crypto::hash h = cryptonote::get_transaction_hash(tx);
|
||||
memcpy(hash.data(), reinterpret_cast<const uint8_t *>(&h), hash.size());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace CryptoNote
|
||||
{
|
||||
|
||||
WalletSynchronizer::WalletSynchronizer(const cryptonote::account_base& account, INode& node, std::vector<crypto::hash>& blockchain,
|
||||
WalletTransferDetails& transferDetails, WalletUnconfirmedTransactions& unconfirmedTransactions,
|
||||
WalletUserTransactionsCache& transactionsCache) :
|
||||
m_account(account),
|
||||
m_node(node),
|
||||
m_blockchain(blockchain),
|
||||
m_transferDetails(transferDetails),
|
||||
m_unconfirmedTransactions(unconfirmedTransactions),
|
||||
m_transactionsCache(transactionsCache),
|
||||
m_actualBalance(0),
|
||||
m_pendingBalance(0),
|
||||
m_isStoping(false) {
|
||||
}
|
||||
|
||||
void WalletSynchronizer::stop() {
|
||||
m_isStoping = true;
|
||||
}
|
||||
|
||||
std::shared_ptr<WalletRequest> WalletSynchronizer::makeStartRefreshRequest() {
|
||||
std::shared_ptr<SynchronizationContext> context = std::make_shared<SynchronizationContext>();
|
||||
std::shared_ptr<WalletRequest> request = makeGetNewBlocksRequest(context);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
void WalletSynchronizer::postGetTransactionOutsGlobalIndicesRequest(ProcessParameters& parameters, const crypto::hash& hash, std::vector<uint64_t>& outsGlobalIndices, uint64_t height) {
|
||||
parameters.nextRequest = std::make_shared<WalletGetTransactionOutsGlobalIndicesRequest>(hash, outsGlobalIndices,
|
||||
std::bind(&WalletSynchronizer::handleTransactionOutGlobalIndicesResponse, this, parameters.context, hash, height, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
}
|
||||
|
||||
std::shared_ptr<WalletRequest> WalletSynchronizer::makeGetNewBlocksRequest(std::shared_ptr<SynchronizationContext> context) {
|
||||
context->newBlocks.clear();
|
||||
context->startHeight = 0;
|
||||
context->progress = SynchronizationState();
|
||||
|
||||
std::list<crypto::hash> ids;
|
||||
getShortChainHistory(ids);
|
||||
|
||||
std::shared_ptr<WalletRequest>req = std::make_shared<WalletGetNewBlocksRequest>(ids, context, std::bind(&WalletSynchronizer::handleNewBlocksPortion, this,
|
||||
context, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
void WalletSynchronizer::getShortChainHistory(std::list<crypto::hash>& ids) {
|
||||
size_t i = 0;
|
||||
size_t current_multiplier = 1;
|
||||
size_t sz = m_blockchain.size();
|
||||
|
||||
if(!sz)
|
||||
return;
|
||||
|
||||
size_t current_back_offset = 1;
|
||||
bool genesis_included = false;
|
||||
|
||||
while(current_back_offset < sz) {
|
||||
ids.push_back(m_blockchain[sz-current_back_offset]);
|
||||
if(sz-current_back_offset == 0)
|
||||
genesis_included = true;
|
||||
if(i < 10) {
|
||||
++current_back_offset;
|
||||
}else
|
||||
{
|
||||
current_back_offset += current_multiplier *= 2;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
if(!genesis_included)
|
||||
ids.push_back(m_blockchain[0]);
|
||||
}
|
||||
|
||||
void WalletSynchronizer::handleNewBlocksPortion(std::shared_ptr<SynchronizationContext> context, std::deque<std::shared_ptr<WalletEvent> >& events,
|
||||
boost::optional<std::shared_ptr<WalletRequest> >& nextRequest, std::error_code ec) {
|
||||
if (m_isStoping) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ec) {
|
||||
events.push_back(std::make_shared<WalletSynchronizationProgressUpdatedEvent>(context->startHeight, m_node.getLastLocalBlockHeight(), ec));
|
||||
return;
|
||||
}
|
||||
|
||||
ProcessParameters parameters;
|
||||
parameters.context = context;
|
||||
|
||||
try
|
||||
{
|
||||
bool fillRequest = processNewBlocks(parameters);
|
||||
|
||||
if (fillRequest) {
|
||||
parameters.nextRequest = makeGetNewBlocksRequest(context);
|
||||
}
|
||||
}
|
||||
catch (std::system_error& e) {
|
||||
parameters.nextRequest = boost::none;
|
||||
parameters.events.push_back(std::make_shared<WalletSynchronizationProgressUpdatedEvent>(context->startHeight, m_node.getLastLocalBlockHeight(), e.code()));
|
||||
}
|
||||
catch (std::exception&) {
|
||||
parameters.nextRequest = boost::none;
|
||||
parameters.events.push_back(std::make_shared<WalletSynchronizationProgressUpdatedEvent>(context->startHeight, m_node.getLastLocalBlockHeight(), make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR)));
|
||||
}
|
||||
|
||||
refreshBalance(events);
|
||||
|
||||
std::copy(parameters.events.begin(), parameters.events.end(), std::back_inserter(events));
|
||||
nextRequest = parameters.nextRequest;
|
||||
}
|
||||
|
||||
//returns true if new request should be performed
|
||||
bool WalletSynchronizer::processNewBlocks(ProcessParameters& parameters) {
|
||||
bool fillRequest = false;
|
||||
std::shared_ptr<SynchronizationContext> context = parameters.context;
|
||||
|
||||
size_t currentIndex = context->startHeight + context->progress.blockIdx;
|
||||
|
||||
try
|
||||
{
|
||||
auto blocksIt = context->newBlocks.begin();
|
||||
std::advance(blocksIt, context->progress.blockIdx);
|
||||
|
||||
for (; blocksIt != context->newBlocks.end(); ++blocksIt) {
|
||||
if (m_isStoping) return false;
|
||||
|
||||
auto& blockEntry = *blocksIt;
|
||||
|
||||
NextBlockAction action = handleNewBlockchainEntry(parameters, blockEntry, currentIndex);
|
||||
|
||||
if (action == INTERRUPT)
|
||||
return false;
|
||||
else if (action == CONTINUE)
|
||||
fillRequest = true;
|
||||
|
||||
++context->progress.blockIdx;
|
||||
++currentIndex;
|
||||
}
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
context->startHeight = currentIndex;
|
||||
throw e;
|
||||
}
|
||||
|
||||
return fillRequest;
|
||||
}
|
||||
|
||||
WalletSynchronizer::NextBlockAction WalletSynchronizer::handleNewBlockchainEntry(ProcessParameters& parameters, cryptonote::block_complete_entry& blockEntry, uint64_t height) {
|
||||
cryptonote::block b;
|
||||
bool r = cryptonote::parse_and_validate_block_from_blob(blockEntry.block, b);
|
||||
throwIf(!r, cryptonote::error::INTERNAL_WALLET_ERROR);
|
||||
|
||||
crypto::hash blockId = get_block_hash(b);
|
||||
if (height >= m_blockchain.size()) {
|
||||
r = processNewBlockchainEntry(parameters, blockEntry, b, blockId, height);
|
||||
|
||||
if (r) {
|
||||
parameters.events.push_back(std::make_shared<WalletSynchronizationProgressUpdatedEvent>(height, m_node.getLastLocalBlockHeight(), std::error_code()));
|
||||
return CONTINUE;
|
||||
}
|
||||
return INTERRUPT;
|
||||
}
|
||||
|
||||
if(blockId != m_blockchain[height]) {
|
||||
//split detected here !!!
|
||||
//Wrong daemon response
|
||||
throwIf(height == parameters.context->startHeight, cryptonote::error::INTERNAL_WALLET_ERROR);
|
||||
|
||||
detachBlockchain(height);
|
||||
|
||||
r = processNewBlockchainEntry(parameters, blockEntry, b, blockId, height);
|
||||
|
||||
if (r) {
|
||||
parameters.events.push_back(std::make_shared<WalletSynchronizationProgressUpdatedEvent>(height, m_node.getLastLocalBlockHeight(), std::error_code()));
|
||||
return CONTINUE;
|
||||
}
|
||||
return INTERRUPT;
|
||||
}
|
||||
|
||||
//we already have this block.
|
||||
return SKIP;
|
||||
}
|
||||
|
||||
bool WalletSynchronizer::processNewBlockchainEntry(ProcessParameters& parameters, cryptonote::block_complete_entry& blockEntry, const cryptonote::block& b, crypto::hash& blockId, uint64_t height) {
|
||||
throwIf(height != m_blockchain.size(), cryptonote::error::INTERNAL_WALLET_ERROR);
|
||||
|
||||
if(b.timestamp + 60*60*24 > m_account.get_createtime()) {
|
||||
if (!processMinersTx(parameters, b.miner_tx, height, b.timestamp))
|
||||
return false;
|
||||
|
||||
auto txIt = blockEntry.txs.begin();
|
||||
std::advance(txIt, parameters.context->progress.transactionIdx);
|
||||
|
||||
for (; txIt != blockEntry.txs.end(); ++txIt) {
|
||||
auto& txblob = *txIt;
|
||||
cryptonote::transaction tx;
|
||||
|
||||
bool r = parse_and_validate_tx_from_blob(txblob, tx);
|
||||
throwIf(!r, cryptonote::error::INTERNAL_WALLET_ERROR);
|
||||
|
||||
r = processNewTransaction(parameters, tx, height, false, b.timestamp);
|
||||
parameters.context->progress.transactionIdx++;
|
||||
|
||||
if (!r) return false;
|
||||
}
|
||||
}
|
||||
|
||||
parameters.context->progress.transactionIdx = 0;
|
||||
|
||||
m_blockchain.push_back(blockId);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WalletSynchronizer::processMinersTx(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t height, uint64_t timestamp) {
|
||||
bool r = true;
|
||||
|
||||
if (!parameters.context->progress.minersTxProcessed) {
|
||||
r = processNewTransaction(parameters, tx, height, true, timestamp);
|
||||
parameters.context->progress.minersTxProcessed = true;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
bool WalletSynchronizer::processNewTransaction(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t height, bool isCoinbase, uint64_t timestamp) {
|
||||
bool res = true;
|
||||
|
||||
processUnconfirmed(parameters, tx, height, timestamp);
|
||||
std::vector<size_t> outs;
|
||||
uint64_t moneyInMyOuts = 0;
|
||||
|
||||
crypto::public_key publicKey;
|
||||
if (!getTxPubKey(tx, publicKey))
|
||||
return true; //Public key wasn't found in the transaction extra. Skipping transaction
|
||||
|
||||
findMyOuts(m_account.get_keys(), tx, publicKey, outs, moneyInMyOuts);
|
||||
|
||||
if(!outs.empty() && moneyInMyOuts) {
|
||||
fillGetTransactionOutsGlobalIndicesRequest(parameters, tx, outs, publicKey, height);
|
||||
res = false;
|
||||
}
|
||||
|
||||
uint64_t moneyInMyInputs = processMyInputs(tx);
|
||||
|
||||
if (!moneyInMyOuts && !moneyInMyInputs)
|
||||
return res; //There's nothing related to our account, skip it
|
||||
|
||||
updateTransactionsCache(parameters, tx, moneyInMyOuts, moneyInMyInputs, height, isCoinbase, timestamp);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
uint64_t WalletSynchronizer::processMyInputs(const cryptonote::transaction& tx) {
|
||||
uint64_t money = 0;
|
||||
// check all outputs for spending (compare key images)
|
||||
for (auto& in: tx.vin) {
|
||||
if(in.type() != typeid(cryptonote::txin_to_key))
|
||||
continue;
|
||||
|
||||
size_t idx;
|
||||
if (!m_transferDetails.getTransferDetailsIdxByKeyImage(boost::get<cryptonote::txin_to_key>(in).k_image, idx))
|
||||
continue;
|
||||
|
||||
money += boost::get<cryptonote::txin_to_key>(in).amount;
|
||||
|
||||
TransferDetails& td = m_transferDetails.getTransferDetails(idx);
|
||||
td.spent = true;
|
||||
}
|
||||
|
||||
return money;
|
||||
}
|
||||
|
||||
void WalletSynchronizer::fillGetTransactionOutsGlobalIndicesRequest(ProcessParameters& parameters, const cryptonote::transaction& tx,
|
||||
const std::vector<size_t>& outs, const crypto::public_key& publicKey, uint64_t height) {
|
||||
crypto::hash txid = cryptonote::get_transaction_hash(tx);
|
||||
|
||||
TransactionContextInfo tx_context;
|
||||
tx_context.requestedOuts = outs;
|
||||
tx_context.transaction = tx;
|
||||
tx_context.transactionPubKey = publicKey;
|
||||
|
||||
auto insert_result = parameters.context->transactionContext.emplace(txid, std::move(tx_context));
|
||||
|
||||
postGetTransactionOutsGlobalIndicesRequest(parameters, txid, insert_result.first->second.globalIndices, height);
|
||||
}
|
||||
|
||||
void WalletSynchronizer::updateTransactionsCache(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t myOuts, uint64_t myInputs, uint64_t height, bool isCoinbase, uint64_t timestamp) {
|
||||
|
||||
uint64_t allOuts = countOverallTxOutputs(tx);
|
||||
uint64_t allInputs = countOverallTxInputs(tx);
|
||||
|
||||
TransactionId foundTx = m_transactionsCache.findTransactionByHash(cryptonote::get_transaction_hash(tx));
|
||||
if (foundTx == INVALID_TRANSACTION_ID) {
|
||||
Transaction transaction;
|
||||
transaction.firstTransferId = INVALID_TRANSFER_ID;
|
||||
transaction.transferCount = 0;
|
||||
transaction.totalAmount = myOuts - myInputs;
|
||||
transaction.fee = isCoinbase ? 0 : allInputs - allOuts;
|
||||
fillTransactionHash(tx, transaction.hash);
|
||||
transaction.blockHeight = height;
|
||||
transaction.isCoinbase = isCoinbase;
|
||||
transaction.timestamp = timestamp;
|
||||
|
||||
TransactionId newId = m_transactionsCache.insertTransaction(std::move(transaction));
|
||||
|
||||
parameters.events.push_back(std::make_shared<WalletExternalTransactionCreatedEvent>(newId));
|
||||
}
|
||||
else
|
||||
{
|
||||
Transaction& transaction = m_transactionsCache.getTransaction(foundTx);
|
||||
transaction.blockHeight = height;
|
||||
transaction.timestamp = timestamp;
|
||||
transaction.isCoinbase = isCoinbase;
|
||||
|
||||
parameters.events.push_back(std::make_shared<WalletTransactionUpdatedEvent>(foundTx));
|
||||
}
|
||||
}
|
||||
|
||||
void WalletSynchronizer::processUnconfirmed(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t height, uint64_t timestamp) {
|
||||
TransactionId id;
|
||||
crypto::hash hash = get_transaction_hash(tx);
|
||||
|
||||
if (!m_unconfirmedTransactions.findTransactionId(hash, id))
|
||||
return;
|
||||
|
||||
Transaction& tr = m_transactionsCache.getTransaction(id);
|
||||
tr.blockHeight = height;
|
||||
tr.timestamp = timestamp;
|
||||
|
||||
m_unconfirmedTransactions.erase(hash);
|
||||
|
||||
parameters.events.push_back(std::make_shared<WalletTransactionUpdatedEvent>(id));
|
||||
}
|
||||
|
||||
void WalletSynchronizer::handleTransactionOutGlobalIndicesResponse(std::shared_ptr<SynchronizationContext> context, crypto::hash txid, uint64_t height,
|
||||
std::deque<std::shared_ptr<WalletEvent> >& events, boost::optional<std::shared_ptr<WalletRequest> >& nextRequest, std::error_code ec) {
|
||||
if (m_isStoping) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ec) {
|
||||
events.push_back(std::make_shared<WalletSynchronizationProgressUpdatedEvent>(height, m_node.getLastLocalBlockHeight(), ec));
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto it = context->transactionContext.find(txid);
|
||||
if (it == context->transactionContext.end()) {
|
||||
events.push_back(std::make_shared<WalletSynchronizationProgressUpdatedEvent>(height, m_node.getLastLocalBlockHeight(), make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR)));
|
||||
return;
|
||||
}
|
||||
|
||||
cryptonote::transaction& tx = it->second.transaction;
|
||||
std::vector<size_t>& outs = it->second.requestedOuts;
|
||||
crypto::public_key& tx_pub_key = it->second.transactionPubKey;
|
||||
std::vector<uint64_t>& global_indices = it->second.globalIndices;
|
||||
|
||||
for (size_t o: outs) {
|
||||
throwIf(tx.vout.size() <= o, cryptonote::error::INTERNAL_WALLET_ERROR);
|
||||
|
||||
TransferDetails td;
|
||||
td.blockHeight = height;
|
||||
td.internalOutputIndex = o;
|
||||
td.globalOutputIndex = global_indices[o];
|
||||
td.tx = tx;
|
||||
td.spent = false;
|
||||
cryptonote::keypair in_ephemeral;
|
||||
cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, o, in_ephemeral, td.keyImage);
|
||||
throwIf(in_ephemeral.pub != boost::get<cryptonote::txout_to_key>(tx.vout[o].target).key, cryptonote::error::INTERNAL_WALLET_ERROR);
|
||||
|
||||
m_transferDetails.addTransferDetails(td);
|
||||
}
|
||||
|
||||
context->transactionContext.erase(it);
|
||||
}
|
||||
catch (std::exception&) {
|
||||
events.push_back(std::make_shared<WalletSynchronizationProgressUpdatedEvent>(height, m_node.getLastLocalBlockHeight(), make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR)));
|
||||
return;
|
||||
}
|
||||
|
||||
handleNewBlocksPortion(context, events, nextRequest, ec);
|
||||
}
|
||||
|
||||
void WalletSynchronizer::detachBlockchain(uint64_t height) {
|
||||
m_transferDetails.detachTransferDetails(height);
|
||||
|
||||
m_blockchain.erase(m_blockchain.begin()+height, m_blockchain.end());
|
||||
|
||||
m_transactionsCache.detachTransactions(height);
|
||||
}
|
||||
|
||||
void WalletSynchronizer::refreshBalance(std::deque<std::shared_ptr<WalletEvent> >& events) {
|
||||
uint64_t actualBalance = m_transferDetails.countActualBalance();
|
||||
uint64_t pendingBalance = m_unconfirmedTransactions.countPendingBalance();
|
||||
pendingBalance += m_transferDetails.countPendingBalance();
|
||||
|
||||
if (actualBalance != m_actualBalance) {
|
||||
events.push_back(std::make_shared<WalletActualBalanceUpdatedEvent>(actualBalance));
|
||||
m_actualBalance = actualBalance;
|
||||
}
|
||||
|
||||
if (pendingBalance != m_pendingBalance) {
|
||||
events.push_back(std::make_shared<WalletPendingBalanceUpdatedEvent>(pendingBalance));
|
||||
m_pendingBalance = pendingBalance;
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace CryptoNote */
|
83
src/wallet/WalletSynchronizer.h
Normal file
83
src/wallet/WalletSynchronizer.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
#include "INode.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "WalletTransferDetails.h"
|
||||
#include "WalletUnconfirmedTransactions.h"
|
||||
#include "WalletUserTransactionsCache.h"
|
||||
#include "WalletEvent.h"
|
||||
#include "WalletSynchronizationContext.h"
|
||||
#include "WalletRequest.h"
|
||||
|
||||
namespace CryptoNote {
|
||||
|
||||
class WalletSynchronizer
|
||||
{
|
||||
public:
|
||||
WalletSynchronizer(const cryptonote::account_base& account, INode& node, std::vector<crypto::hash>& blockchain, WalletTransferDetails& transferDetails,
|
||||
WalletUnconfirmedTransactions& unconfirmedTransactions, WalletUserTransactionsCache& transactionsCache);
|
||||
|
||||
std::shared_ptr<WalletRequest> makeStartRefreshRequest();
|
||||
|
||||
void stop();
|
||||
|
||||
private:
|
||||
|
||||
struct ProcessParameters {
|
||||
std::shared_ptr<SynchronizationContext> context;
|
||||
std::vector<std::shared_ptr<WalletEvent> > events;
|
||||
boost::optional<std::shared_ptr<WalletRequest> > nextRequest;
|
||||
};
|
||||
|
||||
enum NextBlockAction {
|
||||
INTERRUPT = 0,
|
||||
CONTINUE,
|
||||
SKIP
|
||||
};
|
||||
|
||||
void handleNewBlocksPortion(std::shared_ptr<SynchronizationContext> context, std::deque<std::shared_ptr<WalletEvent> >& events,
|
||||
boost::optional<std::shared_ptr<WalletRequest> >& nextRequest, std::error_code ec);
|
||||
void handleTransactionOutGlobalIndicesResponse(std::shared_ptr<SynchronizationContext> context, crypto::hash txid, uint64_t height,
|
||||
std::deque<std::shared_ptr<WalletEvent> >& events, boost::optional<std::shared_ptr<WalletRequest> >& nextRequest, std::error_code ec);
|
||||
|
||||
void getShortChainHistory(std::list<crypto::hash>& ids);
|
||||
|
||||
bool processNewBlocks(ProcessParameters& parameters);
|
||||
NextBlockAction handleNewBlockchainEntry(ProcessParameters& parameters, cryptonote::block_complete_entry& blockEntry, uint64_t height);
|
||||
bool processNewBlockchainEntry(ProcessParameters& parameters, cryptonote::block_complete_entry& blockEntry, const cryptonote::block& b, crypto::hash& blockId, uint64_t height);
|
||||
bool processNewTransaction(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t height, bool isCoinbase, uint64_t timestamp);
|
||||
bool processMinersTx(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t height, uint64_t timestamp);
|
||||
void processUnconfirmed(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t height, uint64_t timestamp);
|
||||
uint64_t processMyInputs(const cryptonote::transaction& tx);
|
||||
void updateTransactionsCache(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t myOuts, uint64_t myInputs, uint64_t height,
|
||||
bool isCoinbase, uint64_t timestamp);
|
||||
void detachBlockchain(uint64_t height);
|
||||
void refreshBalance(std::deque<std::shared_ptr<WalletEvent> >& events);
|
||||
|
||||
void fillGetTransactionOutsGlobalIndicesRequest(ProcessParameters& parameters, const cryptonote::transaction& tx,
|
||||
const std::vector<size_t>& outs, const crypto::public_key& publicKey, uint64_t height);
|
||||
void postGetTransactionOutsGlobalIndicesRequest(ProcessParameters& parameters, const crypto::hash& hash, std::vector<uint64_t>& outsGlobalIndices, uint64_t height);
|
||||
std::shared_ptr<WalletRequest> makeGetNewBlocksRequest(std::shared_ptr<SynchronizationContext> context);
|
||||
|
||||
const cryptonote::account_base& m_account;
|
||||
INode& m_node;
|
||||
std::vector<crypto::hash>& m_blockchain;
|
||||
WalletTransferDetails& m_transferDetails;
|
||||
WalletUnconfirmedTransactions& m_unconfirmedTransactions;
|
||||
WalletUserTransactionsCache& m_transactionsCache;
|
||||
|
||||
uint64_t m_actualBalance;
|
||||
uint64_t m_pendingBalance;
|
||||
|
||||
bool m_isStoping;
|
||||
};
|
||||
|
||||
} /* namespace CryptoNote */
|
299
src/wallet/WalletTransactionSender.cpp
Normal file
299
src/wallet/WalletTransactionSender.cpp
Normal file
|
@ -0,0 +1,299 @@
|
|||
/*
|
||||
* WalletTransactionSender.cpp
|
||||
*
|
||||
* Created on: 18 июня 2014 г.
|
||||
* Author: milo
|
||||
*/
|
||||
|
||||
#include "WalletTransactionSender.h"
|
||||
#include "WalletUtils.h"
|
||||
|
||||
namespace {
|
||||
|
||||
uint64_t countNeededMoney(uint64_t fee, const std::vector<CryptoNote::Transfer>& transfers) {
|
||||
uint64_t needed_money = fee;
|
||||
for (auto& transfer: transfers) {
|
||||
CryptoNote::throwIf(transfer.amount == 0, cryptonote::error::ZERO_DESTINATION);
|
||||
CryptoNote::throwIf(transfer.amount < 0, cryptonote::error::WRONG_AMOUNT);
|
||||
|
||||
needed_money += transfer.amount;
|
||||
CryptoNote::throwIf(static_cast<int64_t>(needed_money) < transfer.amount, cryptonote::error::SUM_OVERFLOW);
|
||||
}
|
||||
|
||||
return needed_money;
|
||||
}
|
||||
|
||||
void createChangeDestinations(const cryptonote::account_public_address& address, uint64_t neededMoney, uint64_t foundMoney, cryptonote::tx_destination_entry& changeDts) {
|
||||
if (neededMoney < foundMoney) {
|
||||
changeDts.addr = address;
|
||||
changeDts.amount = foundMoney - neededMoney;
|
||||
}
|
||||
}
|
||||
|
||||
void constructTx(const cryptonote::account_keys keys, const std::vector<cryptonote::tx_source_entry>& sources, const std::vector<cryptonote::tx_destination_entry>& splittedDests,
|
||||
const std::string& extra, uint64_t unlockTimestamp, uint64_t sizeLimit, cryptonote::transaction& tx) {
|
||||
std::vector<uint8_t> extraVec;
|
||||
extraVec.reserve(extra.size());
|
||||
std::for_each(extra.begin(), extra.end(), [&extraVec] (const char el) { extraVec.push_back(el);});
|
||||
|
||||
bool r = cryptonote::construct_tx(keys, sources, splittedDests, extraVec, tx, unlockTimestamp);
|
||||
CryptoNote::throwIf(!r, cryptonote::error::INTERNAL_WALLET_ERROR);
|
||||
CryptoNote::throwIf(cryptonote::get_object_blobsize(tx) >= sizeLimit, cryptonote::error::TRANSACTION_SIZE_TOO_BIG);
|
||||
}
|
||||
|
||||
void fillTransactionHash(const cryptonote::transaction& tx, CryptoNote::TransactionHash& hash) {
|
||||
crypto::hash h = cryptonote::get_transaction_hash(tx);
|
||||
memcpy(hash.data(), reinterpret_cast<const uint8_t *>(&h), hash.size());
|
||||
}
|
||||
|
||||
} //namespace
|
||||
|
||||
namespace CryptoNote {
|
||||
|
||||
WalletTransactionSender::WalletTransactionSender(WalletUserTransactionsCache& transactionsCache, WalletTxSendingState& sendingTxsStates,
|
||||
WalletTransferDetails& transferDetails, WalletUnconfirmedTransactions& unconfirmedTransactions):
|
||||
m_transactionsCache(transactionsCache),
|
||||
m_sendingTxsStates(sendingTxsStates),
|
||||
m_transferDetails(transferDetails),
|
||||
m_unconfirmedTransactions(unconfirmedTransactions),
|
||||
m_isInitialized(false),
|
||||
m_isStoping(false) {
|
||||
m_upperTransactionSizeLimit = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE*2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
|
||||
}
|
||||
|
||||
void WalletTransactionSender::init(cryptonote::account_keys keys) {
|
||||
if (m_isInitialized) {
|
||||
throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED));
|
||||
}
|
||||
|
||||
m_keys = keys;
|
||||
m_isInitialized = true;
|
||||
}
|
||||
|
||||
void WalletTransactionSender::stop() {
|
||||
m_isStoping = true;
|
||||
}
|
||||
|
||||
std::shared_ptr<WalletRequest> WalletTransactionSender::makeSendRequest(TransactionId& transactionId, std::deque<std::shared_ptr<WalletEvent> >& events,
|
||||
const std::vector<Transfer>& transfers, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) {
|
||||
if (!m_isInitialized)
|
||||
throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED));
|
||||
|
||||
using namespace cryptonote;
|
||||
|
||||
std::shared_ptr<SendTransactionContext> context = std::make_shared<SendTransactionContext>();
|
||||
throwIf(transfers.empty(), cryptonote::error::ZERO_DESTINATION);
|
||||
|
||||
TransferId firstTransferId = m_transactionsCache.insertTransfers(transfers);
|
||||
|
||||
uint64_t neededMoney = countNeededMoney(fee, transfers);
|
||||
|
||||
Transaction transaction;
|
||||
transaction.firstTransferId = firstTransferId;
|
||||
transaction.transferCount = transfers.size();
|
||||
transaction.totalAmount = neededMoney;
|
||||
transaction.fee = fee;
|
||||
transaction.isCoinbase = false;
|
||||
transaction.timestamp = 0;
|
||||
transaction.extra = extra;
|
||||
transaction.blockHeight = UNCONFIRMED_TRANSACTION_HEIGHT;
|
||||
|
||||
TransactionId txId = m_transactionsCache.insertTransaction(std::move(transaction));
|
||||
transactionId = txId;
|
||||
m_sendingTxsStates.sending(txId);
|
||||
|
||||
context->transactionId = txId;
|
||||
context->unlockTimestamp = unlockTimestamp;
|
||||
context->mixIn = mixIn;
|
||||
|
||||
context->foundMoney = m_transferDetails.selectTransfersToSend(neededMoney, 0 == mixIn, context->dustPolicy.dustThreshold, context->selectedTransfers);
|
||||
throwIf(context->foundMoney < neededMoney, cryptonote::error::WRONG_AMOUNT);
|
||||
|
||||
if(context->mixIn) {
|
||||
std::shared_ptr<WalletRequest> request = makeGetRandomOutsRequest(context);
|
||||
return request;
|
||||
}
|
||||
|
||||
return doSendTransaction(context, events);
|
||||
}
|
||||
|
||||
std::shared_ptr<WalletRequest> WalletTransactionSender::makeGetRandomOutsRequest(std::shared_ptr<SendTransactionContext> context) {
|
||||
uint64_t outsCount = context->mixIn + 1;// add one to make possible (if need) to skip real output key
|
||||
std::vector<uint64_t> amounts;
|
||||
|
||||
for (auto idx: context->selectedTransfers) {
|
||||
const TransferDetails& td = m_transferDetails.getTransferDetails(idx);
|
||||
throwIf(td.tx.vout.size() <= td.internalOutputIndex, cryptonote::error::INTERNAL_WALLET_ERROR);
|
||||
amounts.push_back(td.amount());
|
||||
}
|
||||
|
||||
return std::make_shared<WalletGetRandomOutsByAmountsRequest>(amounts, outsCount, context, std::bind(&WalletTransactionSender::sendTransactionRandomOutsByAmount,
|
||||
this, context, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
}
|
||||
|
||||
void WalletTransactionSender::sendTransactionRandomOutsByAmount(std::shared_ptr<SendTransactionContext> context, std::deque<std::shared_ptr<WalletEvent> >& events,
|
||||
boost::optional<std::shared_ptr<WalletRequest> >& nextRequest, std::error_code ec) {
|
||||
if (m_isStoping) {
|
||||
events.push_back(std::make_shared<WalletSendTransactionCompletedEvent>(context->transactionId, make_error_code(cryptonote::error::TX_CANCELLED)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (ec) {
|
||||
events.push_back(std::make_shared<WalletSendTransactionCompletedEvent>(context->transactionId, ec));
|
||||
return;
|
||||
}
|
||||
|
||||
auto scanty_it = std::find_if(context->outs.begin(), context->outs.end(), [&] (cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& out) {return out.outs.size() < context->mixIn;});
|
||||
|
||||
if (scanty_it != context->outs.end()) {
|
||||
events.push_back(std::make_shared<WalletSendTransactionCompletedEvent>(context->transactionId, make_error_code(cryptonote::error::MIXIN_COUNT_TOO_BIG)));
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<WalletRequest> req = doSendTransaction(context, events);
|
||||
if (req)
|
||||
nextRequest = req;
|
||||
}
|
||||
|
||||
std::shared_ptr<WalletRequest> WalletTransactionSender::doSendTransaction(std::shared_ptr<SendTransactionContext> context, std::deque<std::shared_ptr<WalletEvent> >& events) {
|
||||
if (m_isStoping) {
|
||||
events.push_back(std::make_shared<WalletSendTransactionCompletedEvent>(context->transactionId, make_error_code(cryptonote::error::TX_CANCELLED)));
|
||||
return std::shared_ptr<WalletRequest>();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Transaction& transaction = m_transactionsCache.getTransaction(context->transactionId);
|
||||
|
||||
std::vector<cryptonote::tx_source_entry> sources;
|
||||
prepareInputs(context->selectedTransfers, context->outs, sources, context->mixIn);
|
||||
|
||||
cryptonote::tx_destination_entry changeDts = AUTO_VAL_INIT(changeDts);
|
||||
createChangeDestinations(m_keys.m_account_address, transaction.totalAmount, context->foundMoney, changeDts);
|
||||
|
||||
std::vector<cryptonote::tx_destination_entry> splittedDests;
|
||||
splitDestinations(transaction.firstTransferId, transaction.transferCount, changeDts, context->dustPolicy, splittedDests);
|
||||
|
||||
cryptonote::transaction tx;
|
||||
constructTx(m_keys, sources, splittedDests, transaction.extra, context->unlockTimestamp, m_upperTransactionSizeLimit, tx);
|
||||
|
||||
fillTransactionHash(tx, transaction.hash);
|
||||
|
||||
m_unconfirmedTransactions.add(tx, context->transactionId, changeDts.amount);
|
||||
notifyBalanceChanged(events);
|
||||
|
||||
return std::make_shared<WalletRelayTransactionRequest>(tx, std::bind(&WalletTransactionSender::relayTransactionCallback, this, context->transactionId,
|
||||
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
}
|
||||
catch(std::system_error& ec) {
|
||||
events.push_back(std::make_shared<WalletSendTransactionCompletedEvent>(context->transactionId, ec.code()));
|
||||
}
|
||||
catch(std::exception&) {
|
||||
events.push_back(std::make_shared<WalletSendTransactionCompletedEvent>(context->transactionId, make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR)));
|
||||
}
|
||||
|
||||
return std::shared_ptr<WalletRequest>();
|
||||
}
|
||||
|
||||
void WalletTransactionSender::relayTransactionCallback(TransactionId txId, std::deque<std::shared_ptr<WalletEvent> >& events,
|
||||
boost::optional<std::shared_ptr<WalletRequest> >& nextRequest, std::error_code ec) {
|
||||
if (m_isStoping) return;
|
||||
|
||||
if (ec) {
|
||||
m_sendingTxsStates.error(txId);
|
||||
} else {
|
||||
m_sendingTxsStates.sent(txId);
|
||||
}
|
||||
|
||||
events.push_back(std::make_shared<WalletSendTransactionCompletedEvent>(txId, ec));
|
||||
}
|
||||
|
||||
|
||||
void WalletTransactionSender::splitDestinations(TransferId firstTransferId, size_t transfersCount, const cryptonote::tx_destination_entry& changeDts,
|
||||
const TxDustPolicy& dustPolicy, std::vector<cryptonote::tx_destination_entry>& splittedDests) {
|
||||
uint64_t dust = 0;
|
||||
|
||||
digitSplitStrategy(firstTransferId, transfersCount, changeDts, dustPolicy.dustThreshold, splittedDests, dust);
|
||||
|
||||
throwIf(dustPolicy.dustThreshold < dust, cryptonote::error::INTERNAL_WALLET_ERROR);
|
||||
if (0 != dust && !dustPolicy.addToFee) {
|
||||
splittedDests.push_back(cryptonote::tx_destination_entry(dust, dustPolicy.addrForDust));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WalletTransactionSender::digitSplitStrategy(TransferId firstTransferId, size_t transfersCount,
|
||||
const cryptonote::tx_destination_entry& change_dst, uint64_t dust_threshold,
|
||||
std::vector<cryptonote::tx_destination_entry>& splitted_dsts, uint64_t& dust) {
|
||||
splitted_dsts.clear();
|
||||
dust = 0;
|
||||
|
||||
for (TransferId idx = firstTransferId; idx < firstTransferId + transfersCount; ++idx) {
|
||||
Transfer& de = m_transactionsCache.getTransfer(idx);
|
||||
|
||||
cryptonote::account_public_address addr;
|
||||
if (!cryptonote::get_account_address_from_str(addr, de.address)) {
|
||||
throw std::system_error(make_error_code(cryptonote::error::BAD_ADDRESS));
|
||||
}
|
||||
|
||||
cryptonote::decompose_amount_into_digits(de.amount, dust_threshold,
|
||||
[&](uint64_t chunk) { splitted_dsts.push_back(cryptonote::tx_destination_entry(chunk, addr)); },
|
||||
[&](uint64_t a_dust) { splitted_dsts.push_back(cryptonote::tx_destination_entry(a_dust, addr)); } );
|
||||
}
|
||||
|
||||
cryptonote::decompose_amount_into_digits(change_dst.amount, dust_threshold,
|
||||
[&](uint64_t chunk) { splitted_dsts.push_back(cryptonote::tx_destination_entry(chunk, change_dst.addr)); },
|
||||
[&](uint64_t a_dust) { dust = a_dust; } );
|
||||
}
|
||||
|
||||
|
||||
void WalletTransactionSender::prepareInputs(const std::list<size_t>& selectedTransfers, std::vector<cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount>& outs,
|
||||
std::vector<cryptonote::tx_source_entry>& sources, uint64_t mixIn) {
|
||||
size_t i = 0;
|
||||
for (size_t idx: selectedTransfers) {
|
||||
sources.resize(sources.size()+1);
|
||||
cryptonote::tx_source_entry& src = sources.back();
|
||||
TransferDetails& td = m_transferDetails.getTransferDetails(idx);
|
||||
src.amount = td.amount();
|
||||
|
||||
//paste mixin transaction
|
||||
if(outs.size()) {
|
||||
outs[i].outs.sort([](const cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& a, const cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& b){return a.global_amount_index < b.global_amount_index;});
|
||||
for (auto& daemon_oe: outs[i].outs) {
|
||||
if(td.globalOutputIndex == daemon_oe.global_amount_index)
|
||||
continue;
|
||||
cryptonote::tx_source_entry::output_entry oe;
|
||||
oe.first = daemon_oe.global_amount_index;
|
||||
oe.second = daemon_oe.out_key;
|
||||
src.outputs.push_back(oe);
|
||||
if(src.outputs.size() >= mixIn)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//paste real transaction to the random index
|
||||
auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const cryptonote::tx_source_entry::output_entry& a) { return a.first >= td.globalOutputIndex; });
|
||||
|
||||
cryptonote::tx_source_entry::output_entry real_oe;
|
||||
real_oe.first = td.globalOutputIndex;
|
||||
real_oe.second = boost::get<cryptonote::txout_to_key>(td.tx.vout[td.internalOutputIndex].target).key;
|
||||
|
||||
auto interted_it = src.outputs.insert(it_to_insert, real_oe);
|
||||
|
||||
src.real_out_tx_key = get_tx_pub_key_from_extra(td.tx);
|
||||
src.real_output = interted_it - src.outputs.begin();
|
||||
src.real_output_in_tx_index = td.internalOutputIndex;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
void WalletTransactionSender::notifyBalanceChanged(std::deque<std::shared_ptr<WalletEvent> >& events) {
|
||||
uint64_t actualBalance = m_transferDetails.countActualBalance();
|
||||
uint64_t pendingBalance = m_unconfirmedTransactions.countPendingBalance();
|
||||
pendingBalance += m_transferDetails.countPendingBalance();
|
||||
|
||||
events.push_back(std::make_shared<WalletActualBalanceUpdatedEvent>(actualBalance));
|
||||
events.push_back(std::make_shared<WalletPendingBalanceUpdatedEvent>(pendingBalance));
|
||||
}
|
||||
|
||||
} /* namespace CryptoNote */
|
55
src/wallet/WalletTransactionSender.h
Normal file
55
src/wallet/WalletTransactionSender.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "INode.h"
|
||||
#include "WalletSendTransactionContext.h"
|
||||
#include "WalletUserTransactionsCache.h"
|
||||
#include "WalletTransferDetails.h"
|
||||
#include "WalletUnconfirmedTransactions.h"
|
||||
#include "WalletRequest.h"
|
||||
|
||||
namespace CryptoNote {
|
||||
|
||||
class WalletTransactionSender
|
||||
{
|
||||
public:
|
||||
WalletTransactionSender(WalletUserTransactionsCache& transactionsCache, WalletTxSendingState& sendingTxsStates, WalletTransferDetails& transferDetails,
|
||||
WalletUnconfirmedTransactions& unconfirmedTransactions);
|
||||
|
||||
void init(cryptonote::account_keys keys);
|
||||
void stop();
|
||||
|
||||
std::shared_ptr<WalletRequest> makeSendRequest(TransactionId& transactionId, std::deque<std::shared_ptr<WalletEvent> >& events, const std::vector<Transfer>& transfers,
|
||||
uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0);
|
||||
|
||||
private:
|
||||
std::shared_ptr<WalletRequest> makeGetRandomOutsRequest(std::shared_ptr<SendTransactionContext> context);
|
||||
std::shared_ptr<WalletRequest> doSendTransaction(std::shared_ptr<SendTransactionContext> context, std::deque<std::shared_ptr<WalletEvent> >& events);
|
||||
void prepareInputs(const std::list<size_t>& selectedTransfers, std::vector<cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount>& outs,
|
||||
std::vector<cryptonote::tx_source_entry>& sources, uint64_t mixIn);
|
||||
void splitDestinations(TransferId firstTransferId, size_t transfersCount, const cryptonote::tx_destination_entry& changeDts,
|
||||
const TxDustPolicy& dustPolicy, std::vector<cryptonote::tx_destination_entry>& splittedDests);
|
||||
void digitSplitStrategy(TransferId firstTransferId, size_t transfersCount, const cryptonote::tx_destination_entry& change_dst, uint64_t dust_threshold,
|
||||
std::vector<cryptonote::tx_destination_entry>& splitted_dsts, uint64_t& dust);
|
||||
void sendTransactionRandomOutsByAmount(std::shared_ptr<SendTransactionContext> context, std::deque<std::shared_ptr<WalletEvent> >& events,
|
||||
boost::optional<std::shared_ptr<WalletRequest> >& nextRequest, std::error_code ec);
|
||||
void relayTransactionCallback(TransactionId txId, std::deque<std::shared_ptr<WalletEvent> >& events,
|
||||
boost::optional<std::shared_ptr<WalletRequest> >& nextRequest, std::error_code ec);
|
||||
void notifyBalanceChanged(std::deque<std::shared_ptr<WalletEvent> >& events);
|
||||
|
||||
cryptonote::account_keys m_keys;
|
||||
WalletUserTransactionsCache& m_transactionsCache;
|
||||
WalletTxSendingState& m_sendingTxsStates;
|
||||
WalletTransferDetails& m_transferDetails;
|
||||
WalletUnconfirmedTransactions& m_unconfirmedTransactions;
|
||||
uint64_t m_upperTransactionSizeLimit;
|
||||
|
||||
bool m_isInitialized;
|
||||
bool m_isStoping;
|
||||
};
|
||||
|
||||
} /* namespace CryptoNote */
|
||||
|
169
src/wallet/WalletTransferDetails.cpp
Normal file
169
src/wallet/WalletTransferDetails.cpp
Normal file
|
@ -0,0 +1,169 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
||||
#include "WalletTransferDetails.h"
|
||||
|
||||
#include <utility>
|
||||
#include <chrono>
|
||||
#include <random>
|
||||
|
||||
#include <crypto/crypto.h>
|
||||
|
||||
#include "WalletErrors.h"
|
||||
|
||||
#define DEFAULT_TX_SPENDABLE_AGE 10
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename URNG, typename T>
|
||||
T popRandomValue(URNG& randomGenerator, std::vector<T>& vec) {
|
||||
CHECK_AND_ASSERT_MES(!vec.empty(), T(), "Vector must be non-empty");
|
||||
|
||||
std::uniform_int_distribution<size_t> distribution(0, vec.size() - 1);
|
||||
size_t idx = distribution(randomGenerator);
|
||||
|
||||
T res = vec[idx];
|
||||
if (idx + 1 != vec.size()) {
|
||||
vec[idx] = vec.back();
|
||||
}
|
||||
vec.resize(vec.size() - 1);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace CryptoNote
|
||||
{
|
||||
|
||||
WalletTransferDetails::WalletTransferDetails(const std::vector<crypto::hash>& blockchain) : m_blockchain(blockchain) {
|
||||
}
|
||||
|
||||
WalletTransferDetails::~WalletTransferDetails() {
|
||||
}
|
||||
|
||||
TransferDetails& WalletTransferDetails::getTransferDetails(size_t idx) {
|
||||
return m_transfers.at(idx);
|
||||
}
|
||||
|
||||
void WalletTransferDetails::addTransferDetails(const TransferDetails& details) {
|
||||
m_transfers.push_back(details);
|
||||
size_t idx = m_transfers.size() - 1;
|
||||
|
||||
m_keyImages.insert(std::make_pair(details.keyImage, idx));
|
||||
}
|
||||
|
||||
bool WalletTransferDetails::getTransferDetailsIdxByKeyImage(const crypto::key_image& image, size_t& idx) {
|
||||
auto it = m_keyImages.find(image);
|
||||
if (it == m_keyImages.end())
|
||||
return false;
|
||||
|
||||
idx = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WalletTransferDetails::isTxSpendtimeUnlocked(uint64_t unlockTime) const
|
||||
{
|
||||
if(unlockTime < CRYPTONOTE_MAX_BLOCK_NUMBER) {
|
||||
//interpret as block index
|
||||
if(m_blockchain.size()-1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlockTime)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//interpret as time
|
||||
uint64_t current_time = static_cast<uint64_t>(time(NULL));
|
||||
if(current_time + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS >= unlockTime)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WalletTransferDetails::isTransferUnlocked(const TransferDetails& td) const
|
||||
{
|
||||
if(!isTxSpendtimeUnlocked(td.tx.unlock_time))
|
||||
return false;
|
||||
|
||||
if(td.blockHeight + DEFAULT_TX_SPENDABLE_AGE > m_blockchain.size())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t WalletTransferDetails::countActualBalance() const
|
||||
{
|
||||
uint64_t amount = 0;
|
||||
for (auto& transfer: m_transfers) {
|
||||
if(!transfer.spent && isTransferUnlocked(transfer))
|
||||
amount += transfer.amount();
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
uint64_t WalletTransferDetails::countPendingBalance() const
|
||||
{
|
||||
uint64_t amount = 0;
|
||||
for (auto& td: m_transfers) {
|
||||
if (!td.spent)
|
||||
amount += td.amount();
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
uint64_t WalletTransferDetails::selectTransfersToSend(uint64_t neededMoney, bool addDust, uint64_t dust, std::list<size_t>& selectedTransfers) {
|
||||
std::vector<size_t> unusedTransfers;
|
||||
std::vector<size_t> unusedDust;
|
||||
|
||||
for (size_t i = 0; i < m_transfers.size(); ++i) {
|
||||
const TransferDetails& td = m_transfers[i];
|
||||
if (!td.spent && isTransferUnlocked(td)) {
|
||||
if (dust < td.amount())
|
||||
unusedTransfers.push_back(i);
|
||||
else
|
||||
unusedDust.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
std::default_random_engine randomGenerator(crypto::rand<std::default_random_engine::result_type>());
|
||||
bool selectOneDust = addDust && !unusedDust.empty();
|
||||
uint64_t foundMoney = 0;
|
||||
while (foundMoney < neededMoney && (!unusedTransfers.empty() || !unusedDust.empty())) {
|
||||
size_t idx;
|
||||
if (selectOneDust) {
|
||||
idx = popRandomValue(randomGenerator, unusedDust);
|
||||
selectOneDust = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
idx = !unusedTransfers.empty() ? popRandomValue(randomGenerator, unusedTransfers) : popRandomValue(randomGenerator, unusedDust);
|
||||
}
|
||||
|
||||
selectedTransfers.push_back(idx);
|
||||
foundMoney += m_transfers[idx].amount();
|
||||
}
|
||||
|
||||
return foundMoney;
|
||||
}
|
||||
|
||||
void WalletTransferDetails::detachTransferDetails(size_t height) {
|
||||
auto it = std::find_if(m_transfers.begin(), m_transfers.end(), [&](const TransferDetails& td){return td.blockHeight >= height;});
|
||||
size_t start = it - m_transfers.begin();
|
||||
|
||||
for(size_t i = start; i!= m_transfers.size();i++) {
|
||||
auto ki = m_keyImages.find(m_transfers[i].keyImage);
|
||||
if(ki == m_keyImages.end()) throw std::system_error(make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR));
|
||||
|
||||
m_keyImages.erase(ki);
|
||||
}
|
||||
m_transfers.erase(it, m_transfers.end());
|
||||
}
|
||||
|
||||
} /* namespace CryptoNote */
|
82
src/wallet/WalletTransferDetails.h
Normal file
82
src/wallet/WalletTransferDetails.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "cryptonote_core/cryptonote_format_utils.h"
|
||||
#include "IWallet.h"
|
||||
|
||||
namespace CryptoNote {
|
||||
|
||||
struct TransferDetails
|
||||
{
|
||||
uint64_t blockHeight;
|
||||
cryptonote::transaction tx;
|
||||
size_t internalOutputIndex;
|
||||
uint64_t globalOutputIndex;
|
||||
bool spent;
|
||||
crypto::key_image keyImage;
|
||||
|
||||
uint64_t amount() const
|
||||
{
|
||||
return tx.vout[internalOutputIndex].amount;
|
||||
}
|
||||
};
|
||||
|
||||
class WalletTransferDetails
|
||||
{
|
||||
public:
|
||||
WalletTransferDetails(const std::vector<crypto::hash>& blockchain);
|
||||
~WalletTransferDetails();
|
||||
|
||||
TransferDetails& getTransferDetails(size_t idx);
|
||||
void addTransferDetails(const TransferDetails& details);
|
||||
bool getTransferDetailsIdxByKeyImage(const crypto::key_image& image, size_t& idx);
|
||||
|
||||
uint64_t countActualBalance() const;
|
||||
uint64_t countPendingBalance() const;
|
||||
bool isTransferUnlocked(const TransferDetails& td) const;
|
||||
|
||||
uint64_t selectTransfersToSend(uint64_t neededMoney, bool addDust, uint64_t dust, std::list<size_t>& selectedTransfers);
|
||||
|
||||
void detachTransferDetails(size_t height);
|
||||
|
||||
template <typename Archive>
|
||||
void save(Archive& ar, bool saveCache) const;
|
||||
|
||||
template<typename Archive>
|
||||
void load(Archive& ar);
|
||||
|
||||
private:
|
||||
bool isTxSpendtimeUnlocked(uint64_t unlock_time) const;
|
||||
|
||||
typedef std::vector<TransferDetails> TransferContainer;
|
||||
TransferContainer m_transfers;
|
||||
|
||||
typedef std::unordered_map<crypto::key_image, size_t> KeyImagesContainer;
|
||||
KeyImagesContainer m_keyImages;
|
||||
|
||||
const std::vector<crypto::hash>& m_blockchain;
|
||||
};
|
||||
|
||||
template <typename Archive>
|
||||
void WalletTransferDetails::save(Archive& ar, bool saveCache) const
|
||||
{
|
||||
const TransferContainer& transfers = saveCache ? m_transfers : TransferContainer();
|
||||
const KeyImagesContainer& keyImages = saveCache ? m_keyImages : KeyImagesContainer();
|
||||
|
||||
ar << transfers;
|
||||
ar << keyImages;
|
||||
}
|
||||
|
||||
template<typename Archive>
|
||||
void WalletTransferDetails::load(Archive& ar)
|
||||
{
|
||||
ar >> m_transfers;
|
||||
ar >> m_keyImages;
|
||||
}
|
||||
|
||||
} //namespace CryptoNote
|
29
src/wallet/WalletTxSendingState.cpp
Normal file
29
src/wallet/WalletTxSendingState.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "WalletTxSendingState.h"
|
||||
|
||||
namespace CryptoNote {
|
||||
|
||||
void WalletTxSendingState::sending(TransactionId id) {
|
||||
m_states[id] = SENDING;
|
||||
}
|
||||
|
||||
void WalletTxSendingState::sent(TransactionId id) {
|
||||
m_states.erase(id);
|
||||
}
|
||||
|
||||
void WalletTxSendingState::error(TransactionId id) {
|
||||
m_states[id] = ERRORED;
|
||||
}
|
||||
|
||||
WalletTxSendingState::State WalletTxSendingState::state(TransactionId id) {
|
||||
auto it = m_states.find(id);
|
||||
|
||||
if (it == m_states.end())
|
||||
return NOT_FOUND;
|
||||
|
||||
return it->second;
|
||||
}
|
||||
} //namespace CryptoNote
|
33
src/wallet/WalletTxSendingState.h
Normal file
33
src/wallet/WalletTxSendingState.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IWallet.h"
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
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
|
45
src/wallet/WalletUnconfirmedTransactions.cpp
Normal file
45
src/wallet/WalletUnconfirmedTransactions.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "WalletUnconfirmedTransactions.h"
|
||||
#include "cryptonote_core/cryptonote_format_utils.h"
|
||||
|
||||
namespace CryptoNote {
|
||||
|
||||
bool WalletUnconfirmedTransactions::findTransactionId(const crypto::hash& hash, TransactionId& id) {
|
||||
auto it = m_unconfirmedTxs.find(hash);
|
||||
|
||||
if(it == m_unconfirmedTxs.end())
|
||||
return false;
|
||||
|
||||
id = it->second.transactionId;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WalletUnconfirmedTransactions::erase(const crypto::hash& hash) {
|
||||
m_unconfirmedTxs.erase(hash);
|
||||
}
|
||||
|
||||
void WalletUnconfirmedTransactions::add(const cryptonote::transaction& tx,
|
||||
TransactionId transactionId, uint64_t change_amount) {
|
||||
UnconfirmedTransferDetails& utd = m_unconfirmedTxs[cryptonote::get_transaction_hash(tx)];
|
||||
|
||||
utd.change = change_amount;
|
||||
utd.sentTime = time(NULL);
|
||||
utd.tx = tx;
|
||||
utd.transactionId = transactionId;
|
||||
}
|
||||
|
||||
uint64_t WalletUnconfirmedTransactions::countPendingBalance() const
|
||||
{
|
||||
uint64_t amount = 0;
|
||||
|
||||
for (auto& utx: m_unconfirmedTxs)
|
||||
amount+= utx.second.change;
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
} /* namespace CryptoNote */
|
59
src/wallet/WalletUnconfirmedTransactions.h
Normal file
59
src/wallet/WalletUnconfirmedTransactions.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "IWallet.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "cryptonote_core/cryptonote_basic.h"
|
||||
|
||||
namespace CryptoNote {
|
||||
|
||||
struct UnconfirmedTransferDetails
|
||||
{
|
||||
cryptonote::transaction tx;
|
||||
uint64_t change;
|
||||
time_t sentTime;
|
||||
TransactionId transactionId;
|
||||
};
|
||||
|
||||
class WalletUnconfirmedTransactions
|
||||
{
|
||||
public:
|
||||
template <typename Archive>
|
||||
void save(Archive& ar, bool saveCache) const;
|
||||
|
||||
template<typename Archive>
|
||||
void load(Archive& ar);
|
||||
|
||||
bool findTransactionId(const crypto::hash& hash, TransactionId& id);
|
||||
void erase(const crypto::hash& hash);
|
||||
void add(const cryptonote::transaction& tx, TransactionId transactionId, uint64_t change_amount);
|
||||
|
||||
uint64_t countPendingBalance() const;
|
||||
|
||||
private:
|
||||
typedef std::unordered_map<crypto::hash, UnconfirmedTransferDetails> UnconfirmedTxsContainer;
|
||||
UnconfirmedTxsContainer m_unconfirmedTxs;
|
||||
};
|
||||
|
||||
template <typename Archive>
|
||||
void WalletUnconfirmedTransactions::save(Archive& ar, bool saveCache) const
|
||||
{
|
||||
const UnconfirmedTxsContainer& unconfirmedTxs = saveCache ? m_unconfirmedTxs : UnconfirmedTxsContainer();
|
||||
ar << unconfirmedTxs;
|
||||
}
|
||||
|
||||
template<typename Archive>
|
||||
void WalletUnconfirmedTransactions::load(Archive& ar)
|
||||
{
|
||||
ar >> m_unconfirmedTxs;
|
||||
}
|
||||
|
||||
} // namespace CryptoNote
|
||||
|
166
src/wallet/WalletUserTransactionsCache.cpp
Normal file
166
src/wallet/WalletUserTransactionsCache.cpp
Normal file
|
@ -0,0 +1,166 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "WalletUserTransactionsCache.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace {
|
||||
bool hashesEqual(const CryptoNote::TransactionHash& h1, const crypto::hash& h2) {
|
||||
return !memcmp(static_cast<const void *>(h1.data()), static_cast<const void *>(&h2), h1.size());
|
||||
}
|
||||
}
|
||||
|
||||
namespace CryptoNote {
|
||||
|
||||
size_t WalletUserTransactionsCache::getTransactionCount() const
|
||||
{
|
||||
return m_transactions.size();
|
||||
}
|
||||
|
||||
size_t WalletUserTransactionsCache::getTransferCount() const
|
||||
{
|
||||
return m_transfers.size();
|
||||
}
|
||||
|
||||
TransactionId WalletUserTransactionsCache::findTransactionByTransferId(TransferId transferId) const
|
||||
{
|
||||
TransactionId id;
|
||||
for (id = 0; id < m_transactions.size(); ++id) {
|
||||
const Transaction& tx = m_transactions[id];
|
||||
|
||||
if (tx.firstTransferId == INVALID_TRANSFER_ID || tx.transferCount == 0)
|
||||
continue;
|
||||
|
||||
if (transferId >= tx.firstTransferId && transferId < (tx.firstTransferId + tx.transferCount))
|
||||
break;
|
||||
}
|
||||
|
||||
if (id == m_transactions.size())
|
||||
return INVALID_TRANSACTION_ID;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
bool WalletUserTransactionsCache::getTransaction(TransactionId transactionId, Transaction& transaction) const
|
||||
{
|
||||
if (transactionId >= m_transactions.size())
|
||||
return false;
|
||||
|
||||
transaction = m_transactions[transactionId];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WalletUserTransactionsCache::getTransfer(TransferId transferId, Transfer& transfer) const
|
||||
{
|
||||
if (transferId >= m_transfers.size())
|
||||
return false;
|
||||
|
||||
transfer = m_transfers[transferId];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TransactionId WalletUserTransactionsCache::insertTransaction(Transaction&& transaction) {
|
||||
m_transactions.emplace_back(transaction);
|
||||
return m_transactions.size() - 1;
|
||||
}
|
||||
|
||||
TransactionId WalletUserTransactionsCache::findTransactionByHash(const crypto::hash& hash) {
|
||||
auto it = std::find_if(m_transactions.begin(), m_transactions.end(), [&hash] (const Transaction& tx) { return hashesEqual(tx.hash, hash); });
|
||||
|
||||
if (it == m_transactions.end())
|
||||
return CryptoNote::INVALID_TRANSACTION_ID;
|
||||
|
||||
return std::distance(m_transactions.begin(), it);
|
||||
}
|
||||
|
||||
void WalletUserTransactionsCache::detachTransactions(uint64_t height) {
|
||||
for (size_t id = 0; id < m_transactions.size(); ++id) {
|
||||
Transaction& tx = m_transactions[id];
|
||||
if (tx.blockHeight >= height) {
|
||||
tx.blockHeight = UNCONFIRMED_TRANSACTION_HEIGHT;
|
||||
tx.timestamp = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Transaction& WalletUserTransactionsCache::getTransaction(TransactionId transactionId) {
|
||||
return m_transactions.at(transactionId);
|
||||
}
|
||||
|
||||
void WalletUserTransactionsCache::getGoodItems(bool saveDetailed, UserTransactions& transactions, UserTransfers& transfers) {
|
||||
size_t offset = 0;
|
||||
|
||||
for (size_t txId = 0; txId < m_transactions.size(); ++txId) {
|
||||
WalletTxSendingState::State state = m_sendingTxsStates.state(txId);
|
||||
bool isGood = state != WalletTxSendingState::ERRORED;
|
||||
|
||||
if (isGood) {
|
||||
getGoodTransaction(txId, offset, saveDetailed, transactions, transfers);
|
||||
}
|
||||
else
|
||||
{
|
||||
const Transaction& t = m_transactions[txId];
|
||||
if (t.firstTransferId != INVALID_TRANSFER_ID)
|
||||
offset += t.transferCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WalletUserTransactionsCache::getGoodTransaction(TransactionId txId, size_t offset, bool saveDetailed, UserTransactions& transactions, UserTransfers& transfers) {
|
||||
transactions.push_back(m_transactions[txId]);
|
||||
Transaction& tx = transactions.back();
|
||||
|
||||
if (!saveDetailed) {
|
||||
tx.firstTransferId = INVALID_TRANSFER_ID;
|
||||
tx.transferCount = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (tx.firstTransferId == INVALID_TRANSFER_ID) {
|
||||
return;
|
||||
}
|
||||
|
||||
UserTransfers::const_iterator first = m_transfers.begin() + tx.firstTransferId;
|
||||
UserTransfers::const_iterator last = first + tx.transferCount;
|
||||
|
||||
tx.firstTransferId -= offset;
|
||||
|
||||
std::copy(first, last, std::back_inserter(transfers));
|
||||
}
|
||||
|
||||
void WalletUserTransactionsCache::getGoodTransfers(UserTransfers& transfers) {
|
||||
for (size_t txId = 0; txId < m_transactions.size(); ++txId) {
|
||||
WalletTxSendingState::State state = m_sendingTxsStates.state(txId);
|
||||
|
||||
if (state != WalletTxSendingState::ERRORED) {
|
||||
getTransfersByTx(txId, transfers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WalletUserTransactionsCache::getTransfersByTx(TransactionId id, UserTransfers& transfers) {
|
||||
const Transaction& tx = m_transactions[id];
|
||||
|
||||
if (tx.firstTransferId != INVALID_TRANSFER_ID) {
|
||||
UserTransfers::const_iterator first = m_transfers.begin() + tx.firstTransferId;
|
||||
UserTransfers::const_iterator last = first + tx.transferCount;
|
||||
std::copy(first, last, std::back_inserter(transfers));
|
||||
}
|
||||
}
|
||||
|
||||
TransferId WalletUserTransactionsCache::insertTransfers(const std::vector<Transfer>& transfers) {
|
||||
std::copy(transfers.begin(), transfers.end(), std::back_inserter(m_transfers));
|
||||
return m_transfers.size() - transfers.size();
|
||||
}
|
||||
|
||||
Transfer& WalletUserTransactionsCache::getTransfer(TransferId transferId) {
|
||||
return m_transfers.at(transferId);
|
||||
}
|
||||
|
||||
} //namespace CryptoNote
|
||||
|
77
src/wallet/WalletUserTransactionsCache.h
Normal file
77
src/wallet/WalletUserTransactionsCache.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "crypto/hash.h"
|
||||
#include "IWallet.h"
|
||||
#include "WalletTxSendingState.h"
|
||||
|
||||
namespace CryptoNote {
|
||||
|
||||
class WalletUserTransactionsCache
|
||||
{
|
||||
public:
|
||||
WalletUserTransactionsCache(WalletTxSendingState& states) : m_sendingTxsStates(states) {}
|
||||
|
||||
template <typename Archive>
|
||||
void save(Archive& ar, bool saveDetailed, bool saveCache);
|
||||
|
||||
template<typename Archive>
|
||||
void load(Archive& ar);
|
||||
|
||||
size_t getTransactionCount() const;
|
||||
size_t getTransferCount() const;
|
||||
|
||||
TransactionId findTransactionByTransferId(TransferId transferId) const;
|
||||
|
||||
bool getTransaction(TransactionId transactionId, Transaction& transaction) const;
|
||||
Transaction& getTransaction(TransactionId transactionId);
|
||||
bool getTransfer(TransferId transferId, Transfer& transfer) const;
|
||||
Transfer& getTransfer(TransferId transferId);
|
||||
|
||||
TransactionId insertTransaction(Transaction&& transaction);
|
||||
TransferId insertTransfers(const std::vector<Transfer>& transfers);
|
||||
|
||||
TransactionId findTransactionByHash(const crypto::hash& hash);
|
||||
void detachTransactions(uint64_t height);
|
||||
|
||||
private:
|
||||
typedef std::vector<Transfer> UserTransfers;
|
||||
typedef std::vector<Transaction> UserTransactions;
|
||||
|
||||
void getGoodItems(bool saveDetailed, UserTransactions& transactions, UserTransfers& transfers);
|
||||
void getGoodTransaction(TransactionId txId, size_t offset, bool saveDetailed, UserTransactions& transactions, UserTransfers& transfers);
|
||||
|
||||
void getGoodTransfers(UserTransfers& transfers);
|
||||
void getTransfersByTx(TransactionId id, UserTransfers& transfers);
|
||||
|
||||
UserTransactions m_transactions;
|
||||
UserTransfers m_transfers;
|
||||
|
||||
WalletTxSendingState& m_sendingTxsStates;
|
||||
};
|
||||
|
||||
template<typename Archive>
|
||||
void WalletUserTransactionsCache::load(Archive& ar) {
|
||||
ar >> m_transactions;
|
||||
ar >> m_transfers;
|
||||
}
|
||||
|
||||
template <typename Archive>
|
||||
void WalletUserTransactionsCache::save(Archive& ar, bool saveDetailed, bool saveCache) {
|
||||
UserTransactions txsToSave;
|
||||
UserTransfers transfersToSave;
|
||||
|
||||
if (saveCache) {
|
||||
getGoodItems(saveDetailed, txsToSave, transfersToSave);
|
||||
} else {
|
||||
if (saveDetailed) getGoodTransfers(transfersToSave);
|
||||
}
|
||||
|
||||
ar << txsToSave;
|
||||
ar << transfersToSave;
|
||||
}
|
||||
|
||||
} //namespace CryptoNote
|
19
src/wallet/WalletUtils.h
Normal file
19
src/wallet/WalletUtils.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <exception>
|
||||
#include "WalletErrors.h"
|
||||
|
||||
namespace CryptoNote {
|
||||
|
||||
inline void throwIf(bool expr, cryptonote::error::WalletErrorCodes ec)
|
||||
{
|
||||
if (expr)
|
||||
throw std::system_error(make_error_code(ec));
|
||||
}
|
||||
|
||||
} //namespace CryptoNote
|
||||
|
|
@ -12,6 +12,7 @@ using namespace epee;
|
|||
|
||||
#include "wallet2.h"
|
||||
#include "cryptonote_core/cryptonote_format_utils.h"
|
||||
#include "cryptonote_protocol/blobdatatype.h"
|
||||
#include "rpc/core_rpc_server_commands_defs.h"
|
||||
#include "misc_language.h"
|
||||
#include "cryptonote_core/cryptonote_basic_impl.h"
|
||||
|
@ -387,7 +388,8 @@ bool wallet2::store_keys(const std::string& keys_file_name, const std::string& p
|
|||
wallet2::keys_file_data keys_file_data = boost::value_initialized<wallet2::keys_file_data>();
|
||||
|
||||
crypto::chacha8_key key;
|
||||
crypto::generate_chacha8_key(password, key);
|
||||
crypto::cn_context cn_context;
|
||||
crypto::generate_chacha8_key(cn_context, password, key);
|
||||
std::string cipher;
|
||||
cipher.resize(account_data.size());
|
||||
keys_file_data.iv = crypto::rand<crypto::chacha8_iv>();
|
||||
|
@ -422,7 +424,8 @@ void wallet2::load_keys(const std::string& keys_file_name, const std::string& pa
|
|||
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"');
|
||||
|
||||
crypto::chacha8_key key;
|
||||
crypto::generate_chacha8_key(password, key);
|
||||
crypto::cn_context cn_context;
|
||||
crypto::generate_chacha8_key(cn_context, password, key);
|
||||
std::string account_data;
|
||||
account_data.resize(keys_file_data.account_data.size());
|
||||
crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
|
||||
|
@ -465,6 +468,20 @@ void wallet2::wallet_exists(const std::string& file_path, bool& keys_file_exists
|
|||
wallet_file_exists = boost::filesystem::exists(wallet_file, ignore);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id) {
|
||||
cryptonote::blobdata payment_id_data;
|
||||
if (!epee::string_tools::parse_hexstr_to_binbuff(payment_id_str, payment_id_data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sizeof(crypto::hash) != payment_id_data.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
payment_id = *reinterpret_cast<const crypto::hash*>(payment_id_data.data());
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::prepare_file_names(const std::string& file_path)
|
||||
{
|
||||
do_prepare_file_names(file_path, m_keys_file, m_wallet_file);
|
||||
|
@ -663,7 +680,7 @@ void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t cha
|
|||
void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count,
|
||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx)
|
||||
{
|
||||
transfer(dsts, fake_outputs_count, unlock_time, fee, extra, detail::digit_split_strategy, tx_dust_policy(fee), tx);
|
||||
transfer(dsts, fake_outputs_count, unlock_time, fee, extra, detail::digit_split_strategy, tx_dust_policy(DEFAULT_DUST_THRESHOLD), tx);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count,
|
||||
|
|
|
@ -145,6 +145,7 @@ namespace tools
|
|||
}
|
||||
|
||||
static void wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exists);
|
||||
static bool parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id);
|
||||
|
||||
private:
|
||||
bool store_keys(const std::string& keys_file_name, const std::string& password);
|
||||
|
|
|
@ -73,13 +73,10 @@ namespace tools
|
|||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx)
|
||||
{
|
||||
|
||||
std::vector<cryptonote::tx_destination_entry> dsts;
|
||||
for (auto it = req.destinations.begin(); it != req.destinations.end(); it++)
|
||||
{
|
||||
for (auto it = req.destinations.begin(); it != req.destinations.end(); it++) {
|
||||
cryptonote::tx_destination_entry de;
|
||||
if(!get_account_address_from_str(de.addr, it->address))
|
||||
{
|
||||
if (!get_account_address_from_str(de.addr, it->address)) {
|
||||
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
|
||||
er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + it->address;
|
||||
return false;
|
||||
|
@ -87,27 +84,41 @@ namespace tools
|
|||
de.amount = it->amount;
|
||||
dsts.push_back(de);
|
||||
}
|
||||
try
|
||||
{
|
||||
|
||||
std::vector<uint8_t> extra;
|
||||
if (!req.payment_id.empty()) {
|
||||
std::string payment_id_str = req.payment_id;
|
||||
|
||||
crypto::hash payment_id;
|
||||
if (!wallet2::parse_payment_id(payment_id_str, payment_id)) {
|
||||
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
|
||||
er.message = "Payment id has invalid format: \"" + payment_id_str + "\", expected 64-character string";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string extra_nonce;
|
||||
cryptonote::set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
|
||||
if (!cryptonote::add_extra_nonce_to_tx_extra(extra, extra_nonce)) {
|
||||
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
|
||||
er.message = "Something went wrong with payment_id. Please check its format: \"" + payment_id_str + "\", expected 64-character string";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
cryptonote::transaction tx;
|
||||
m_wallet.transfer(dsts, req.mixin, req.unlock_time, req.fee, std::vector<uint8_t>(), tx);
|
||||
m_wallet.transfer(dsts, req.mixin, req.unlock_time, req.fee, extra, tx);
|
||||
res.tx_hash = boost::lexical_cast<std::string>(cryptonote::get_transaction_hash(tx));
|
||||
return true;
|
||||
}
|
||||
catch (const tools::error::daemon_busy& e)
|
||||
{
|
||||
} catch (const tools::error::daemon_busy& e) {
|
||||
er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY;
|
||||
er.message = e.what();
|
||||
return false;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
} catch (const std::exception& e) {
|
||||
er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR;
|
||||
er.message = e.what();
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
} catch (...) {
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR";
|
||||
return false;
|
||||
|
|
|
@ -52,12 +52,14 @@ namespace wallet_rpc
|
|||
uint64_t fee;
|
||||
uint64_t mixin;
|
||||
uint64_t unlock_time;
|
||||
std::string payment_id;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(destinations)
|
||||
KV_SERIALIZE(fee)
|
||||
KV_SERIALIZE(mixin)
|
||||
KV_SERIALIZE(unlock_time)
|
||||
KV_SERIALIZE(payment_id)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
add_definitions(-DSTATICLIB)
|
||||
|
||||
add_subdirectory(gtest)
|
||||
include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
|
||||
include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR} ../version)
|
||||
|
||||
file(GLOB_RECURSE CORE_TESTS core_tests/*)
|
||||
file(GLOB_RECURSE CRYPTO_TESTS crypto/*)
|
||||
|
@ -19,6 +19,8 @@ source_group(unit_tests FILES ${UNIT_TESTS})
|
|||
|
||||
# add_subdirectory(daemon_tests)
|
||||
|
||||
add_library(coretests_lib ${CORE_TESTS})
|
||||
|
||||
add_executable(coretests ${CORE_TESTS})
|
||||
add_executable(crypto-tests ${CRYPTO_TESTS})
|
||||
add_executable(difficulty-tests difficulty/difficulty.cpp)
|
||||
|
@ -38,16 +40,24 @@ target_link_libraries(functional_tests cryptonote_core wallet common crypto ${Bo
|
|||
target_link_libraries(hash-tests crypto)
|
||||
target_link_libraries(hash-target-tests crypto cryptonote_core)
|
||||
target_link_libraries(performance_tests cryptonote_core common crypto ${Boost_LIBRARIES})
|
||||
target_link_libraries(unit_tests cryptonote_core common crypto gtest_main ${Boost_LIBRARIES})
|
||||
target_link_libraries(unit_tests cryptonote_core common crypto wallet coretests_lib gtest_main ${Boost_LIBRARIES})
|
||||
target_link_libraries(net_load_tests_clt cryptonote_core common crypto gtest_main ${Boost_LIBRARIES})
|
||||
target_link_libraries(net_load_tests_srv cryptonote_core common crypto gtest_main ${Boost_LIBRARIES})
|
||||
|
||||
|
||||
|
||||
file(GLOB_RECURSE NODE_RPC_PROXY_TEST node_rpc_proxy_test/*)
|
||||
source_group(node_rpc_proxy_test FILES ${NODE_RPC_PROXY_TEST})
|
||||
add_executable(node_rpc_proxy_test ${NODE_RPC_PROXY_TEST})
|
||||
target_link_libraries(node_rpc_proxy_test node_rpc_proxy cryptonote_core common crypto ${Boost_LIBRARIES})
|
||||
|
||||
|
||||
if(NOT MSVC)
|
||||
set_property(TARGET gtest gtest_main unit_tests net_load_tests_clt net_load_tests_srv APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-sign-compare")
|
||||
endif()
|
||||
|
||||
add_custom_target(tests DEPENDS coretests difficulty hash performance_tests core_proxy unit_tests)
|
||||
set_property(TARGET coretests crypto-tests functional_tests difficulty-tests gtest gtest_main hash-tests hash-target-tests performance_tests core_proxy unit_tests tests net_load_tests_clt net_load_tests_srv PROPERTY FOLDER "tests")
|
||||
add_custom_target(tests DEPENDS coretests difficulty hash performance_tests core_proxy unit_tests node_rpc_proxy_test)
|
||||
set_property(TARGET coretests crypto-tests functional_tests difficulty-tests gtest gtest_main hash-tests hash-target-tests performance_tests core_proxy unit_tests tests net_load_tests_clt net_load_tests_srv node_rpc_proxy_test PROPERTY FOLDER "tests")
|
||||
|
||||
add_test(coretests coretests --generate_and_play_test_data)
|
||||
add_test(crypto crypto-tests ${CMAKE_CURRENT_SOURCE_DIR}/crypto/tests.txt)
|
||||
|
|
|
@ -172,7 +172,7 @@ bool tests::proxy_core::handle_incoming_block(const cryptonote::blobdata& block_
|
|||
crypto::hash lh;
|
||||
cout << "BLOCK" << endl << endl;
|
||||
cout << (h = get_block_hash(b)) << endl;
|
||||
cout << (lh = get_block_longhash(b, 0)) << endl;
|
||||
cout << (lh = get_block_longhash(m_cn_context, b, 0)) << endl;
|
||||
cout << get_transaction_hash(b.miner_tx) << endl;
|
||||
cout << ::get_object_blobsize(b.miner_tx) << endl;
|
||||
//cout << string_tools::buff_to_hex_nodelimer(block_blob) << endl;
|
||||
|
@ -200,7 +200,7 @@ bool tests::proxy_core::get_blockchain_top(uint64_t& height, crypto::hash& top_i
|
|||
bool tests::proxy_core::init(const boost::program_options::variables_map& /*vm*/) {
|
||||
generate_genesis_block(m_genesis);
|
||||
crypto::hash h = get_block_hash(m_genesis);
|
||||
add_block(h, get_block_longhash(m_genesis, 0), m_genesis, block_to_blob(m_genesis));
|
||||
add_block(h, get_block_longhash(m_cn_context, m_genesis, 0), m_genesis, block_to_blob(m_genesis));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ namespace tests
|
|||
crypto::hash m_lastblk;
|
||||
std::list<cryptonote::transaction> txes;
|
||||
|
||||
crypto::cn_context m_cn_context;
|
||||
|
||||
bool add_block(const crypto::hash &_id, const crypto::hash &_longhash, const cryptonote::block &_blk, const cryptonote::blobdata &_blob);
|
||||
void build_short_history(std::list<crypto::hash> &m_history, const crypto::hash &m_start);
|
||||
|
||||
|
|
|
@ -120,8 +120,7 @@ bool gen_chain_switch_1::check_split_not_switched(cryptonote::core& c, size_t ev
|
|||
CHECK_EQ(MK_COINS(3), get_balance(m_recipient_account_4, chain, mtx));
|
||||
|
||||
std::list<transaction> tx_pool;
|
||||
r = c.get_pool_transactions(tx_pool);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
c.get_pool_transactions(tx_pool);
|
||||
CHECK_EQ(1, tx_pool.size());
|
||||
|
||||
std::vector<size_t> tx_outs;
|
||||
|
@ -170,8 +169,7 @@ bool gen_chain_switch_1::check_split_switched(cryptonote::core& c, size_t ev_ind
|
|||
CHECK_EQ(MK_COINS(16), get_balance(m_recipient_account_4, chain, mtx));
|
||||
|
||||
std::list<transaction> tx_pool;
|
||||
r = c.get_pool_transactions(tx_pool);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
c.get_pool_transactions(tx_pool);
|
||||
CHECK_EQ(1, tx_pool.size());
|
||||
CHECK_TEST_CONDITION(!(tx_pool.front() == m_tx_pool.front()));
|
||||
|
||||
|
|
|
@ -153,7 +153,8 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co
|
|||
|
||||
// Nonce search...
|
||||
blk.nonce = 0;
|
||||
while (!miner::find_nonce_for_given_block(blk, get_test_difficulty(), height))
|
||||
crypto::cn_context context;
|
||||
while (!miner::find_nonce_for_given_block(context, blk, get_test_difficulty(), height))
|
||||
blk.timestamp++;
|
||||
|
||||
add_block(blk, txs_size, block_sizes, already_generated_coins);
|
||||
|
@ -479,7 +480,8 @@ void fill_tx_sources_and_destinations(const std::vector<test_event_entry>& event
|
|||
void fill_nonce(cryptonote::block& blk, const difficulty_type& diffic, uint64_t height)
|
||||
{
|
||||
blk.nonce = 0;
|
||||
while (!miner::find_nonce_for_given_block(blk, diffic, height))
|
||||
crypto::cn_context context;
|
||||
while (!miner::find_nonce_for_given_block(context, blk, diffic, height))
|
||||
blk.timestamp++;
|
||||
}
|
||||
|
||||
|
|
|
@ -461,7 +461,7 @@ inline bool do_replay_events(std::vector<test_event_entry>& events)
|
|||
|
||||
cryptonote::cryptonote_protocol_stub pr; //TODO: stub only for this kind of test, make real validation of relayed objects
|
||||
cryptonote::core c(&pr);
|
||||
if (!c.init(vm))
|
||||
if (!c.init(vm, false))
|
||||
{
|
||||
std::cout << concolor::magenta << "Failed to init core" << concolor::normal << std::endl;
|
||||
return false;
|
||||
|
@ -643,4 +643,4 @@ inline bool do_replay_file(const std::string& filename)
|
|||
#define CHECK_EQ(v1, v2) CHECK_AND_ASSERT_MES(v1 == v2, false, "[" << perr_context << "] failed: \"" << QUOTEME(v1) << " == " << QUOTEME(v2) << "\", " << v1 << " != " << v2)
|
||||
#define CHECK_NOT_EQ(v1, v2) CHECK_AND_ASSERT_MES(!(v1 == v2), false, "[" << perr_context << "] failed: \"" << QUOTEME(v1) << " != " << QUOTEME(v2) << "\", " << v1 << " == " << v2)
|
||||
#define MK_COINS(amount) (UINT64_C(amount) * COIN)
|
||||
#define TESTS_DEFAULT_FEE ((uint64_t)1000000) // pow(10, 6)
|
||||
#define TESTS_DEFAULT_FEE (MINIMUM_FEE)
|
||||
|
|
|
@ -99,7 +99,7 @@ struct gen_double_spend_in_alt_chain_in_different_blocks : public gen_double_spe
|
|||
class gen_double_spend_in_different_chains : public test_chain_unit_base
|
||||
{
|
||||
public:
|
||||
static const uint64_t send_amount = MK_COINS(17);
|
||||
static const uint64_t send_amount = MK_COINS(31);
|
||||
static const size_t expected_blockchain_height = 4 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
|
||||
|
||||
gen_double_spend_in_different_chains();
|
||||
|
|
|
@ -52,7 +52,7 @@ bool do_send_money(tools::wallet2& w1, tools::wallet2& w2, size_t mix_in_factor,
|
|||
|
||||
try
|
||||
{
|
||||
w1.transfer(dsts, mix_in_factor, 0, DEFAULT_FEE, std::vector<uint8_t>(), tools::detail::null_split_strategy, tools::tx_dust_policy(DEFAULT_FEE), tx);
|
||||
w1.transfer(dsts, mix_in_factor, 0, MINIMUM_FEE, std::vector<uint8_t>(), tools::detail::null_split_strategy, tools::tx_dust_policy(DEFAULT_DUST_THRESHOLD), tx);
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception&)
|
||||
|
@ -157,7 +157,7 @@ bool transactions_flow_test(std::string& working_folder,
|
|||
BOOST_FOREACH(tools::wallet2::transfer_details& td, incoming_transfers)
|
||||
{
|
||||
cryptonote::transaction tx_s;
|
||||
bool r = do_send_money(w1, w1, 0, td.m_tx.vout[td.m_internal_output_index].amount - DEFAULT_FEE, tx_s, 50);
|
||||
bool r = do_send_money(w1, w1, 0, td.m_tx.vout[td.m_internal_output_index].amount - MINIMUM_FEE, tx_s, 50);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to send starter tx " << get_transaction_hash(tx_s));
|
||||
LOG_PRINT_GREEN("Starter transaction sent " << get_transaction_hash(tx_s), LOG_LEVEL_0);
|
||||
if(++count >= FIRST_N_TRANSFERS)
|
||||
|
@ -185,7 +185,7 @@ bool transactions_flow_test(std::string& working_folder,
|
|||
for(i = 0; i != transactions_count; i++)
|
||||
{
|
||||
uint64_t amount_to_tx = (amount_to_transfer - transfered_money) > transfer_size ? transfer_size: (amount_to_transfer - transfered_money);
|
||||
while(w1.unlocked_balance() < amount_to_tx + DEFAULT_FEE)
|
||||
while(w1.unlocked_balance() < amount_to_tx + MINIMUM_FEE)
|
||||
{
|
||||
misc_utils::sleep_no_w(1000);
|
||||
LOG_PRINT_L0("not enough money, waiting for cashback or mining");
|
||||
|
|
|
@ -16,23 +16,30 @@ using namespace std;
|
|||
using namespace crypto;
|
||||
typedef crypto::hash chash;
|
||||
|
||||
cn_context *context;
|
||||
|
||||
extern "C" {
|
||||
|
||||
PUSH_WARNINGS
|
||||
DISABLE_VS_WARNINGS(4297)
|
||||
extern "C" {
|
||||
static void hash_tree(const void *data, size_t length, char *hash) {
|
||||
if ((length & 31) != 0) {
|
||||
throw ios_base::failure("Invalid input length for tree_hash");
|
||||
}
|
||||
tree_hash((const char (*)[32]) data, length >> 5, hash);
|
||||
}
|
||||
}
|
||||
POP_WARNINGS
|
||||
|
||||
static void slow_hash(const void *data, size_t length, char *hash) {
|
||||
cn_slow_hash(*context, data, length, *reinterpret_cast<chash *>(hash));
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" typedef void hash_f(const void *, size_t, char *);
|
||||
struct hash_func {
|
||||
const string name;
|
||||
hash_f &f;
|
||||
} hashes[] = {{"fast", cn_fast_hash}, {"slow", cn_slow_hash}, {"tree", hash_tree},
|
||||
} hashes[] = {{"fast", cn_fast_hash}, {"slow", slow_hash}, {"tree", hash_tree},
|
||||
{"extra-blake", hash_extra_blake}, {"extra-groestl", hash_extra_groestl},
|
||||
{"extra-jh", hash_extra_jh}, {"extra-skein", hash_extra_skein}};
|
||||
|
||||
|
@ -58,6 +65,9 @@ int main(int argc, char *argv[]) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (f == slow_hash) {
|
||||
context = new cn_context();
|
||||
}
|
||||
input.open(argv[2], ios_base::in);
|
||||
for (;;) {
|
||||
++test;
|
||||
|
|
|
@ -80,7 +80,7 @@ inline void get(std::istream &input, std::vector<char> &res) {
|
|||
}
|
||||
}
|
||||
|
||||
#if !defined(_MSC_VER)
|
||||
#if !defined(_MSC_VER) || _MSC_VER >= 1800
|
||||
|
||||
template<typename T, typename... TT>
|
||||
typename std::enable_if<(sizeof...(TT) > 0), void>::type
|
||||
|
|
132
tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp
Normal file
132
tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp
Normal file
|
@ -0,0 +1,132 @@
|
|||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "include_base_utils.h"
|
||||
|
||||
#include "node_rpc_proxy/NodeRpcProxy.h"
|
||||
|
||||
using namespace cryptonote;
|
||||
using namespace CryptoNote;
|
||||
|
||||
|
||||
class NodeObserver : public INodeObserver {
|
||||
public:
|
||||
NodeObserver(const std::string& name, NodeRpcProxy& nodeProxy)
|
||||
: m_name(name)
|
||||
, m_nodeProxy(nodeProxy) {
|
||||
}
|
||||
|
||||
virtual ~NodeObserver() {
|
||||
}
|
||||
|
||||
virtual void peerCountUpdated(size_t count) {
|
||||
LOG_PRINT_L0('[' << m_name << "] peerCountUpdated " << count << " = " << m_nodeProxy.getPeerCount());
|
||||
}
|
||||
|
||||
virtual void localBlockchainUpdated(uint64_t height) {
|
||||
LOG_PRINT_L0('[' << m_name << "] localBlockchainUpdated " << height << " = " << m_nodeProxy.getLastLocalBlockHeight());
|
||||
|
||||
std::vector<uint64_t> amounts;
|
||||
amounts.push_back(100000000);
|
||||
auto outs = std::make_shared<std::vector<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount>>();
|
||||
m_nodeProxy.getRandomOutsByAmounts(std::move(amounts), 10, *outs.get(), [outs](std::error_code ec) {
|
||||
if (!ec) {
|
||||
if (1 == outs->size() && 10 == (*outs)[0].outs.size()) {
|
||||
LOG_PRINT_L0("getRandomOutsByAmounts called successfully");
|
||||
} else {
|
||||
LOG_PRINT_RED_L0("getRandomOutsByAmounts returned invalid result");
|
||||
}
|
||||
} else {
|
||||
LOG_PRINT_RED_L0("failed to call getRandomOutsByAmounts: " << ec.message() << ':' << ec.value());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
virtual void lastKnownBlockHeightUpdated(uint64_t height) {
|
||||
LOG_PRINT_L0('[' << m_name << "] lastKnownBlockHeightUpdated " << height << " = " << m_nodeProxy.getLastKnownBlockHeight());
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
NodeRpcProxy& m_nodeProxy;
|
||||
};
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
//set up logging options
|
||||
epee::log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2);
|
||||
epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL);
|
||||
|
||||
NodeRpcProxy nodeProxy("127.0.0.1", 18081);
|
||||
|
||||
NodeObserver observer1("obs1", nodeProxy);
|
||||
NodeObserver observer2("obs2", nodeProxy);
|
||||
|
||||
nodeProxy.addObserver(&observer1);
|
||||
nodeProxy.addObserver(&observer2);
|
||||
|
||||
nodeProxy.init([](std::error_code ec) {
|
||||
if (ec) {
|
||||
LOG_PRINT_RED_L0("init error: " << ec.message() << ':' << ec.value());
|
||||
} else {
|
||||
LOG_PRINT_GREEN("initialized", LOG_LEVEL_0);
|
||||
}
|
||||
});
|
||||
|
||||
//nodeProxy.init([](std::error_code ec) {
|
||||
// if (ec) {
|
||||
// LOG_PRINT_RED_L0("init error: " << ec.message() << ':' << ec.value());
|
||||
// } else {
|
||||
// LOG_PRINT_GREEN("initialized", LOG_LEVEL_0);
|
||||
// }
|
||||
//});
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
if (nodeProxy.shutdown()) {
|
||||
LOG_PRINT_GREEN("shutdown", LOG_LEVEL_0);
|
||||
} else {
|
||||
LOG_PRINT_RED_L0("shutdown error");
|
||||
}
|
||||
|
||||
nodeProxy.init([](std::error_code ec) {
|
||||
if (ec) {
|
||||
LOG_PRINT_RED_L0("init error: " << ec.message() << ':' << ec.value());
|
||||
} else {
|
||||
LOG_PRINT_GREEN("initialized", LOG_LEVEL_0);
|
||||
}
|
||||
});
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
if (nodeProxy.shutdown()) {
|
||||
LOG_PRINT_GREEN("shutdown", LOG_LEVEL_0);
|
||||
} else {
|
||||
LOG_PRINT_RED_L0("shutdown error");
|
||||
}
|
||||
|
||||
cryptonote::transaction tx;
|
||||
nodeProxy.relayTransaction(tx, [](std::error_code ec) {
|
||||
if (!ec) {
|
||||
LOG_PRINT_L0("relayTransaction called successfully");
|
||||
} else {
|
||||
LOG_PRINT_RED_L0("failed to call relayTransaction: " << ec.message() << ':' << ec.value());
|
||||
}
|
||||
});
|
||||
|
||||
nodeProxy.init([](std::error_code ec) {
|
||||
if (ec) {
|
||||
LOG_PRINT_RED_L0("init error: " << ec.message() << ':' << ec.value());
|
||||
} else {
|
||||
LOG_PRINT_GREEN("initialized", LOG_LEVEL_0);
|
||||
}
|
||||
});
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
nodeProxy.relayTransaction(tx, [](std::error_code ec) {
|
||||
if (!ec) {
|
||||
LOG_PRINT_L0("relayTransaction called successfully");
|
||||
} else {
|
||||
LOG_PRINT_RED_L0("failed to call relayTransaction: " << ec.message() << ':' << ec.value());
|
||||
}
|
||||
});
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(60));
|
||||
}
|
|
@ -35,11 +35,12 @@ public:
|
|||
bool test()
|
||||
{
|
||||
crypto::hash hash;
|
||||
crypto::cn_slow_hash(&m_data, sizeof(m_data), hash);
|
||||
crypto::cn_slow_hash(m_context, &m_data, sizeof(m_data), hash);
|
||||
return hash == m_expected_hash;
|
||||
}
|
||||
|
||||
private:
|
||||
data_t m_data;
|
||||
crypto::hash m_expected_hash;
|
||||
crypto::cn_context m_context;
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "performance_tests.h"
|
||||
#include "performance_utils.h"
|
||||
#include "crypto/hash.h"
|
||||
|
||||
// tests
|
||||
#include "construct_tx.h"
|
||||
|
|
128
tests/unit_tests/INodeStubs.cpp
Normal file
128
tests/unit_tests/INodeStubs.cpp
Normal file
|
@ -0,0 +1,128 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "INodeStubs.h"
|
||||
#include "cryptonote_core/cryptonote_format_utils.h"
|
||||
#include "wallet/WalletErrors.h"
|
||||
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <iterator>
|
||||
#include <cassert>
|
||||
|
||||
#include "crypto/crypto.h"
|
||||
|
||||
void INodeTrivialRefreshStub::getNewBlocks(std::list<crypto::hash>&& knownBlockIds, std::list<cryptonote::block_complete_entry>& newBlocks, uint64_t& startHeight, const Callback& callback)
|
||||
{
|
||||
std::thread task(&INodeTrivialRefreshStub::doGetNewBlocks, this, knownBlockIds, std::ref(newBlocks), std::ref(startHeight), callback);
|
||||
task.detach();
|
||||
}
|
||||
|
||||
void INodeTrivialRefreshStub::doGetNewBlocks(std::list<crypto::hash> knownBlockIds, std::list<cryptonote::block_complete_entry>& newBlocks, uint64_t& startHeight, const Callback& callback)
|
||||
{
|
||||
auto& blockchain = m_blockchainGenerator.getBlockchain();
|
||||
|
||||
startHeight = m_lastHeight;
|
||||
|
||||
for (; m_lastHeight < blockchain.size(); ++m_lastHeight)
|
||||
{
|
||||
cryptonote::block_complete_entry e;
|
||||
e.block = cryptonote::t_serializable_object_to_blob(blockchain[m_lastHeight]);
|
||||
|
||||
for (auto hash: blockchain[m_lastHeight].tx_hashes)
|
||||
{
|
||||
cryptonote::transaction tx;
|
||||
if (!m_blockchainGenerator.getTransactionByHash(hash, tx))
|
||||
continue;
|
||||
|
||||
e.txs.push_back(t_serializable_object_to_blob(tx));
|
||||
}
|
||||
|
||||
newBlocks.push_back(e);
|
||||
}
|
||||
|
||||
m_lastHeight = blockchain.size() - 1;
|
||||
|
||||
callback(std::error_code());
|
||||
}
|
||||
|
||||
void INodeTrivialRefreshStub::getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector<uint64_t>& outsGlobalIndices, const Callback& callback)
|
||||
{
|
||||
std::thread task(&INodeTrivialRefreshStub::doGetTransactionOutsGlobalIndices, this, transactionHash, std::ref(outsGlobalIndices), callback);
|
||||
task.detach();
|
||||
}
|
||||
|
||||
void INodeTrivialRefreshStub::doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector<uint64_t>& outsGlobalIndices, const Callback& callback)
|
||||
{
|
||||
outsGlobalIndices.resize(20); //random
|
||||
callback(std::error_code());
|
||||
}
|
||||
|
||||
void INodeTrivialRefreshStub::relayTransaction(const cryptonote::transaction& transaction, const Callback& callback)
|
||||
{
|
||||
std::thread task(&INodeTrivialRefreshStub::doRelayTransaction, this, transaction, callback);
|
||||
task.detach();
|
||||
}
|
||||
|
||||
void INodeTrivialRefreshStub::doRelayTransaction(const cryptonote::transaction& transaction, const Callback& callback)
|
||||
{
|
||||
if (m_nextTxError)
|
||||
{
|
||||
callback(make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR));
|
||||
m_nextTxError = false;
|
||||
return;
|
||||
}
|
||||
m_blockchainGenerator.addTxToBlockchain(transaction);
|
||||
callback(std::error_code());
|
||||
}
|
||||
|
||||
void INodeTrivialRefreshStub::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)
|
||||
{
|
||||
std::thread task(&INodeTrivialRefreshStub::doGetRandomOutsByAmounts, this, amounts, outsCount, std::ref(result), callback);
|
||||
task.detach();
|
||||
}
|
||||
|
||||
void INodeTrivialRefreshStub::doGetRandomOutsByAmounts(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)
|
||||
{
|
||||
for (uint64_t amount: amounts)
|
||||
{
|
||||
cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount out;
|
||||
out.amount = amount;
|
||||
|
||||
for (uint64_t i = 0; i < outsCount; ++i)
|
||||
{
|
||||
crypto::public_key key;
|
||||
crypto::secret_key sk;
|
||||
generate_keys(key, sk);
|
||||
|
||||
cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry e;
|
||||
e.global_amount_index = i;
|
||||
e.out_key = key;
|
||||
|
||||
out.outs.push_back(e);
|
||||
}
|
||||
}
|
||||
|
||||
callback(std::error_code());
|
||||
}
|
||||
|
||||
void INodeTrivialRefreshStub::startAlternativeChain(uint64_t height)
|
||||
{
|
||||
std::vector<cryptonote::block>& blockchain = m_blockchainGenerator.getBlockchain();
|
||||
|
||||
assert(height < blockchain.size());
|
||||
assert(height > m_lastHeight);
|
||||
|
||||
auto it = blockchain.begin();
|
||||
std::advance(it, height);
|
||||
|
||||
blockchain.erase(it, blockchain.end());
|
||||
|
||||
m_lastHeight = height;
|
||||
}
|
||||
|
||||
void INodeTrivialRefreshStub::setNextTransactionError()
|
||||
{
|
||||
m_nextTxError = true;
|
||||
}
|
59
tests/unit_tests/INodeStubs.h
Normal file
59
tests/unit_tests/INodeStubs.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "INode.h"
|
||||
#include "cryptonote_core/cryptonote_basic.h"
|
||||
#include "TestBlockchainGenerator.h"
|
||||
|
||||
class INodeDummyStub : public CryptoNote::INode
|
||||
{
|
||||
public:
|
||||
virtual bool addObserver(CryptoNote::INodeObserver* observer) { return true; };
|
||||
virtual bool removeObserver(CryptoNote::INodeObserver* observer) { return true; };
|
||||
|
||||
virtual void init(const CryptoNote::INode::Callback& callback) {callback(std::error_code());};
|
||||
virtual bool shutdown() { return true; };
|
||||
|
||||
virtual size_t getPeerCount() const { return 0; };
|
||||
virtual uint64_t getLastLocalBlockHeight() const { return 0; };
|
||||
virtual uint64_t getLastKnownBlockHeight() const { return 0; };
|
||||
|
||||
virtual void getNewBlocks(std::list<crypto::hash>&& knownBlockIds, std::list<cryptonote::block_complete_entry>& newBlocks, uint64_t& startHeight, const Callback& callback) {callback(std::error_code());};
|
||||
|
||||
virtual void relayTransaction(const cryptonote::transaction& transaction, const Callback& callback) {callback(std::error_code());};
|
||||
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) {callback(std::error_code());};
|
||||
virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector<uint64_t>& outsGlobalIndices, const Callback& callback) { callback(std::error_code()); };
|
||||
};
|
||||
|
||||
class INodeTrivialRefreshStub : public INodeDummyStub
|
||||
{
|
||||
public:
|
||||
INodeTrivialRefreshStub(TestBlockchainGenerator& generator) : m_lastHeight(1), m_blockchainGenerator(generator), m_nextTxError(false) {};
|
||||
|
||||
virtual uint64_t getLastLocalBlockHeight() const { return m_blockchainGenerator.getBlockchain().size() - 1; };
|
||||
virtual uint64_t getLastKnownBlockHeight() const { return m_blockchainGenerator.getBlockchain().size() - 1; };
|
||||
|
||||
virtual void getNewBlocks(std::list<crypto::hash>&& knownBlockIds, std::list<cryptonote::block_complete_entry>& newBlocks, uint64_t& startHeight, const Callback& callback);
|
||||
|
||||
virtual void relayTransaction(const cryptonote::transaction& transaction, const Callback& callback);
|
||||
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);
|
||||
virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector<uint64_t>& outsGlobalIndices, const Callback& callback);
|
||||
|
||||
virtual void startAlternativeChain(uint64_t height);
|
||||
virtual void setNextTransactionError();
|
||||
|
||||
private:
|
||||
void doGetNewBlocks(std::list<crypto::hash> knownBlockIds, std::list<cryptonote::block_complete_entry>& newBlocks, uint64_t& startHeight, const Callback& callback);
|
||||
void doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector<uint64_t>& outsGlobalIndices, const Callback& callback);
|
||||
void doRelayTransaction(const cryptonote::transaction& transaction, const Callback& callback);
|
||||
void doGetRandomOutsByAmounts(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);
|
||||
|
||||
uint64_t m_lastHeight;
|
||||
TestBlockchainGenerator& m_blockchainGenerator;
|
||||
bool m_nextTxError;
|
||||
};
|
113
tests/unit_tests/TestBlockchainGenerator.cpp
Normal file
113
tests/unit_tests/TestBlockchainGenerator.cpp
Normal file
|
@ -0,0 +1,113 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <time.h>
|
||||
#include "TestBlockchainGenerator.h"
|
||||
|
||||
#include "../performance_tests/multi_tx_test_base.h"
|
||||
|
||||
class TransactionForAddressCreator : public multi_tx_test_base<5>
|
||||
{
|
||||
typedef multi_tx_test_base<5> base_class;
|
||||
public:
|
||||
TransactionForAddressCreator() {}
|
||||
|
||||
bool init()
|
||||
{
|
||||
return base_class::init();
|
||||
}
|
||||
|
||||
void generate(const cryptonote::account_public_address& address, cryptonote::transaction& tx)
|
||||
{
|
||||
cryptonote::tx_destination_entry destination(this->m_source_amount, address);
|
||||
std::vector<cryptonote::tx_destination_entry> destinations;
|
||||
destinations.push_back(destination);
|
||||
|
||||
cryptonote::construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, destinations, std::vector<uint8_t>(), tx, 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
TestBlockchainGenerator::TestBlockchainGenerator()
|
||||
{
|
||||
miner_acc.generate();
|
||||
addGenesisBlock();
|
||||
}
|
||||
|
||||
std::vector<cryptonote::block>& TestBlockchainGenerator::getBlockchain()
|
||||
{
|
||||
return m_blockchain;
|
||||
}
|
||||
|
||||
bool TestBlockchainGenerator::getTransactionByHash(const crypto::hash& hash, cryptonote::transaction& tx)
|
||||
{
|
||||
auto it = m_txs.find(hash);
|
||||
if (it == m_txs.end())
|
||||
return false;
|
||||
|
||||
tx = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
void TestBlockchainGenerator::addGenesisBlock()
|
||||
{
|
||||
cryptonote::block genesis;
|
||||
uint64_t timestamp = time(NULL);
|
||||
|
||||
generator.construct_block(genesis, miner_acc, timestamp);
|
||||
m_blockchain.push_back(genesis);
|
||||
}
|
||||
|
||||
void TestBlockchainGenerator::generateEmptyBlocks(size_t count)
|
||||
{
|
||||
addGenesisBlock();
|
||||
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
cryptonote::block& prev_block = m_blockchain.back();
|
||||
cryptonote::block block;
|
||||
generator.construct_block(block, prev_block, miner_acc);
|
||||
m_blockchain.push_back(block);
|
||||
}
|
||||
}
|
||||
|
||||
void TestBlockchainGenerator::addTxToBlockchain(const cryptonote::transaction& transaction)
|
||||
{
|
||||
crypto::hash txHash = cryptonote::get_transaction_hash(transaction);
|
||||
m_txs[txHash] = transaction;
|
||||
|
||||
std::list<cryptonote::transaction> txs;
|
||||
txs.push_back(transaction);
|
||||
|
||||
cryptonote::block& prev_block = m_blockchain.back();
|
||||
cryptonote::block block;
|
||||
|
||||
generator.construct_block(block, prev_block, miner_acc, txs);
|
||||
m_blockchain.push_back(block);
|
||||
}
|
||||
|
||||
bool TestBlockchainGenerator::getBlockRewardForAddress(const cryptonote::account_public_address& address)
|
||||
{
|
||||
TransactionForAddressCreator creator;
|
||||
if (!creator.init())
|
||||
return false;
|
||||
|
||||
cryptonote::transaction tx;
|
||||
creator.generate(address, tx);
|
||||
|
||||
crypto::hash txHash = cryptonote::get_transaction_hash(tx);
|
||||
m_txs[txHash] = tx;
|
||||
|
||||
std::list<cryptonote::transaction> txs;
|
||||
txs.push_back(tx);
|
||||
|
||||
cryptonote::block& prev_block = m_blockchain.back();
|
||||
cryptonote::block block;
|
||||
|
||||
generator.construct_block(block, prev_block, miner_acc, txs);
|
||||
m_blockchain.push_back(block);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
32
tests/unit_tests/TestBlockchainGenerator.h
Normal file
32
tests/unit_tests/TestBlockchainGenerator.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../core_tests/chaingen.h"
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "cryptonote_core/account.h"
|
||||
#include "cryptonote_core/cryptonote_basic.h"
|
||||
#include "crypto/hash.h"
|
||||
|
||||
class TestBlockchainGenerator
|
||||
{
|
||||
public:
|
||||
TestBlockchainGenerator();
|
||||
|
||||
std::vector<cryptonote::block>& getBlockchain();
|
||||
void addGenesisBlock();
|
||||
void generateEmptyBlocks(size_t count);
|
||||
bool getBlockRewardForAddress(const cryptonote::account_public_address& address);
|
||||
void addTxToBlockchain(const cryptonote::transaction& transaction);
|
||||
bool getTransactionByHash(const crypto::hash& hash, cryptonote::transaction& tx);
|
||||
|
||||
private:
|
||||
test_generator generator;
|
||||
cryptonote::account_base miner_acc;
|
||||
std::vector<cryptonote::block> m_blockchain;
|
||||
std::unordered_map<crypto::hash, cryptonote::transaction> m_txs;
|
||||
};
|
|
@ -109,7 +109,7 @@ TEST(parse_and_validate_tx_extra, is_valid_tx_extra_parsed)
|
|||
cryptonote::account_base acc;
|
||||
acc.generate();
|
||||
cryptonote::blobdata b = "dsdsdfsdfsf";
|
||||
ASSERT_TRUE(cryptonote::construct_miner_tx(0, 0, 10000000000000, 1000, DEFAULT_FEE, acc.get_keys().m_account_address, tx, b, 1));
|
||||
ASSERT_TRUE(cryptonote::construct_miner_tx(0, 0, 10000000000000, 1000, MINIMUM_FEE, acc.get_keys().m_account_address, tx, b, 1));
|
||||
crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(tx);
|
||||
ASSERT_NE(tx_pub_key, cryptonote::null_pkey);
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ TEST(parse_and_validate_tx_extra, fails_on_big_extra_nonce)
|
|||
cryptonote::account_base acc;
|
||||
acc.generate();
|
||||
cryptonote::blobdata b(TX_EXTRA_NONCE_MAX_COUNT + 1, 0);
|
||||
ASSERT_FALSE(cryptonote::construct_miner_tx(0, 0, 10000000000000, 1000, DEFAULT_FEE, acc.get_keys().m_account_address, tx, b, 1));
|
||||
ASSERT_FALSE(cryptonote::construct_miner_tx(0, 0, 10000000000000, 1000, MINIMUM_FEE, acc.get_keys().m_account_address, tx, b, 1));
|
||||
}
|
||||
TEST(parse_and_validate_tx_extra, fails_on_wrong_size_in_extra_nonce)
|
||||
{
|
||||
|
|
744
tests/unit_tests/test_wallet.cpp
Normal file
744
tests/unit_tests/test_wallet.cpp
Normal file
|
@ -0,0 +1,744 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <future>
|
||||
#include <chrono>
|
||||
#include <array>
|
||||
|
||||
#include "INode.h"
|
||||
#include "wallet/Wallet.h"
|
||||
#include "cryptonote_core/account.h"
|
||||
|
||||
#include "INodeStubs.h"
|
||||
#include "TestBlockchainGenerator.h"
|
||||
|
||||
class TrivialWalletObserver : public CryptoNote::IWalletObserver
|
||||
{
|
||||
public:
|
||||
TrivialWalletObserver() {}
|
||||
|
||||
bool waitForSyncEnd() {
|
||||
auto future = syncPromise.get_future();
|
||||
return future.wait_for(std::chrono::seconds(3)) == std::future_status::ready;
|
||||
}
|
||||
|
||||
bool waitForSendEnd(std::error_code& ec) {
|
||||
auto future = sendPromise.get_future();
|
||||
|
||||
if (future.wait_for(std::chrono::seconds(5)) == std::future_status::timeout) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ec = future.get();
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool waitForSaveEnd(std::error_code& ec) {
|
||||
auto future = savePromise.get_future();
|
||||
|
||||
if (future.wait_for(std::chrono::seconds(5)) == std::future_status::timeout) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ec = future.get();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool waitForLoadEnd(std::error_code& ec) {
|
||||
auto future = loadPromise.get_future();
|
||||
|
||||
if (future.wait_for(std::chrono::seconds(5)) == std::future_status::timeout) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ec = future.get();
|
||||
return true;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
syncPromise = std::promise<void>();
|
||||
sendPromise = std::promise<std::error_code>();
|
||||
savePromise = std::promise<std::error_code>();
|
||||
loadPromise = std::promise<std::error_code>();
|
||||
}
|
||||
|
||||
virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total, std::error_code result) {
|
||||
if (result) {
|
||||
syncPromise.set_value();
|
||||
return;
|
||||
}
|
||||
|
||||
if (current == total) {
|
||||
syncPromise.set_value();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void sendTransactionCompleted(CryptoNote::TransactionId transactionId, std::error_code result) {
|
||||
sendPromise.set_value(result);
|
||||
}
|
||||
|
||||
virtual void saveCompleted(std::error_code result) {
|
||||
savePromise.set_value(result);
|
||||
}
|
||||
|
||||
virtual void initCompleted(std::error_code result) {
|
||||
loadPromise.set_value(result);
|
||||
}
|
||||
|
||||
virtual void actualBalanceUpdated(uint64_t actualBalance) {
|
||||
// std::cout << "actual balance: " << actualBalance << std::endl;
|
||||
}
|
||||
virtual void pendingBalanceUpdated(uint64_t pendingBalance) {
|
||||
// std::cout << "pending balance: " << pendingBalance << std::endl;
|
||||
}
|
||||
|
||||
std::promise<void> syncPromise;
|
||||
std::promise<std::error_code> sendPromise;
|
||||
std::promise<std::error_code> savePromise;
|
||||
std::promise<std::error_code> loadPromise;
|
||||
};
|
||||
|
||||
static const uint64_t TEST_BLOCK_REWARD = 70368744177663;
|
||||
|
||||
CryptoNote::TransactionId TransferMoney(CryptoNote::Wallet& from, CryptoNote::Wallet& to, int64_t amount, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "") {
|
||||
CryptoNote::Transfer transfer;
|
||||
transfer.amount = amount;
|
||||
transfer.address = to.getAddress();
|
||||
|
||||
return from.sendTransaction(transfer, fee, extra, mixIn);
|
||||
}
|
||||
|
||||
void WaitWalletSync(TrivialWalletObserver* observer) {
|
||||
observer->reset();
|
||||
ASSERT_TRUE(observer->waitForSyncEnd());
|
||||
}
|
||||
|
||||
void WaitWalletSend(TrivialWalletObserver* observer) {
|
||||
std::error_code ec;
|
||||
observer->reset();
|
||||
ASSERT_TRUE(observer->waitForSendEnd(ec));
|
||||
}
|
||||
|
||||
void WaitWalletSend(TrivialWalletObserver* observer, std::error_code& ec) {
|
||||
observer->reset();
|
||||
ASSERT_TRUE(observer->waitForSendEnd(ec));
|
||||
}
|
||||
|
||||
void WaitWalletSave(TrivialWalletObserver* observer) {
|
||||
observer->reset();
|
||||
std::error_code ec;
|
||||
|
||||
ASSERT_TRUE(observer->waitForSaveEnd(ec));
|
||||
EXPECT_FALSE(ec);
|
||||
}
|
||||
|
||||
class WalletApi : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
void SetUp();
|
||||
|
||||
protected:
|
||||
void prepareAliceWallet();
|
||||
void prepareBobWallet();
|
||||
void prepareCarolWallet();
|
||||
|
||||
void GetOneBlockReward(CryptoNote::Wallet& wallet);
|
||||
|
||||
void TestSendMoney(int64_t transferAmount, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "");
|
||||
void performTransferWithErrorTx(const std::array<int64_t, 5>& amounts, uint64_t fee);
|
||||
|
||||
TestBlockchainGenerator generator;
|
||||
|
||||
std::shared_ptr<TrivialWalletObserver> aliceWalletObserver;
|
||||
std::shared_ptr<INodeTrivialRefreshStub> aliceNode;
|
||||
std::shared_ptr<CryptoNote::Wallet> alice;
|
||||
|
||||
std::shared_ptr<TrivialWalletObserver> bobWalletObserver;
|
||||
std::shared_ptr<INodeTrivialRefreshStub> bobNode;
|
||||
std::shared_ptr<CryptoNote::Wallet> bob;
|
||||
|
||||
std::shared_ptr<TrivialWalletObserver> carolWalletObserver;
|
||||
std::shared_ptr<INodeTrivialRefreshStub> carolNode;
|
||||
std::shared_ptr<CryptoNote::Wallet> carol;
|
||||
};
|
||||
|
||||
void WalletApi::SetUp() {
|
||||
prepareAliceWallet();
|
||||
generator.generateEmptyBlocks(3);
|
||||
}
|
||||
|
||||
void WalletApi::prepareAliceWallet() {
|
||||
aliceNode.reset(new INodeTrivialRefreshStub(generator));
|
||||
aliceWalletObserver.reset(new TrivialWalletObserver());
|
||||
|
||||
alice.reset(new CryptoNote::Wallet(*aliceNode));
|
||||
alice->addObserver(aliceWalletObserver.get());
|
||||
}
|
||||
|
||||
void WalletApi::prepareBobWallet() {
|
||||
bobNode.reset(new INodeTrivialRefreshStub(generator));
|
||||
bobWalletObserver.reset(new TrivialWalletObserver());
|
||||
|
||||
bob.reset(new CryptoNote::Wallet(*bobNode));
|
||||
bob->addObserver(bobWalletObserver.get());
|
||||
}
|
||||
|
||||
void WalletApi::prepareCarolWallet() {
|
||||
carolNode.reset(new INodeTrivialRefreshStub(generator));
|
||||
carolWalletObserver.reset(new TrivialWalletObserver());
|
||||
|
||||
carol.reset(new CryptoNote::Wallet(*carolNode));
|
||||
carol->addObserver(carolWalletObserver.get());
|
||||
}
|
||||
|
||||
void WalletApi::GetOneBlockReward(CryptoNote::Wallet& wallet) {
|
||||
cryptonote::account_public_address address;
|
||||
ASSERT_TRUE(cryptonote::get_account_address_from_str(address, wallet.getAddress()));
|
||||
generator.getBlockRewardForAddress(address);
|
||||
}
|
||||
|
||||
void WalletApi::performTransferWithErrorTx(const std::array<int64_t, 5>& amounts, uint64_t fee) {
|
||||
std::vector<CryptoNote::Transfer> trs;
|
||||
CryptoNote::Transfer tr;
|
||||
tr.address = bob->getAddress();
|
||||
tr.amount = amounts[0];
|
||||
trs.push_back(tr);
|
||||
|
||||
tr.address = bob->getAddress();
|
||||
tr.amount = amounts[1];
|
||||
trs.push_back(tr);
|
||||
|
||||
tr.address = carol->getAddress();
|
||||
tr.amount = amounts[2];
|
||||
trs.push_back(tr);
|
||||
|
||||
aliceNode->setNextTransactionError();
|
||||
alice->sendTransaction(trs, fee);
|
||||
|
||||
std::error_code result;
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get(), result));
|
||||
ASSERT_NE(result.value(), 0);
|
||||
|
||||
trs.clear();
|
||||
|
||||
tr.address = bob->getAddress();
|
||||
tr.amount = amounts[3];
|
||||
trs.push_back(tr);
|
||||
|
||||
tr.address = carol->getAddress();
|
||||
tr.amount = amounts[4];
|
||||
trs.push_back(tr);
|
||||
|
||||
alice->sendTransaction(trs, fee);
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get(), result));
|
||||
ASSERT_EQ(result.value(), 0);
|
||||
}
|
||||
|
||||
void WalletApi::TestSendMoney(int64_t transferAmount, uint64_t fee, uint64_t mixIn, const std::string& extra) {
|
||||
prepareBobWallet();
|
||||
prepareCarolWallet();
|
||||
|
||||
alice->initAndGenerate("pass");
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
ASSERT_NO_FATAL_FAILURE(GetOneBlockReward(*alice));
|
||||
|
||||
//unblock Alice's money
|
||||
generator.generateEmptyBlocks(10);
|
||||
uint64_t expectedBalance = TEST_BLOCK_REWARD;
|
||||
|
||||
alice->startRefresh();
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
|
||||
EXPECT_EQ(alice->pendingBalance(), expectedBalance);
|
||||
EXPECT_EQ(alice->actualBalance(), expectedBalance);
|
||||
|
||||
bob->initAndGenerate("pass2");
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get()));
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(TransferMoney(*alice, *bob, transferAmount, fee, 0, ""));
|
||||
|
||||
generator.generateEmptyBlocks(10);
|
||||
|
||||
alice->startRefresh();
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
|
||||
bob->startRefresh();
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get()));
|
||||
|
||||
EXPECT_EQ(bob->pendingBalance(), transferAmount);
|
||||
EXPECT_EQ(bob->actualBalance(), transferAmount);
|
||||
|
||||
EXPECT_EQ(alice->pendingBalance(), expectedBalance - transferAmount - fee);
|
||||
EXPECT_EQ(alice->actualBalance(), expectedBalance - transferAmount - fee);
|
||||
|
||||
alice->shutdown();
|
||||
bob->shutdown();
|
||||
}
|
||||
|
||||
void WaitWalletLoad(TrivialWalletObserver* observer, std::error_code& ec) {
|
||||
observer->reset();
|
||||
|
||||
ASSERT_TRUE(observer->waitForLoadEnd(ec));
|
||||
}
|
||||
|
||||
TEST_F(WalletApi, refreshWithMoney) {
|
||||
alice->initAndGenerate("pass");
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
|
||||
ASSERT_EQ(alice->actualBalance(), 0);
|
||||
ASSERT_EQ(alice->pendingBalance(), 0);
|
||||
|
||||
cryptonote::account_public_address address;
|
||||
ASSERT_TRUE(cryptonote::get_account_address_from_str(address, alice->getAddress()));
|
||||
generator.getBlockRewardForAddress(address);
|
||||
|
||||
alice->startRefresh();
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
|
||||
EXPECT_EQ(alice->actualBalance(), 0);
|
||||
EXPECT_EQ(alice->pendingBalance(), TEST_BLOCK_REWARD);
|
||||
|
||||
alice->shutdown();
|
||||
}
|
||||
|
||||
TEST_F(WalletApi, TransactionsAndTransfersAfterSend) {
|
||||
prepareBobWallet();
|
||||
prepareCarolWallet();
|
||||
|
||||
alice->initAndGenerate("pass");
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
|
||||
EXPECT_EQ(alice->getTransactionCount(), 0);
|
||||
EXPECT_EQ(alice->getTransferCount(), 0);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(GetOneBlockReward(*alice));
|
||||
|
||||
//unblock Alice's money
|
||||
generator.generateEmptyBlocks(10);
|
||||
alice->startRefresh();
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
|
||||
bob->initAndGenerate("pass2");
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get()));
|
||||
|
||||
uint64_t fee = 100000;
|
||||
int64_t amount1 = 1230000;
|
||||
ASSERT_NO_FATAL_FAILURE(TransferMoney(*alice, *bob, amount1, fee, 0));
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get()));
|
||||
|
||||
int64_t amount2 = 1234500;
|
||||
ASSERT_NO_FATAL_FAILURE(TransferMoney(*alice, *bob, amount2, fee, 0));
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get()));
|
||||
|
||||
int64_t amount3 = 1234567;
|
||||
ASSERT_NO_FATAL_FAILURE(TransferMoney(*alice, *bob, amount3, fee, 0));
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get()));
|
||||
|
||||
carol->initAndGenerate("pass3");
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(carolWalletObserver.get()));
|
||||
|
||||
int64_t amount4 = 1020304;
|
||||
ASSERT_NO_FATAL_FAILURE(TransferMoney(*alice, *carol, amount4, fee, 0));
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get()));
|
||||
|
||||
EXPECT_EQ(alice->getTransactionCount(), 5);
|
||||
|
||||
CryptoNote::Transaction tx;
|
||||
|
||||
//Transaction with id = 0 is tested in getTransactionSuccess
|
||||
ASSERT_TRUE(alice->getTransaction(1, tx));
|
||||
EXPECT_EQ(tx.totalAmount, amount1 + fee);
|
||||
EXPECT_EQ(tx.fee, fee);
|
||||
EXPECT_EQ(tx.isCoinbase, false);
|
||||
EXPECT_EQ(tx.firstTransferId, 0);
|
||||
EXPECT_EQ(tx.transferCount, 1);
|
||||
|
||||
ASSERT_TRUE(alice->getTransaction(2, tx));
|
||||
EXPECT_EQ(tx.totalAmount, amount2 + fee);
|
||||
EXPECT_EQ(tx.fee, fee);
|
||||
EXPECT_EQ(tx.isCoinbase, false);
|
||||
EXPECT_EQ(tx.firstTransferId, 1);
|
||||
EXPECT_EQ(tx.transferCount, 1);
|
||||
|
||||
ASSERT_TRUE(alice->getTransaction(3, tx));
|
||||
EXPECT_EQ(tx.totalAmount, amount3 + fee);
|
||||
EXPECT_EQ(tx.fee, fee);
|
||||
EXPECT_EQ(tx.isCoinbase, false);
|
||||
EXPECT_EQ(tx.firstTransferId, 2);
|
||||
EXPECT_EQ(tx.transferCount, 1);
|
||||
|
||||
ASSERT_TRUE(alice->getTransaction(4, tx));
|
||||
EXPECT_EQ(tx.totalAmount, amount4 + fee);
|
||||
EXPECT_EQ(tx.fee, fee);
|
||||
EXPECT_EQ(tx.isCoinbase, false);
|
||||
EXPECT_EQ(tx.firstTransferId, 3);
|
||||
EXPECT_EQ(tx.transferCount, 1);
|
||||
|
||||
//Now checking transfers
|
||||
CryptoNote::Transfer tr;
|
||||
ASSERT_TRUE(alice->getTransfer(0, tr));
|
||||
EXPECT_EQ(tr.amount, amount1);
|
||||
EXPECT_EQ(tr.address, bob->getAddress());
|
||||
|
||||
ASSERT_TRUE(alice->getTransfer(1, tr));
|
||||
EXPECT_EQ(tr.amount, amount2);
|
||||
EXPECT_EQ(tr.address, bob->getAddress());
|
||||
|
||||
ASSERT_TRUE(alice->getTransfer(2, tr));
|
||||
EXPECT_EQ(tr.amount, amount3);
|
||||
EXPECT_EQ(tr.address, bob->getAddress());
|
||||
|
||||
ASSERT_TRUE(alice->getTransfer(3, tr));
|
||||
EXPECT_EQ(tr.amount, amount4);
|
||||
EXPECT_EQ(tr.address, carol->getAddress());
|
||||
|
||||
EXPECT_EQ(alice->findTransactionByTransferId(0), 1);
|
||||
EXPECT_EQ(alice->findTransactionByTransferId(1), 2);
|
||||
EXPECT_EQ(alice->findTransactionByTransferId(2), 3);
|
||||
EXPECT_EQ(alice->findTransactionByTransferId(3), 4);
|
||||
|
||||
alice->shutdown();
|
||||
}
|
||||
|
||||
TEST_F(WalletApi, saveAndLoadCacheDetails) {
|
||||
prepareBobWallet();
|
||||
prepareCarolWallet();
|
||||
|
||||
alice->initAndGenerate("pass");
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(GetOneBlockReward(*alice));
|
||||
|
||||
//unblock Alice's money
|
||||
generator.generateEmptyBlocks(10);
|
||||
alice->startRefresh();
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
|
||||
bob->initAndGenerate("pass2");
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get()));
|
||||
|
||||
carol->initAndGenerate("pass3");
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(carolWalletObserver.get()));
|
||||
|
||||
uint64_t fee = 1000000;
|
||||
int64_t amount1 = 1234567;
|
||||
int64_t amount2 = 1020304;
|
||||
int64_t amount3 = 2030405;
|
||||
|
||||
std::vector<CryptoNote::Transfer> trs;
|
||||
CryptoNote::Transfer tr;
|
||||
tr.address = bob->getAddress();
|
||||
tr.amount = amount1;
|
||||
trs.push_back(tr);
|
||||
|
||||
tr.address = bob->getAddress();
|
||||
tr.amount = amount2;
|
||||
trs.push_back(tr);
|
||||
|
||||
alice->sendTransaction(trs, fee, "", 0, 0);
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get()));
|
||||
|
||||
trs.clear();
|
||||
tr.address = carol->getAddress();
|
||||
tr.amount = amount3;
|
||||
trs.push_back(tr);
|
||||
|
||||
alice->sendTransaction(trs, fee, "", 0, 0);
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get()));
|
||||
|
||||
std::stringstream archive;
|
||||
alice->save(archive, true, true);
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSave(aliceWalletObserver.get()));
|
||||
|
||||
alice->shutdown();
|
||||
|
||||
prepareAliceWallet();
|
||||
|
||||
alice->initAndLoad(archive, "pass");
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
|
||||
ASSERT_EQ(alice->getTransactionCount(), 3);
|
||||
ASSERT_EQ(alice->getTransferCount(), 3);
|
||||
|
||||
CryptoNote::Transaction tx;
|
||||
ASSERT_TRUE(alice->getTransaction(1, tx));
|
||||
EXPECT_EQ(tx.totalAmount, amount1 + amount2 + fee);
|
||||
EXPECT_EQ(tx.fee, fee);
|
||||
EXPECT_EQ(tx.firstTransferId, 0);
|
||||
EXPECT_EQ(tx.transferCount, 2);
|
||||
|
||||
ASSERT_TRUE(alice->getTransaction(2, tx));
|
||||
EXPECT_EQ(tx.totalAmount, amount3 + fee);
|
||||
EXPECT_EQ(tx.fee, fee);
|
||||
EXPECT_EQ(tx.firstTransferId, 2);
|
||||
EXPECT_EQ(tx.transferCount, 1);
|
||||
|
||||
ASSERT_TRUE(alice->getTransfer(0, tr));
|
||||
EXPECT_EQ(tr.address, bob->getAddress());
|
||||
EXPECT_EQ(tr.amount, amount1);
|
||||
|
||||
ASSERT_TRUE(alice->getTransfer(1, tr));
|
||||
EXPECT_EQ(tr.address, bob->getAddress());
|
||||
EXPECT_EQ(tr.amount, amount2);
|
||||
|
||||
ASSERT_TRUE(alice->getTransfer(2, tr));
|
||||
EXPECT_EQ(tr.address, carol->getAddress());
|
||||
EXPECT_EQ(tr.amount, amount3);
|
||||
|
||||
EXPECT_EQ(alice->findTransactionByTransferId(0), 1);
|
||||
EXPECT_EQ(alice->findTransactionByTransferId(1), 1);
|
||||
EXPECT_EQ(alice->findTransactionByTransferId(2), 2);
|
||||
|
||||
alice->shutdown();
|
||||
carol->shutdown();
|
||||
bob->shutdown();
|
||||
}
|
||||
|
||||
TEST_F(WalletApi, sendMoneySuccessNoMixin) {
|
||||
ASSERT_NO_FATAL_FAILURE(TestSendMoney(10000000, 1000000, 0));
|
||||
}
|
||||
|
||||
TEST_F(WalletApi, sendMoneySuccessWithMixin) {
|
||||
ASSERT_NO_FATAL_FAILURE(TestSendMoney(10000000, 1000000, 3));
|
||||
}
|
||||
|
||||
TEST_F(WalletApi, getTransactionSuccess) {
|
||||
alice->initAndGenerate("pass");
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
ASSERT_NO_FATAL_FAILURE(GetOneBlockReward(*alice));
|
||||
|
||||
alice->startRefresh();
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
|
||||
CryptoNote::Transaction tx;
|
||||
|
||||
ASSERT_EQ(alice->getTransactionCount(), 1);
|
||||
ASSERT_TRUE(alice->getTransaction(0, tx));
|
||||
|
||||
EXPECT_EQ(tx.firstTransferId, CryptoNote::INVALID_TRANSFER_ID);
|
||||
EXPECT_EQ(tx.transferCount, 0);
|
||||
EXPECT_EQ(tx.totalAmount, TEST_BLOCK_REWARD);
|
||||
EXPECT_EQ(tx.fee, 0);
|
||||
EXPECT_EQ(tx.isCoinbase, false);
|
||||
|
||||
alice->shutdown();
|
||||
}
|
||||
|
||||
TEST_F(WalletApi, getTransactionFailure) {
|
||||
alice->initAndGenerate("pass");
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
|
||||
CryptoNote::Transaction tx;
|
||||
|
||||
ASSERT_EQ(alice->getTransactionCount(), 0);
|
||||
ASSERT_FALSE(alice->getTransaction(0, tx));
|
||||
|
||||
alice->shutdown();
|
||||
}
|
||||
|
||||
TEST_F(WalletApi, useNotInitializedObject) {
|
||||
EXPECT_THROW(alice->pendingBalance(), std::system_error);
|
||||
EXPECT_THROW(alice->actualBalance(), std::system_error);
|
||||
EXPECT_THROW(alice->getTransactionCount(), std::system_error);
|
||||
EXPECT_THROW(alice->getTransferCount(), std::system_error);
|
||||
EXPECT_THROW(alice->getAddress(), std::system_error);
|
||||
|
||||
std::stringstream archive;
|
||||
EXPECT_THROW(alice->save(archive, true, true), std::system_error);
|
||||
|
||||
EXPECT_THROW(alice->findTransactionByTransferId(1), std::system_error);
|
||||
|
||||
CryptoNote::Transaction tx;
|
||||
CryptoNote::Transfer tr;
|
||||
EXPECT_THROW(alice->getTransaction(1, tx), std::system_error);
|
||||
EXPECT_THROW(alice->getTransfer(2, tr), std::system_error);
|
||||
|
||||
tr.address = "lslslslslslsls";
|
||||
tr.amount = 1000000;
|
||||
EXPECT_THROW(alice->sendTransaction(tr, 300201), std::system_error);
|
||||
|
||||
std::vector<CryptoNote::Transfer> trs;
|
||||
trs.push_back(tr);
|
||||
EXPECT_THROW(alice->sendTransaction(trs, 329293), std::system_error);
|
||||
}
|
||||
|
||||
TEST_F(WalletApi, sendWrongAmount) {
|
||||
alice->initAndGenerate("pass");
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
|
||||
CryptoNote::Transfer tr;
|
||||
tr.address = "1234567890qwertasdfgzxcvbyuiophjklnm";
|
||||
tr.amount = 1;
|
||||
|
||||
EXPECT_THROW(alice->sendTransaction(tr, 1), std::system_error);
|
||||
|
||||
alice->shutdown();
|
||||
}
|
||||
|
||||
TEST_F(WalletApi, wrongPassword) {
|
||||
alice->initAndGenerate("pass");
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
|
||||
std::stringstream archive;
|
||||
alice->save(archive, true, false);
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSave(aliceWalletObserver.get()));
|
||||
|
||||
alice->shutdown();
|
||||
|
||||
prepareAliceWallet();
|
||||
alice->initAndLoad(archive, "wrongpass");
|
||||
|
||||
std::error_code result;
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletLoad(aliceWalletObserver.get(), result));
|
||||
EXPECT_EQ(result.value(), cryptonote::error::WRONG_PASSWORD);
|
||||
}
|
||||
|
||||
TEST_F(WalletApi, detachBlockchain) {
|
||||
alice->initAndGenerate("pass");
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(GetOneBlockReward(*alice));
|
||||
|
||||
generator.generateEmptyBlocks(10);
|
||||
alice->startRefresh();
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
|
||||
aliceNode->startAlternativeChain(3);
|
||||
generator.generateEmptyBlocks(10);
|
||||
alice->startRefresh();
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
|
||||
EXPECT_EQ(alice->actualBalance(), 0);
|
||||
EXPECT_EQ(alice->pendingBalance(), 0);
|
||||
|
||||
alice->shutdown();
|
||||
}
|
||||
|
||||
TEST_F(WalletApi, saveAndLoadErroneousTxsCacheDetails) {
|
||||
prepareBobWallet();
|
||||
prepareCarolWallet();
|
||||
|
||||
alice->initAndGenerate("pass");
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(GetOneBlockReward(*alice));
|
||||
generator.generateEmptyBlocks(10);
|
||||
alice->startRefresh();
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
|
||||
bob->initAndGenerate("pass");
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get()));
|
||||
|
||||
carol->initAndGenerate("pass");
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(carolWalletObserver.get()));
|
||||
|
||||
std::array<int64_t, 5> amounts;
|
||||
amounts[0] = 1234567;
|
||||
amounts[1] = 1345678;
|
||||
amounts[2] = 1456789;
|
||||
amounts[3] = 1567890;
|
||||
amounts[4] = 1678901;
|
||||
uint64_t fee = 10000;
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(performTransferWithErrorTx(amounts, fee));
|
||||
|
||||
std::stringstream archive;
|
||||
alice->save(archive);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSave(aliceWalletObserver.get()));
|
||||
|
||||
prepareAliceWallet();
|
||||
alice->initAndLoad(archive, "pass");
|
||||
|
||||
std::error_code result;
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletLoad(aliceWalletObserver.get(), result));
|
||||
ASSERT_EQ(result.value(), 0);
|
||||
|
||||
EXPECT_EQ(alice->getTransactionCount(), 2);
|
||||
EXPECT_EQ(alice->getTransferCount(), 2);
|
||||
|
||||
CryptoNote::Transaction tx;
|
||||
ASSERT_TRUE(alice->getTransaction(1, tx));
|
||||
EXPECT_EQ(tx.totalAmount, amounts[3] + amounts[4] + fee);
|
||||
EXPECT_EQ(tx.firstTransferId, 0);
|
||||
EXPECT_EQ(tx.transferCount, 2);
|
||||
|
||||
CryptoNote::Transfer tr;
|
||||
ASSERT_TRUE(alice->getTransfer(0, tr));
|
||||
EXPECT_EQ(tr.amount, amounts[3]);
|
||||
EXPECT_EQ(tr.address, bob->getAddress());
|
||||
|
||||
ASSERT_TRUE(alice->getTransfer(1, tr));
|
||||
EXPECT_EQ(tr.amount, amounts[4]);
|
||||
EXPECT_EQ(tr.address, carol->getAddress());
|
||||
|
||||
alice->shutdown();
|
||||
}
|
||||
|
||||
TEST_F(WalletApi, saveAndLoadErroneousTxsCacheNoDetails) {
|
||||
prepareBobWallet();
|
||||
prepareCarolWallet();
|
||||
|
||||
alice->initAndGenerate("pass");
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(GetOneBlockReward(*alice));
|
||||
generator.generateEmptyBlocks(10);
|
||||
alice->startRefresh();
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get()));
|
||||
|
||||
bob->initAndGenerate("pass");
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get()));
|
||||
|
||||
carol->initAndGenerate("pass");
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSync(carolWalletObserver.get()));
|
||||
|
||||
std::array<int64_t, 5> amounts;
|
||||
amounts[0] = 1234567;
|
||||
amounts[1] = 1345678;
|
||||
amounts[2] = 1456789;
|
||||
amounts[3] = 1567890;
|
||||
amounts[4] = 1678901;
|
||||
uint64_t fee = 10000;
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(performTransferWithErrorTx(amounts, fee));
|
||||
|
||||
std::stringstream archive;
|
||||
alice->save(archive, false, true);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletSave(aliceWalletObserver.get()));
|
||||
|
||||
prepareAliceWallet();
|
||||
alice->initAndLoad(archive, "pass");
|
||||
|
||||
std::error_code result;
|
||||
ASSERT_NO_FATAL_FAILURE(WaitWalletLoad(aliceWalletObserver.get(), result));
|
||||
ASSERT_EQ(result.value(), 0);
|
||||
|
||||
EXPECT_EQ(alice->getTransactionCount(), 2);
|
||||
EXPECT_EQ(alice->getTransferCount(), 0);
|
||||
|
||||
CryptoNote::Transaction tx;
|
||||
ASSERT_TRUE(alice->getTransaction(1, tx));
|
||||
EXPECT_EQ(tx.totalAmount, amounts[3] + amounts[4] + fee);
|
||||
EXPECT_EQ(tx.firstTransferId, CryptoNote::INVALID_TRANSFER_ID);
|
||||
EXPECT_EQ(tx.transferCount, 0);
|
||||
|
||||
alice->shutdown();
|
||||
}
|
Loading…
Reference in a new issue