Merge remote-tracking branch 'bytecoin/master'

This commit is contained in:
Albert Werner 2014-06-28 18:40:09 +04:00
commit 7853c212f4
77 changed files with 5240 additions and 464 deletions

View file

@ -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)

View file

@ -1,3 +1,9 @@
Release notes 0.8.11
- High level API implementation
- CryptoNight hash function optimization
- Improvements for wallet JSON RPC API
Release notes 0.8.10
- Optimized blockchain storage memory usage

View file

@ -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;
};
}

View file

@ -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;

View file

@ -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,11 +39,13 @@ 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")
#TODO Specify the name of daemon for your currency
#set_property(TARGET daemon PROPERTY OUTPUT_NAME "cryptonoted")

View 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;
};
}

View file

@ -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;

View file

@ -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));
}
}

View file

@ -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);

View file

@ -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) {

View file

@ -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 hash_state hs;
struct
{
uint8_t k[64];
uint8_t init[INIT_SIZE_BYTE];
};
union cn_slow_hash_state {
union hash_state hs;
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;
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;
};
a0 = U64(a)[0];
b0 = U64(b)[0];
lo = mul128(a0, b0, &hi);
U64(res)[0] = hi;
U64(res)[1] = lo;
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 sum_half_blocks(uint8_t *a, const uint8_t *b)
static inline void ExpandAESKey256_sub2(__m128i *tmp1, __m128i *tmp3)
{
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;
__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);
}
STATIC INLINE void swap_blocks(uint8_t *a, uint8_t *b)
// Special thanks to Intel for helping me
// with ExpandAESKey256() and its subroutines
static inline void ExpandAESKey256(uint8_t *keybuf)
{
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];
__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;
}
STATIC INLINE void xor_blocks(uint8_t *a, const uint8_t *b)
static void (*const extra_hashes[4])(const void *, size_t, char *) =
{
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;
oaes_ctx *aes_ctx;
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);
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);
hash_extra_blake, hash_extra_groestl, hash_extra_jh, hash_extra_skein
};
#include "slow-hash.inl"
#define AESNI
#include "slow-hash.inl"
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
View 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
View 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);
}

View file

@ -80,70 +80,76 @@ 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 (!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, CRYPTONOTE_BLOCKS_FILENAME), appendPath(config_folder, CRYPTONOTE_BLOCKINDEXES_FILENAME), 1024)) {
return false;
}
if (m_blocks.empty()) {
LOG_PRINT_L0("Can't load blockchain storage from file.");
} else {
bool rebuild = true;
try {
std::ifstream file(appendPath(config_folder, CRYPTONOTE_BLOCKSCACHE_FILENAME), std::ios::binary);
boost::archive::binary_iarchive archive(file);
crypto::hash lastBlockHash;
archive & lastBlockHash;
if (lastBlockHash == get_block_hash(m_blocks.back().bl)) {
archive & m_blockMap;
archive & m_transactionMap;
archive & m_spent_keys;
archive & m_outputs;
rebuild = false;
}
} catch (std::exception&) {
}
if (load_existing) {
LOG_PRINT_L0("Loading blockchain...");
if (rebuild) {
LOG_PRINT_L0("No actual blockchain cache found, rebuilding internal structures...");
std::chrono::steady_clock::time_point timePoint = std::chrono::steady_clock::now();
for (uint32_t b = 0; b < m_blocks.size(); ++b) {
const Block& block = m_blocks[b];
crypto::hash blockHash = get_block_hash(block.bl);
m_blockMap.insert(std::make_pair(blockHash, b));
for (uint16_t t = 0; t < block.transactions.size(); ++t) {
const Transaction& transaction = block.transactions[t];
crypto::hash transactionHash = get_transaction_hash(transaction.tx);
TransactionIndex transactionIndex = { b, t };
m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex));
for (auto& i : transaction.tx.vin) {
if (i.type() == typeid(txin_to_key)) {
m_spent_keys.insert(::boost::get<txin_to_key>(i).k_image);
if (m_blocks.empty()) {
LOG_PRINT_L0("Can't load blockchain storage from file.");
} else {
bool rebuild = true;
try {
std::ifstream file(appendPath(config_folder, CRYPTONOTE_BLOCKSCACHE_FILENAME), std::ios::binary);
boost::archive::binary_iarchive archive(file);
crypto::hash lastBlockHash;
archive & lastBlockHash;
if (lastBlockHash == get_block_hash(m_blocks.back().bl)) {
archive & m_blockMap;
archive & m_transactionMap;
archive & m_spent_keys;
archive & m_outputs;
rebuild = false;
}
} catch (std::exception&) {
}
if (rebuild) {
LOG_PRINT_L0("No actual blockchain cache found, rebuilding internal structures...");
std::chrono::steady_clock::time_point timePoint = std::chrono::steady_clock::now();
for (uint32_t b = 0; b < m_blocks.size(); ++b) {
const Block& block = m_blocks[b];
crypto::hash blockHash = get_block_hash(block.bl);
m_blockMap.insert(std::make_pair(blockHash, b));
for (uint16_t t = 0; t < block.transactions.size(); ++t) {
const Transaction& transaction = block.transactions[t];
crypto::hash transactionHash = get_transaction_hash(transaction.tx);
TransactionIndex transactionIndex = { b, t };
m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex));
for (auto& i : transaction.tx.vin) {
if (i.type() == typeid(txin_to_key)) {
m_spent_keys.insert(::boost::get<txin_to_key>(i).k_image);
}
}
for (uint16_t o = 0; o < transaction.tx.vout.size(); ++o) {
m_outputs[transaction.tx.vout[o].amount].push_back(std::make_pair<>(transactionIndex, o));
}
}
for (uint16_t o = 0; o < transaction.tx.vout.size(); ++o) {
m_outputs[transaction.tx.vout[o].amount].push_back(std::make_pair<>(transactionIndex, o));
}
}
}
std::chrono::duration<double> duration = std::chrono::steady_clock::now() - timePoint;
LOG_PRINT_L0("Rebuilding internal structures took: " << duration.count());
std::chrono::duration<double> duration = std::chrono::steady_clock::now() - timePoint;
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);
@ -684,7 +690,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
@ -1305,7 +1311,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;

View file

@ -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; }
@ -29,8 +29,6 @@ namespace cryptonote {
crypto::hash get_block_id_by_height(uint64_t height);
bool get_block_by_hash(const crypto::hash &h, block &blk);
template<class archive_t> void serialize(archive_t & ar, const unsigned int version);
bool have_tx(const crypto::hash &id);
bool have_tx_keyimges_as_spent(const transaction &tx);
@ -75,24 +73,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);
}
missed_txs.push_back(tx_id);
} else {
txs.push_back(transactionByIndex(it->second).tx);
}
}
return true;
}
//debug functions
@ -142,6 +133,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;

View file

@ -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,30 +403,33 @@ namespace cryptonote
}
m_blockchain_storage.add_new_block(b, bvc);
if (bvc.m_added_to_main_chain) {
cryptonote_connection_context exclude_context = boost::value_initialized<cryptonote_connection_context>();
NOTIFY_NEW_BLOCK::request arg = AUTO_VAL_INIT(arg);
arg.hop = 0;
arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height();
std::list<crypto::hash> missed_txs;
std::list<transaction> txs;
m_blockchain_storage.get_transactions(b.tx_hashes, txs, missed_txs);
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());
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();
}
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;
arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height();
std::list<crypto::hash> missed_txs;
std::list<transaction> txs;
m_blockchain_storage.get_transactions(b.tx_hashes, txs, missed_txs);
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 {
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);
}
}
//-----------------------------------------------------------------------------------------------
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)

View file

@ -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);

View file

@ -645,16 +645,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;
}
//---------------------------------------------------------------
@ -678,10 +678,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;
}
//---------------------------------------------------------------

View file

@ -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);

View file

@ -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"
@ -42,9 +44,9 @@ namespace cryptonote
m_thread_index(0),
m_phandler(phandler),
m_height(0),
m_pausers_count(0),
m_pausers_count(0),
m_threads_total(0),
m_starter_nonce(0),
m_starter_nonce(0),
m_last_hr_merge_time(0),
m_hashes(0),
m_do_print_hashrate(false),
@ -83,7 +85,7 @@ namespace cryptonote
block bl = AUTO_VAL_INIT(bl);
difficulty_type di = AUTO_VAL_INIT(di);
uint64_t height = AUTO_VAL_INIT(height);
cryptonote::blobdata extra_nonce;
cryptonote::blobdata extra_nonce;
if(m_extra_messages.size() && m_config.current_extra_message_index < m_extra_messages.size())
{
extra_nonce = m_extra_messages[m_config.current_extra_message_index];
@ -109,7 +111,7 @@ namespace cryptonote
merge_hr();
return true;
});
return true;
}
//-----------------------------------------------------------------------------------------------------
@ -192,7 +194,7 @@ namespace cryptonote
{
return !m_stop;
}
//-----------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------
bool miner::start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs)
{
m_mine_address = adr;
@ -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)
{
@ -319,7 +322,7 @@ namespace cryptonote
if(local_template_ver != m_template_no)
{
CRITICAL_REGION_BEGIN(m_template_lock);
b = m_template;
local_diff = m_diffic;
@ -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))
{

View file

@ -2,7 +2,7 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#pragma once
#include <boost/program_options.hpp>
#include <atomic>
@ -27,7 +27,7 @@ namespace cryptonote
/************************************************************************/
class miner
{
public:
public:
miner(i_miner_handler* phandler);
~miner();
bool init(const boost::program_options::variables_map& vm);
@ -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);
@ -51,7 +51,7 @@ namespace cryptonote
bool worker_thread();
bool request_block_template();
void merge_hr();
struct miner_config
{
uint64_t current_extra_message_index;
@ -69,7 +69,7 @@ namespace cryptonote
std::atomic<uint32_t> m_starter_nonce;
difficulty_type m_diffic;
uint64_t m_height;
volatile uint32_t m_thread_index;
volatile uint32_t m_thread_index;
volatile uint32_t m_threads_total;
std::atomic<int32_t> m_pausers_count;
epee::critical_section m_miners_count_lock;
@ -82,7 +82,7 @@ namespace cryptonote
epee::math_helper::once_a_time_seconds<2> m_update_merge_hr_interval;
std::vector<blobdata> m_extra_messages;
miner_config m_config;
std::string m_config_folder_path;
std::string m_config_folder_path;
std::atomic<uint64_t> m_last_hr_merge_time;
std::atomic<uint64_t> m_hashes;
std::atomic<uint64_t> m_current_hash_rate;

View file

@ -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");

View file

@ -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,11 +159,11 @@ 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!
COMMAND_RPC_SUBMITSHARE::request submit_request = AUTO_VAL_INIT(submit_request);
COMMAND_RPC_SUBMITSHARE::response submit_response = AUTO_VAL_INIT(submit_response);
submit_request.id = pool_session_id;

View 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;
};
}

View 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;
}
}

View 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);
}

View 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);
}
}

View 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;
};
}

View file

@ -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;
}

View file

@ -115,8 +115,8 @@ namespace cryptonote
{
struct request
{
std::list<uint64_t> amounts;
uint64_t outs_count;
std::vector<uint64_t> amounts;
uint64_t outs_count;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amounts)
KV_SERIALIZE(outs_count)
@ -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
{

View file

@ -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;

View file

@ -1,4 +1,4 @@
#define BUILD_COMMIT_ID "@VERSION@"
#define PROJECT_VERSION "0.8.10"
#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
View 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
View 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

View 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

View 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

View 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
View 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
View 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 */

View 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

View 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

View 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

View 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

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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

View 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

View 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

View 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 */

View 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

View 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

View 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
View 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

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -24,8 +24,8 @@ namespace wallet_rpc
struct response
{
uint64_t balance;
uint64_t unlocked_balance;
uint64_t balance;
uint64_t unlocked_balance;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(balance)
@ -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()
};

View file

@ -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)

View file

@ -41,7 +41,7 @@ int main(int argc, char* argv[])
#ifdef WIN32
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif
#endif
TRY_ENTRY();
@ -51,8 +51,8 @@ int main(int argc, char* argv[])
//set up logging options
log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2);
//log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL);
log_space::log_singletone::add_logger(LOGGER_FILE,
log_space::log_singletone::get_default_log_file().c_str(),
log_space::log_singletone::add_logger(LOGGER_FILE,
log_space::log_singletone::get_default_log_file().c_str(),
log_space::log_singletone::get_default_log_folder().c_str());
@ -98,14 +98,14 @@ int main(int argc, char* argv[])
//initialize core here
LOG_PRINT_L0("Initializing proxy core...");
res = pr_core.init(vm);
CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core");
CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core");
LOG_PRINT_L0("Core initialized OK");
LOG_PRINT_L0("Starting p2p net loop...");
p2psrv.run();
LOG_PRINT_L0("p2p net loop stopped");
//deinitialize components
//deinitialize components
LOG_PRINT_L0("Deinitializing core...");
pr_core.deinit();
LOG_PRINT_L0("Deinitializing cryptonote_protocol...");
@ -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;
}

View file

@ -34,9 +34,11 @@ 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);
public:
void on_synchronized(){}

View file

@ -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()));

View file

@ -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++;
}

View file

@ -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;

View file

@ -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();

View file

@ -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;

View file

@ -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

View 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));
}

View file

@ -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;
};

View file

@ -4,6 +4,7 @@
#include "performance_tests.h"
#include "performance_utils.h"
#include "crypto/hash.h"
// tests
#include "construct_tx.h"

View 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;
}

View 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;
};

View 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;
}

View 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;
};

View 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();
}