diff options
Diffstat (limited to 'src')
51 files changed, 4931 insertions, 436 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 61c04711b..e4e682b99 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -30,13 +30,13 @@ add_library(cryptonote_core ${CRYPTONOTE_CORE}) add_executable(daemon ${DAEMON} ${P2P} ${CRYPTONOTE_PROTOCOL}) add_executable(connectivity_tool ${CONN_TOOL}) add_executable(simpleminer ${MINER}) -target_link_libraries(daemon rpc cryptonote_core crypto common upnpc-static ${Boost_LIBRARIES}) -target_link_libraries(connectivity_tool cryptonote_core crypto common ${Boost_LIBRARIES}) -target_link_libraries(simpleminer cryptonote_core crypto common ${Boost_LIBRARIES}) +target_link_libraries(daemon rpc cryptonote_core crypto common upnpc-static ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) +target_link_libraries(connectivity_tool cryptonote_core crypto common ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) +target_link_libraries(simpleminer cryptonote_core crypto common ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) 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}) +target_link_libraries(simplewallet wallet rpc cryptonote_core crypto common upnpc-static ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) add_dependencies(daemon version) add_dependencies(rpc version) add_dependencies(simplewallet version) diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp index 0b90345d9..d507f36a7 100644 --- a/src/common/command_line.cpp +++ b/src/common/command_line.cpp @@ -3,9 +3,21 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "command_line.h" +#include "string_tools.h" namespace command_line { + std::string input_line(const std::string& prompt) + { + std::cout << prompt; + + std::string buf; + std::getline(std::cin, buf); + + return epee::string_tools::trim(buf); + + } + const arg_descriptor<bool> arg_help = {"help", "Produce help message"}; const arg_descriptor<bool> arg_version = {"version", "Output version information"}; const arg_descriptor<std::string> arg_data_dir = {"data-dir", "Specify data directory"}; diff --git a/src/common/command_line.h b/src/common/command_line.h index 860653772..a6f78569c 100644 --- a/src/common/command_line.h +++ b/src/common/command_line.h @@ -14,6 +14,9 @@ namespace command_line { + + std::string input_line(const std::string& prompt); + template<typename T, bool required = false> struct arg_descriptor; diff --git a/src/common/pod-class.h b/src/common/pod-class.h index c07edb208..10d680484 100644 --- a/src/common/pod-class.h +++ b/src/common/pod-class.h @@ -4,8 +4,14 @@ #pragma once +// FIXME: Why is this ifdef needed? Hopefully making it struct won't break things. + +/* #if defined(_MSC_VER) #define POD_CLASS struct #else #define POD_CLASS class #endif +*/ + +#define POD_CLASS struct diff --git a/src/crypto/aesb.c b/src/crypto/aesb.c new file mode 100644 index 000000000..ba48313da --- /dev/null +++ b/src/crypto/aesb.c @@ -0,0 +1,177 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 +*/ + +#include <stdint.h> + +#if defined(__cplusplus) +extern "C" +{ +#endif + +#define TABLE_ALIGN 32 +#define WPOLY 0x011b +#define N_COLS 4 +#define AES_BLOCK_SIZE 16 +#define RC_LENGTH (5 * (AES_BLOCK_SIZE / 4 - 2)) + +#if defined(_MSC_VER) +#define ALIGN __declspec(align(TABLE_ALIGN)) +#elif defined(__GNUC__) +#define ALIGN __attribute__ ((aligned(16))) +#else +#define ALIGN +#endif + +#define rf1(r,c) (r) +#define word_in(x,c) (*((uint32_t*)(x)+(c))) +#define word_out(x,c,v) (*((uint32_t*)(x)+(c)) = (v)) + +#define s(x,c) x[c] +#define si(y,x,c) (s(y,c) = word_in(x, c)) +#define so(y,x,c) word_out(y, c, s(x,c)) +#define state_in(y,x) si(y,x,0); si(y,x,1); si(y,x,2); si(y,x,3) +#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); so(y,x,3) +#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); rm(y,x,k,3) +#define to_byte(x) ((x) & 0xff) +#define bval(x,n) to_byte((x) >> (8 * (n))) + +#define fwd_var(x,r,c)\ + ( r == 0 ? ( c == 0 ? s(x,0) : c == 1 ? s(x,1) : c == 2 ? s(x,2) : s(x,3))\ + : r == 1 ? ( c == 0 ? s(x,1) : c == 1 ? s(x,2) : c == 2 ? s(x,3) : s(x,0))\ + : r == 2 ? ( c == 0 ? s(x,2) : c == 1 ? s(x,3) : c == 2 ? s(x,0) : s(x,1))\ + : ( c == 0 ? s(x,3) : c == 1 ? s(x,0) : c == 2 ? s(x,1) : s(x,2))) + +#define fwd_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ four_tables(x,t_use(f,n),fwd_var,rf1,c)) + +#define sb_data(w) {\ + w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5),\ + w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76),\ + w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0),\ + w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c), w(0xa4), w(0x72), w(0xc0),\ + w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc),\ + w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15),\ + w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a),\ + w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75),\ + w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0),\ + w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84),\ + w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b),\ + w(0x6a), w(0xcb), w(0xbe), w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf),\ + w(0xd0), w(0xef), w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85),\ + w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8),\ + w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5),\ + w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), w(0xd2),\ + w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17),\ + w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73),\ + w(0x60), w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88),\ + w(0x46), w(0xee), w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb),\ + w(0xe0), w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c),\ + w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79),\ + w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e), w(0xa9),\ + w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), w(0xae), w(0x08),\ + w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), w(0xc6),\ + w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a),\ + w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e),\ + w(0x61), w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e),\ + w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94),\ + w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf),\ + w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6), w(0x42), w(0x68),\ + w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), w(0x54), w(0xbb), w(0x16) } + +#define rc_data(w) {\ + w(0x01), w(0x02), w(0x04), w(0x08), w(0x10),w(0x20), w(0x40), w(0x80),\ + w(0x1b), w(0x36) } + +#define bytes2word(b0, b1, b2, b3) (((uint32_t)(b3) << 24) | \ + ((uint32_t)(b2) << 16) | ((uint32_t)(b1) << 8) | (b0)) + +#define h0(x) (x) +#define w0(p) bytes2word(p, 0, 0, 0) +#define w1(p) bytes2word(0, p, 0, 0) +#define w2(p) bytes2word(0, 0, p, 0) +#define w3(p) bytes2word(0, 0, 0, p) + +#define u0(p) bytes2word(f2(p), p, p, f3(p)) +#define u1(p) bytes2word(f3(p), f2(p), p, p) +#define u2(p) bytes2word(p, f3(p), f2(p), p) +#define u3(p) bytes2word(p, p, f3(p), f2(p)) + +#define v0(p) bytes2word(fe(p), f9(p), fd(p), fb(p)) +#define v1(p) bytes2word(fb(p), fe(p), f9(p), fd(p)) +#define v2(p) bytes2word(fd(p), fb(p), fe(p), f9(p)) +#define v3(p) bytes2word(f9(p), fd(p), fb(p), fe(p)) + +#define f2(x) ((x<<1) ^ (((x>>7) & 1) * WPOLY)) +#define f4(x) ((x<<2) ^ (((x>>6) & 1) * WPOLY) ^ (((x>>6) & 2) * WPOLY)) +#define f8(x) ((x<<3) ^ (((x>>5) & 1) * WPOLY) ^ (((x>>5) & 2) * WPOLY) ^ (((x>>5) & 4) * WPOLY)) +#define f3(x) (f2(x) ^ x) +#define f9(x) (f8(x) ^ x) +#define fb(x) (f8(x) ^ f2(x) ^ x) +#define fd(x) (f8(x) ^ f4(x) ^ x) +#define fe(x) (f8(x) ^ f4(x) ^ f2(x)) + +#define t_dec(m,n) t_##m##n +#define t_set(m,n) t_##m##n +#define t_use(m,n) t_##m##n + +#define d_4(t,n,b,e,f,g,h) ALIGN const t n[4][256] = { b(e), b(f), b(g), b(h) } + +#define four_tables(x,tab,vf,rf,c) \ + (tab[0][bval(vf(x,0,c),rf(0,c))] \ + ^ tab[1][bval(vf(x,1,c),rf(1,c))] \ + ^ tab[2][bval(vf(x,2,c),rf(2,c))] \ + ^ tab[3][bval(vf(x,3,c),rf(3,c))]) + +d_4(uint32_t, t_dec(f,n), sb_data, u0, u1, u2, u3); + +void aesb_single_round(const uint8_t *in, uint8_t *out, uint8_t *expandedKey) +{ + uint32_t b0[4], b1[4]; + const uint32_t *kp = (uint32_t *) expandedKey; + state_in(b0, in); + + round(fwd_rnd, b1, b0, kp); + + state_out(out, b1); +} + +void aesb_pseudo_round(const uint8_t *in, uint8_t *out, uint8_t *expandedKey) +{ + uint32_t b0[4], b1[4]; + const uint32_t *kp = (uint32_t *) expandedKey; + state_in(b0, in); + + round(fwd_rnd, b1, b0, kp); + round(fwd_rnd, b0, b1, kp + 1 * N_COLS); + round(fwd_rnd, b1, b0, kp + 2 * N_COLS); + round(fwd_rnd, b0, b1, kp + 3 * N_COLS); + round(fwd_rnd, b1, b0, kp + 4 * N_COLS); + round(fwd_rnd, b0, b1, kp + 5 * N_COLS); + round(fwd_rnd, b1, b0, kp + 6 * N_COLS); + round(fwd_rnd, b0, b1, kp + 7 * N_COLS); + round(fwd_rnd, b1, b0, kp + 8 * N_COLS); + round(fwd_rnd, b0, b1, kp + 9 * N_COLS); + + state_out(out, b0); +} + + +#if defined(__cplusplus) +} +#endif diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index f5f525700..98a17a3e4 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -50,6 +50,7 @@ namespace crypto { return &reinterpret_cast<const unsigned char &>(scalar); } + /* generate a random 32-byte (256-bit) integer and copy it to res */ static inline void random_scalar(ec_scalar &res) { unsigned char tmp[64]; generate_random_bytes(64, tmp); @@ -62,12 +63,32 @@ namespace crypto { sc_reduce32(&res); } - void crypto_ops::generate_keys(public_key &pub, secret_key &sec) { + /* + * generate public and secret keys from a random 256-bit integer + * TODO: allow specifiying random value (for wallet recovery) + * + */ + secret_key crypto_ops::generate_keys(public_key &pub, secret_key &sec, const secret_key& recovery_key, bool recover) { lock_guard<mutex> lock(random_lock); ge_p3 point; - random_scalar(sec); + + secret_key rng; + + if (recover) + { + rng = recovery_key; + } + else + { + random_scalar(rng); + } + sec = rng; + sc_reduce32(&sec); // reduce in case second round of keys (sendkeys) + ge_scalarmult_base(&point, &sec); ge_p3_tobytes(&pub, &point); + + return rng; } bool crypto_ops::check_key(const public_key &key) { diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 61641fbcf..024713df1 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -62,8 +62,8 @@ namespace crypto { void operator=(const crypto_ops &); ~crypto_ops(); - static void generate_keys(public_key &, secret_key &); - friend void generate_keys(public_key &, secret_key &); + static secret_key generate_keys(public_key &pub, secret_key &sec, const secret_key& recovery_key = secret_key(), bool recover = false); + friend secret_key generate_keys(public_key &pub, secret_key &sec, const secret_key& recovery_key, bool recover); static bool check_key(const public_key &); friend bool check_key(const public_key &); static bool secret_key_to_public_key(const secret_key &, public_key &); @@ -102,8 +102,8 @@ namespace crypto { /* Generate a new key pair */ - inline void generate_keys(public_key &pub, secret_key &sec) { - crypto_ops::generate_keys(pub, sec); + inline secret_key generate_keys(public_key &pub, secret_key &sec, const secret_key& recovery_key = secret_key(), bool recover = false) { + return crypto_ops::generate_keys(pub, sec, recovery_key, recover); } /* Check a public key. Returns true if it is valid, false otherwise. diff --git a/src/crypto/electrum-words.cpp b/src/crypto/electrum-words.cpp new file mode 100644 index 000000000..96a392d04 --- /dev/null +++ b/src/crypto/electrum-words.cpp @@ -0,0 +1,110 @@ +/* + * This file and its header file are for translating Electrum-style word lists + * into their equivalent byte representations for cross-compatibility with + * that method of "backing up" one's wallet keys. + */ + +#include <string> +#include <cassert> +#include <map> +#include <cstdint> +#include <vector> +#include <boost/algorithm/string.hpp> +#include "crypto/crypto.h" // for declaration of crypto::secret_key + +#include "crypto/electrum-words.h" + +namespace crypto +{ + namespace ElectrumWords + { + + /* convert words to bytes, 3 words -> 4 bytes + * returns: + * false if not a multiple of 3 words, or if a words is not in the + * words list + * + * true otherwise + */ + bool words_to_bytes(const std::string& words, crypto::secret_key& dst) + { + int n = NUMWORDS; // hardcoded because this is what electrum uses + + std::vector<std::string> wlist; + + boost::split(wlist, words, boost::is_any_of(" ")); + + // error on non-compliant word list + if (wlist.size() != 12 && wlist.size() != 24) return false; + + for (unsigned int i=0; i < wlist.size() / 3; i++) + { + uint32_t val; + uint32_t w1, w2, w3; + + // verify all three words exist in the word list + if (wordsMap.count(wlist[i*3]) == 0 || + wordsMap.count(wlist[i*3 + 1]) == 0 || + wordsMap.count(wlist[i*3 + 2]) == 0) + { + return false; + } + + w1 = wordsMap.at(wlist[i*3]); + w2 = wordsMap.at(wlist[i*3 + 1]); + w3 = wordsMap.at(wlist[i*3 + 2]); + + val = w1 + n * (((n - w1) + w2) % n) + n * n * (((n - w2) + w3) % n); + + if (!(val % n == w1)) return false; + + memcpy(dst.data + i * 4, &val, 4); // copy 4 bytes to position + } + + std::string wlist_copy = words; + if (wlist.size() == 12) + { + memcpy(dst.data, dst.data + 16, 16); // if electrum 12-word seed, duplicate + wlist_copy += ' '; + wlist_copy += words; + } + + return true; + } + + /* convert bytes to words, 4 bytes-> 3 words + * returns: + * false if wrong number of bytes (shouldn't be possible) + * true otherwise + */ + bool bytes_to_words(const crypto::secret_key& src, std::string& words) + { + int n = NUMWORDS; // hardcoded because this is what electrum uses + + if (sizeof(src.data) % 4 != 0) return false; + + // 8 bytes -> 3 words. 8 digits base 16 -> 3 digits base 1626 + for (unsigned int i=0; i < sizeof(src.data)/4; i++, words += ' ') + { + uint32_t w1, w2, w3; + + uint32_t val; + + memcpy(&val, (src.data) + (i * 4), 4); + + w1 = val % n; + w2 = ((val / n) + w1) % n; + w3 = (((val / n) / n) + w2) % n; + + words += wordsArray[w1]; + words += ' '; + words += wordsArray[w2]; + words += ' '; + words += wordsArray[w3]; + } + return false; + } + + } // namespace ElectrumWords + +} // namespace crypto diff --git a/src/crypto/electrum-words.h b/src/crypto/electrum-words.h new file mode 100644 index 000000000..a533299bd --- /dev/null +++ b/src/crypto/electrum-words.h @@ -0,0 +1,3280 @@ +/* + * This file and its cpp file are for translating Electrum-style word lists + * into their equivalent byte representations for cross-compatibility with + * that method of "backing up" one's wallet keys. + */ + +#include <string> +#include <cstdint> +#include <map> +#include "crypto/crypto.h" // for declaration of crypto::secret_key + +namespace crypto +{ + namespace ElectrumWords + { + + const int NUMWORDS = 1626; + + bool words_to_bytes(const std::string& words, crypto::secret_key& dst); + bool bytes_to_words(const crypto::secret_key& src, std::string& words); + + const std::map<std::string,uint32_t> wordsMap = { + {"like", 0}, + {"just", 1}, + {"love", 2}, + {"know", 3}, + {"never", 4}, + {"want", 5}, + {"time", 6}, + {"out", 7}, + {"there", 8}, + {"make", 9}, + {"look", 10}, + {"eye", 11}, + {"down", 12}, + {"only", 13}, + {"think", 14}, + {"heart", 15}, + {"back", 16}, + {"then", 17}, + {"into", 18}, + {"about", 19}, + {"more", 20}, + {"away", 21}, + {"still", 22}, + {"them", 23}, + {"take", 24}, + {"thing", 25}, + {"even", 26}, + {"through", 27}, + {"long", 28}, + {"always", 29}, + {"world", 30}, + {"too", 31}, + {"friend", 32}, + {"tell", 33}, + {"try", 34}, + {"hand", 35}, + {"thought", 36}, + {"over", 37}, + {"here", 38}, + {"other", 39}, + {"need", 40}, + {"smile", 41}, + {"again", 42}, + {"much", 43}, + {"cry", 44}, + {"been", 45}, + {"night", 46}, + {"ever", 47}, + {"little", 48}, + {"said", 49}, + {"end", 50}, + {"some", 51}, + {"those", 52}, + {"around", 53}, + {"mind", 54}, + {"people", 55}, + {"girl", 56}, + {"leave", 57}, + {"dream", 58}, + {"left", 59}, + {"turn", 60}, + {"myself", 61}, + {"give", 62}, + {"nothing", 63}, + {"really", 64}, + {"off", 65}, + {"before", 66}, + {"something", 67}, + {"find", 68}, + {"walk", 69}, + {"wish", 70}, + {"good", 71}, + {"once", 72}, + {"place", 73}, + {"ask", 74}, + {"stop", 75}, + {"keep", 76}, + {"watch", 77}, + {"seem", 78}, + {"everything", 79}, + {"wait", 80}, + {"got", 81}, + {"yet", 82}, + {"made", 83}, + {"remember", 84}, + {"start", 85}, + {"alone", 86}, + {"run", 87}, + {"hope", 88}, + {"maybe", 89}, + {"believe", 90}, + {"body", 91}, + {"hate", 92}, + {"after", 93}, + {"close", 94}, + {"talk", 95}, + {"stand", 96}, + {"own", 97}, + {"each", 98}, + {"hurt", 99}, + {"help", 100}, + {"home", 101}, + {"god", 102}, + {"soul", 103}, + {"new", 104}, + {"many", 105}, + {"two", 106}, + {"inside", 107}, + {"should", 108}, + {"true", 109}, + {"first", 110}, + {"fear", 111}, + {"mean", 112}, + {"better", 113}, + {"play", 114}, + {"another", 115}, + {"gone", 116}, + {"change", 117}, + {"use", 118}, + {"wonder", 119}, + {"someone", 120}, + {"hair", 121}, + {"cold", 122}, + {"open", 123}, + {"best", 124}, + {"any", 125}, + {"behind", 126}, + {"happen", 127}, + {"water", 128}, + {"dark", 129}, + {"laugh", 130}, + {"stay", 131}, + {"forever", 132}, + {"name", 133}, + {"work", 134}, + {"show", 135}, + {"sky", 136}, + {"break", 137}, + {"came", 138}, + {"deep", 139}, + {"door", 140}, + {"put", 141}, + {"black", 142}, + {"together", 143}, + {"upon", 144}, + {"happy", 145}, + {"such", 146}, + {"great", 147}, + {"white", 148}, + {"matter", 149}, + {"fill", 150}, + {"past", 151}, + {"please", 152}, + {"burn", 153}, + {"cause", 154}, + {"enough", 155}, + {"touch", 156}, + {"moment", 157}, + {"soon", 158}, + {"voice", 159}, + {"scream", 160}, + {"anything", 161}, + {"stare", 162}, + {"sound", 163}, + {"red", 164}, + {"everyone", 165}, + {"hide", 166}, + {"kiss", 167}, + {"truth", 168}, + {"death", 169}, + {"beautiful", 170}, + {"mine", 171}, + {"blood", 172}, + {"broken", 173}, + {"very", 174}, + {"pass", 175}, + {"next", 176}, + {"forget", 177}, + {"tree", 178}, + {"wrong", 179}, + {"air", 180}, + {"mother", 181}, + {"understand", 182}, + {"lip", 183}, + {"hit", 184}, + {"wall", 185}, + {"memory", 186}, + {"sleep", 187}, + {"free", 188}, + {"high", 189}, + {"realize", 190}, + {"school", 191}, + {"might", 192}, + {"skin", 193}, + {"sweet", 194}, + {"perfect", 195}, + {"blue", 196}, + {"kill", 197}, + {"breath", 198}, + {"dance", 199}, + {"against", 200}, + {"fly", 201}, + {"between", 202}, + {"grow", 203}, + {"strong", 204}, + {"under", 205}, + {"listen", 206}, + {"bring", 207}, + {"sometimes", 208}, + {"speak", 209}, + {"pull", 210}, + {"person", 211}, + {"become", 212}, + {"family", 213}, + {"begin", 214}, + {"ground", 215}, + {"real", 216}, + {"small", 217}, + {"father", 218}, + {"sure", 219}, + {"feet", 220}, + {"rest", 221}, + {"young", 222}, + {"finally", 223}, + {"land", 224}, + {"across", 225}, + {"today", 226}, + {"different", 227}, + {"guy", 228}, + {"line", 229}, + {"fire", 230}, + {"reason", 231}, + {"reach", 232}, + {"second", 233}, + {"slowly", 234}, + {"write", 235}, + {"eat", 236}, + {"smell", 237}, + {"mouth", 238}, + {"step", 239}, + {"learn", 240}, + {"three", 241}, + {"floor", 242}, + {"promise", 243}, + {"breathe", 244}, + {"darkness", 245}, + {"push", 246}, + {"earth", 247}, + {"guess", 248}, + {"save", 249}, + {"song", 250}, + {"above", 251}, + {"along", 252}, + {"both", 253}, + {"color", 254}, + {"house", 255}, + {"almost", 256}, + {"sorry", 257}, + {"anymore", 258}, + {"brother", 259}, + {"okay", 260}, + {"dear", 261}, + {"game", 262}, + {"fade", 263}, + {"already", 264}, + {"apart", 265}, + {"warm", 266}, + {"beauty", 267}, + {"heard", 268}, + {"notice", 269}, + {"question", 270}, + {"shine", 271}, + {"began", 272}, + {"piece", 273}, + {"whole", 274}, + {"shadow", 275}, + {"secret", 276}, + {"street", 277}, + {"within", 278}, + {"finger", 279}, + {"point", 280}, + {"morning", 281}, + {"whisper", 282}, + {"child", 283}, + {"moon", 284}, + {"green", 285}, + {"story", 286}, + {"glass", 287}, + {"kid", 288}, + {"silence", 289}, + {"since", 290}, + {"soft", 291}, + {"yourself", 292}, + {"empty", 293}, + {"shall", 294}, + {"angel", 295}, + {"answer", 296}, + {"baby", 297}, + {"bright", 298}, + {"dad", 299}, + {"path", 300}, + {"worry", 301}, + {"hour", 302}, + {"drop", 303}, + {"follow", 304}, + {"power", 305}, + {"war", 306}, + {"half", 307}, + {"flow", 308}, + {"heaven", 309}, + {"act", 310}, + {"chance", 311}, + {"fact", 312}, + {"least", 313}, + {"tired", 314}, + {"children", 315}, + {"near", 316}, + {"quite", 317}, + {"afraid", 318}, + {"rise", 319}, + {"sea", 320}, + {"taste", 321}, + {"window", 322}, + {"cover", 323}, + {"nice", 324}, + {"trust", 325}, + {"lot", 326}, + {"sad", 327}, + {"cool", 328}, + {"force", 329}, + {"peace", 330}, + {"return", 331}, + {"blind", 332}, + {"easy", 333}, + {"ready", 334}, + {"roll", 335}, + {"rose", 336}, + {"drive", 337}, + {"held", 338}, + {"music", 339}, + {"beneath", 340}, + {"hang", 341}, + {"mom", 342}, + {"paint", 343}, + {"emotion", 344}, + {"quiet", 345}, + {"clear", 346}, + {"cloud", 347}, + {"few", 348}, + {"pretty", 349}, + {"bird", 350}, + {"outside", 351}, + {"paper", 352}, + {"picture", 353}, + {"front", 354}, + {"rock", 355}, + {"simple", 356}, + {"anyone", 357}, + {"meant", 358}, + {"reality", 359}, + {"road", 360}, + {"sense", 361}, + {"waste", 362}, + {"bit", 363}, + {"leaf", 364}, + {"thank", 365}, + {"happiness", 366}, + {"meet", 367}, + {"men", 368}, + {"smoke", 369}, + {"truly", 370}, + {"decide", 371}, + {"self", 372}, + {"age", 373}, + {"book", 374}, + {"form", 375}, + {"alive", 376}, + {"carry", 377}, + {"escape", 378}, + {"damn", 379}, + {"instead", 380}, + {"able", 381}, + {"ice", 382}, + {"minute", 383}, + {"throw", 384}, + {"catch", 385}, + {"leg", 386}, + {"ring", 387}, + {"course", 388}, + {"goodbye", 389}, + {"lead", 390}, + {"poem", 391}, + {"sick", 392}, + {"corner", 393}, + {"desire", 394}, + {"known", 395}, + {"problem", 396}, + {"remind", 397}, + {"shoulder", 398}, + {"suppose", 399}, + {"toward", 400}, + {"wave", 401}, + {"drink", 402}, + {"jump", 403}, + {"woman", 404}, + {"pretend", 405}, + {"sister", 406}, + {"week", 407}, + {"human", 408}, + {"joy", 409}, + {"crack", 410}, + {"grey", 411}, + {"pray", 412}, + {"surprise", 413}, + {"dry", 414}, + {"knee", 415}, + {"less", 416}, + {"search", 417}, + {"bleed", 418}, + {"caught", 419}, + {"clean", 420}, + {"embrace", 421}, + {"future", 422}, + {"king", 423}, + {"son", 424}, + {"sorrow", 425}, + {"chest", 426}, + {"hug", 427}, + {"remain", 428}, + {"sat", 429}, + {"worth", 430}, + {"blow", 431}, + {"daddy", 432}, + {"final", 433}, + {"parent", 434}, + {"tight", 435}, + {"also", 436}, + {"create", 437}, + {"lonely", 438}, + {"safe", 439}, + {"cross", 440}, + {"dress", 441}, + {"evil", 442}, + {"silent", 443}, + {"bone", 444}, + {"fate", 445}, + {"perhaps", 446}, + {"anger", 447}, + {"class", 448}, + {"scar", 449}, + {"snow", 450}, + {"tiny", 451}, + {"tonight", 452}, + {"continue", 453}, + {"control", 454}, + {"dog", 455}, + {"edge", 456}, + {"mirror", 457}, + {"month", 458}, + {"suddenly", 459}, + {"comfort", 460}, + {"given", 461}, + {"loud", 462}, + {"quickly", 463}, + {"gaze", 464}, + {"plan", 465}, + {"rush", 466}, + {"stone", 467}, + {"town", 468}, + {"battle", 469}, + {"ignore", 470}, + {"spirit", 471}, + {"stood", 472}, + {"stupid", 473}, + {"yours", 474}, + {"brown", 475}, + {"build", 476}, + {"dust", 477}, + {"hey", 478}, + {"kept", 479}, + {"pay", 480}, + {"phone", 481}, + {"twist", 482}, + {"although", 483}, + {"ball", 484}, + {"beyond", 485}, + {"hidden", 486}, + {"nose", 487}, + {"taken", 488}, + {"fail", 489}, + {"float", 490}, + {"pure", 491}, + {"somehow", 492}, + {"wash", 493}, + {"wrap", 494}, + {"angry", 495}, + {"cheek", 496}, + {"creature", 497}, + {"forgotten", 498}, + {"heat", 499}, + {"rip", 500}, + {"single", 501}, + {"space", 502}, + {"special", 503}, + {"weak", 504}, + {"whatever", 505}, + {"yell", 506}, + {"anyway", 507}, + {"blame", 508}, + {"job", 509}, + {"choose", 510}, + {"country", 511}, + {"curse", 512}, + {"drift", 513}, + {"echo", 514}, + {"figure", 515}, + {"grew", 516}, + {"laughter", 517}, + {"neck", 518}, + {"suffer", 519}, + {"worse", 520}, + {"yeah", 521}, + {"disappear", 522}, + {"foot", 523}, + {"forward", 524}, + {"knife", 525}, + {"mess", 526}, + {"somewhere", 527}, + {"stomach", 528}, + {"storm", 529}, + {"beg", 530}, + {"idea", 531}, + {"lift", 532}, + {"offer", 533}, + {"breeze", 534}, + {"field", 535}, + {"five", 536}, + {"often", 537}, + {"simply", 538}, + {"stuck", 539}, + {"win", 540}, + {"allow", 541}, + {"confuse", 542}, + {"enjoy", 543}, + {"except", 544}, + {"flower", 545}, + {"seek", 546}, + {"strength", 547}, + {"calm", 548}, + {"grin", 549}, + {"gun", 550}, + {"heavy", 551}, + {"hill", 552}, + {"large", 553}, + {"ocean", 554}, + {"shoe", 555}, + {"sigh", 556}, + {"straight", 557}, + {"summer", 558}, + {"tongue", 559}, + {"accept", 560}, + {"crazy", 561}, + {"everyday", 562}, + {"exist", 563}, + {"grass", 564}, + {"mistake", 565}, + {"sent", 566}, + {"shut", 567}, + {"surround", 568}, + {"table", 569}, + {"ache", 570}, + {"brain", 571}, + {"destroy", 572}, + {"heal", 573}, + {"nature", 574}, + {"shout", 575}, + {"sign", 576}, + {"stain", 577}, + {"choice", 578}, + {"doubt", 579}, + {"glance", 580}, + {"glow", 581}, + {"mountain", 582}, + {"queen", 583}, + {"stranger", 584}, + {"throat", 585}, + {"tomorrow", 586}, + {"city", 587}, + {"either", 588}, + {"fish", 589}, + {"flame", 590}, + {"rather", 591}, + {"shape", 592}, + {"spin", 593}, + {"spread", 594}, + {"ash", 595}, + {"distance", 596}, + {"finish", 597}, + {"image", 598}, + {"imagine", 599}, + {"important", 600}, + {"nobody", 601}, + {"shatter", 602}, + {"warmth", 603}, + {"became", 604}, + {"feed", 605}, + {"flesh", 606}, + {"funny", 607}, + {"lust", 608}, + {"shirt", 609}, + {"trouble", 610}, + {"yellow", 611}, + {"attention", 612}, + {"bare", 613}, + {"bite", 614}, + {"money", 615}, + {"protect", 616}, + {"amaze", 617}, + {"appear", 618}, + {"born", 619}, + {"choke", 620}, + {"completely", 621}, + {"daughter", 622}, + {"fresh", 623}, + {"friendship", 624}, + {"gentle", 625}, + {"probably", 626}, + {"six", 627}, + {"deserve", 628}, + {"expect", 629}, + {"grab", 630}, + {"middle", 631}, + {"nightmare", 632}, + {"river", 633}, + {"thousand", 634}, + {"weight", 635}, + {"worst", 636}, + {"wound", 637}, + {"barely", 638}, + {"bottle", 639}, + {"cream", 640}, + {"regret", 641}, + {"relationship", 642}, + {"stick", 643}, + {"test", 644}, + {"crush", 645}, + {"endless", 646}, + {"fault", 647}, + {"itself", 648}, + {"rule", 649}, + {"spill", 650}, + {"art", 651}, + {"circle", 652}, + {"join", 653}, + {"kick", 654}, + {"mask", 655}, + {"master", 656}, + {"passion", 657}, + {"quick", 658}, + {"raise", 659}, + {"smooth", 660}, + {"unless", 661}, + {"wander", 662}, + {"actually", 663}, + {"broke", 664}, + {"chair", 665}, + {"deal", 666}, + {"favorite", 667}, + {"gift", 668}, + {"note", 669}, + {"number", 670}, + {"sweat", 671}, + {"box", 672}, + {"chill", 673}, + {"clothes", 674}, + {"lady", 675}, + {"mark", 676}, + {"park", 677}, + {"poor", 678}, + {"sadness", 679}, + {"tie", 680}, + {"animal", 681}, + {"belong", 682}, + {"brush", 683}, + {"consume", 684}, + {"dawn", 685}, + {"forest", 686}, + {"innocent", 687}, + {"pen", 688}, + {"pride", 689}, + {"stream", 690}, + {"thick", 691}, + {"clay", 692}, + {"complete", 693}, + {"count", 694}, + {"draw", 695}, + {"faith", 696}, + {"press", 697}, + {"silver", 698}, + {"struggle", 699}, + {"surface", 700}, + {"taught", 701}, + {"teach", 702}, + {"wet", 703}, + {"bless", 704}, + {"chase", 705}, + {"climb", 706}, + {"enter", 707}, + {"letter", 708}, + {"melt", 709}, + {"metal", 710}, + {"movie", 711}, + {"stretch", 712}, + {"swing", 713}, + {"vision", 714}, + {"wife", 715}, + {"beside", 716}, + {"crash", 717}, + {"forgot", 718}, + {"guide", 719}, + {"haunt", 720}, + {"joke", 721}, + {"knock", 722}, + {"plant", 723}, + {"pour", 724}, + {"prove", 725}, + {"reveal", 726}, + {"steal", 727}, + {"stuff", 728}, + {"trip", 729}, + {"wood", 730}, + {"wrist", 731}, + {"bother", 732}, + {"bottom", 733}, + {"crawl", 734}, + {"crowd", 735}, + {"fix", 736}, + {"forgive", 737}, + {"frown", 738}, + {"grace", 739}, + {"loose", 740}, + {"lucky", 741}, + {"party", 742}, + {"release", 743}, + {"surely", 744}, + {"survive", 745}, + {"teacher", 746}, + {"gently", 747}, + {"grip", 748}, + {"speed", 749}, + {"suicide", 750}, + {"travel", 751}, + {"treat", 752}, + {"vein", 753}, + {"written", 754}, + {"cage", 755}, + {"chain", 756}, + {"conversation", 757}, + {"date", 758}, + {"enemy", 759}, + {"however", 760}, + {"interest", 761}, + {"million", 762}, + {"page", 763}, + {"pink", 764}, + {"proud", 765}, + {"sway", 766}, + {"themselves", 767}, + {"winter", 768}, + {"church", 769}, + {"cruel", 770}, + {"cup", 771}, + {"demon", 772}, + {"experience", 773}, + {"freedom", 774}, + {"pair", 775}, + {"pop", 776}, + {"purpose", 777}, + {"respect", 778}, + {"shoot", 779}, + {"softly", 780}, + {"state", 781}, + {"strange", 782}, + {"bar", 783}, + {"birth", 784}, + {"curl", 785}, + {"dirt", 786}, + {"excuse", 787}, + {"lord", 788}, + {"lovely", 789}, + {"monster", 790}, + {"order", 791}, + {"pack", 792}, + {"pants", 793}, + {"pool", 794}, + {"scene", 795}, + {"seven", 796}, + {"shame", 797}, + {"slide", 798}, + {"ugly", 799}, + {"among", 800}, + {"blade", 801}, + {"blonde", 802}, + {"closet", 803}, + {"creek", 804}, + {"deny", 805}, + {"drug", 806}, + {"eternity", 807}, + {"gain", 808}, + {"grade", 809}, + {"handle", 810}, + {"key", 811}, + {"linger", 812}, + {"pale", 813}, + {"prepare", 814}, + {"swallow", 815}, + {"swim", 816}, + {"tremble", 817}, + {"wheel", 818}, + {"won", 819}, + {"cast", 820}, + {"cigarette", 821}, + {"claim", 822}, + {"college", 823}, + {"direction", 824}, + {"dirty", 825}, + {"gather", 826}, + {"ghost", 827}, + {"hundred", 828}, + {"loss", 829}, + {"lung", 830}, + {"orange", 831}, + {"present", 832}, + {"swear", 833}, + {"swirl", 834}, + {"twice", 835}, + {"wild", 836}, + {"bitter", 837}, + {"blanket", 838}, + {"doctor", 839}, + {"everywhere", 840}, + {"flash", 841}, + {"grown", 842}, + {"knowledge", 843}, + {"numb", 844}, + {"pressure", 845}, + {"radio", 846}, + {"repeat", 847}, + {"ruin", 848}, + {"spend", 849}, + {"unknown", 850}, + {"buy", 851}, + {"clock", 852}, + {"devil", 853}, + {"early", 854}, + {"false", 855}, + {"fantasy", 856}, + {"pound", 857}, + {"precious", 858}, + {"refuse", 859}, + {"sheet", 860}, + {"teeth", 861}, + {"welcome", 862}, + {"add", 863}, + {"ahead", 864}, + {"block", 865}, + {"bury", 866}, + {"caress", 867}, + {"content", 868}, + {"depth", 869}, + {"despite", 870}, + {"distant", 871}, + {"marry", 872}, + {"purple", 873}, + {"threw", 874}, + {"whenever", 875}, + {"bomb", 876}, + {"dull", 877}, + {"easily", 878}, + {"grasp", 879}, + {"hospital", 880}, + {"innocence", 881}, + {"normal", 882}, + {"receive", 883}, + {"reply", 884}, + {"rhyme", 885}, + {"shade", 886}, + {"someday", 887}, + {"sword", 888}, + {"toe", 889}, + {"visit", 890}, + {"asleep", 891}, + {"bought", 892}, + {"center", 893}, + {"consider", 894}, + {"flat", 895}, + {"hero", 896}, + {"history", 897}, + {"ink", 898}, + {"insane", 899}, + {"muscle", 900}, + {"mystery", 901}, + {"pocket", 902}, + {"reflection", 903}, + {"shove", 904}, + {"silently", 905}, + {"smart", 906}, + {"soldier", 907}, + {"spot", 908}, + {"stress", 909}, + {"train", 910}, + {"type", 911}, + {"view", 912}, + {"whether", 913}, + {"bus", 914}, + {"energy", 915}, + {"explain", 916}, + {"holy", 917}, + {"hunger", 918}, + {"inch", 919}, + {"magic", 920}, + {"mix", 921}, + {"noise", 922}, + {"nowhere", 923}, + {"prayer", 924}, + {"presence", 925}, + {"shock", 926}, + {"snap", 927}, + {"spider", 928}, + {"study", 929}, + {"thunder", 930}, + {"trail", 931}, + {"admit", 932}, + {"agree", 933}, + {"bag", 934}, + {"bang", 935}, + {"bound", 936}, + {"butterfly", 937}, + {"cute", 938}, + {"exactly", 939}, + {"explode", 940}, + {"familiar", 941}, + {"fold", 942}, + {"further", 943}, + {"pierce", 944}, + {"reflect", 945}, + {"scent", 946}, + {"selfish", 947}, + {"sharp", 948}, + {"sink", 949}, + {"spring", 950}, + {"stumble", 951}, + {"universe", 952}, + {"weep", 953}, + {"women", 954}, + {"wonderful", 955}, + {"action", 956}, + {"ancient", 957}, + {"attempt", 958}, + {"avoid", 959}, + {"birthday", 960}, + {"branch", 961}, + {"chocolate", 962}, + {"core", 963}, + {"depress", 964}, + {"drunk", 965}, + {"especially", 966}, + {"focus", 967}, + {"fruit", 968}, + {"honest", 969}, + {"match", 970}, + {"palm", 971}, + {"perfectly", 972}, + {"pillow", 973}, + {"pity", 974}, + {"poison", 975}, + {"roar", 976}, + {"shift", 977}, + {"slightly", 978}, + {"thump", 979}, + {"truck", 980}, + {"tune", 981}, + {"twenty", 982}, + {"unable", 983}, + {"wipe", 984}, + {"wrote", 985}, + {"coat", 986}, + {"constant", 987}, + {"dinner", 988}, + {"drove", 989}, + {"egg", 990}, + {"eternal", 991}, + {"flight", 992}, + {"flood", 993}, + {"frame", 994}, + {"freak", 995}, + {"gasp", 996}, + {"glad", 997}, + {"hollow", 998}, + {"motion", 999}, + {"peer", 1000}, + {"plastic", 1001}, + {"root", 1002}, + {"screen", 1003}, + {"season", 1004}, + {"sting", 1005}, + {"strike", 1006}, + {"team", 1007}, + {"unlike", 1008}, + {"victim", 1009}, + {"volume", 1010}, + {"warn", 1011}, + {"weird", 1012}, + {"attack", 1013}, + {"await", 1014}, + {"awake", 1015}, + {"built", 1016}, + {"charm", 1017}, + {"crave", 1018}, + {"despair", 1019}, + {"fought", 1020}, + {"grant", 1021}, + {"grief", 1022}, + {"horse", 1023}, + {"limit", 1024}, + {"message", 1025}, + {"ripple", 1026}, + {"sanity", 1027}, + {"scatter", 1028}, + {"serve", 1029}, + {"split", 1030}, + {"string", 1031}, + {"trick", 1032}, + {"annoy", 1033}, + {"blur", 1034}, + {"boat", 1035}, + {"brave", 1036}, + {"clearly", 1037}, + {"cling", 1038}, + {"connect", 1039}, + {"fist", 1040}, + {"forth", 1041}, + {"imagination", 1042}, + {"iron", 1043}, + {"jock", 1044}, + {"judge", 1045}, + {"lesson", 1046}, + {"milk", 1047}, + {"misery", 1048}, + {"nail", 1049}, + {"naked", 1050}, + {"ourselves", 1051}, + {"poet", 1052}, + {"possible", 1053}, + {"princess", 1054}, + {"sail", 1055}, + {"size", 1056}, + {"snake", 1057}, + {"society", 1058}, + {"stroke", 1059}, + {"torture", 1060}, + {"toss", 1061}, + {"trace", 1062}, + {"wise", 1063}, + {"bloom", 1064}, + {"bullet", 1065}, + {"cell", 1066}, + {"check", 1067}, + {"cost", 1068}, + {"darling", 1069}, + {"during", 1070}, + {"footstep", 1071}, + {"fragile", 1072}, + {"hallway", 1073}, + {"hardly", 1074}, + {"horizon", 1075}, + {"invisible", 1076}, + {"journey", 1077}, + {"midnight", 1078}, + {"mud", 1079}, + {"nod", 1080}, + {"pause", 1081}, + {"relax", 1082}, + {"shiver", 1083}, + {"sudden", 1084}, + {"value", 1085}, + {"youth", 1086}, + {"abuse", 1087}, + {"admire", 1088}, + {"blink", 1089}, + {"breast", 1090}, + {"bruise", 1091}, + {"constantly", 1092}, + {"couple", 1093}, + {"creep", 1094}, + {"curve", 1095}, + {"difference", 1096}, + {"dumb", 1097}, + {"emptiness", 1098}, + {"gotta", 1099}, + {"honor", 1100}, + {"plain", 1101}, + {"planet", 1102}, + {"recall", 1103}, + {"rub", 1104}, + {"ship", 1105}, + {"slam", 1106}, + {"soar", 1107}, + {"somebody", 1108}, + {"tightly", 1109}, + {"weather", 1110}, + {"adore", 1111}, + {"approach", 1112}, + {"bond", 1113}, + {"bread", 1114}, + {"burst", 1115}, + {"candle", 1116}, + {"coffee", 1117}, + {"cousin", 1118}, + {"crime", 1119}, + {"desert", 1120}, + {"flutter", 1121}, + {"frozen", 1122}, + {"grand", 1123}, + {"heel", 1124}, + {"hello", 1125}, + {"language", 1126}, + {"level", 1127}, + {"movement", 1128}, + {"pleasure", 1129}, + {"powerful", 1130}, + {"random", 1131}, + {"rhythm", 1132}, + {"settle", 1133}, + {"silly", 1134}, + {"slap", 1135}, + {"sort", 1136}, + {"spoken", 1137}, + {"steel", 1138}, + {"threaten", 1139}, + {"tumble", 1140}, + {"upset", 1141}, + {"aside", 1142}, + {"awkward", 1143}, + {"bee", 1144}, + {"blank", 1145}, + {"board", 1146}, + {"button", 1147}, + {"card", 1148}, + {"carefully", 1149}, + {"complain", 1150}, + {"crap", 1151}, + {"deeply", 1152}, + {"discover", 1153}, + {"drag", 1154}, + {"dread", 1155}, + {"effort", 1156}, + {"entire", 1157}, + {"fairy", 1158}, + {"giant", 1159}, + {"gotten", 1160}, + {"greet", 1161}, + {"illusion", 1162}, + {"jeans", 1163}, + {"leap", 1164}, + {"liquid", 1165}, + {"march", 1166}, + {"mend", 1167}, + {"nervous", 1168}, + {"nine", 1169}, + {"replace", 1170}, + {"rope", 1171}, + {"spine", 1172}, + {"stole", 1173}, + {"terror", 1174}, + {"accident", 1175}, + {"apple", 1176}, + {"balance", 1177}, + {"boom", 1178}, + {"childhood", 1179}, + {"collect", 1180}, + {"demand", 1181}, + {"depression", 1182}, + {"eventually", 1183}, + {"faint", 1184}, + {"glare", 1185}, + {"goal", 1186}, + {"group", 1187}, + {"honey", 1188}, + {"kitchen", 1189}, + {"laid", 1190}, + {"limb", 1191}, + {"machine", 1192}, + {"mere", 1193}, + {"mold", 1194}, + {"murder", 1195}, + {"nerve", 1196}, + {"painful", 1197}, + {"poetry", 1198}, + {"prince", 1199}, + {"rabbit", 1200}, + {"shelter", 1201}, + {"shore", 1202}, + {"shower", 1203}, + {"soothe", 1204}, + {"stair", 1205}, + {"steady", 1206}, + {"sunlight", 1207}, + {"tangle", 1208}, + {"tease", 1209}, + {"treasure", 1210}, + {"uncle", 1211}, + {"begun", 1212}, + {"bliss", 1213}, + {"canvas", 1214}, + {"cheer", 1215}, + {"claw", 1216}, + {"clutch", 1217}, + {"commit", 1218}, + {"crimson", 1219}, + {"crystal", 1220}, + {"delight", 1221}, + {"doll", 1222}, + {"existence", 1223}, + {"express", 1224}, + {"fog", 1225}, + {"football", 1226}, + {"gay", 1227}, + {"goose", 1228}, + {"guard", 1229}, + {"hatred", 1230}, + {"illuminate", 1231}, + {"mass", 1232}, + {"math", 1233}, + {"mourn", 1234}, + {"rich", 1235}, + {"rough", 1236}, + {"skip", 1237}, + {"stir", 1238}, + {"student", 1239}, + {"style", 1240}, + {"support", 1241}, + {"thorn", 1242}, + {"tough", 1243}, + {"yard", 1244}, + {"yearn", 1245}, + {"yesterday", 1246}, + {"advice", 1247}, + {"appreciate", 1248}, + {"autumn", 1249}, + {"bank", 1250}, + {"beam", 1251}, + {"bowl", 1252}, + {"capture", 1253}, + {"carve", 1254}, + {"collapse", 1255}, + {"confusion", 1256}, + {"creation", 1257}, + {"dove", 1258}, + {"feather", 1259}, + {"girlfriend", 1260}, + {"glory", 1261}, + {"government", 1262}, + {"harsh", 1263}, + {"hop", 1264}, + {"inner", 1265}, + {"loser", 1266}, + {"moonlight", 1267}, + {"neighbor", 1268}, + {"neither", 1269}, + {"peach", 1270}, + {"pig", 1271}, + {"praise", 1272}, + {"screw", 1273}, + {"shield", 1274}, + {"shimmer", 1275}, + {"sneak", 1276}, + {"stab", 1277}, + {"subject", 1278}, + {"throughout", 1279}, + {"thrown", 1280}, + {"tower", 1281}, + {"twirl", 1282}, + {"wow", 1283}, + {"army", 1284}, + {"arrive", 1285}, + {"bathroom", 1286}, + {"bump", 1287}, + {"cease", 1288}, + {"cookie", 1289}, + {"couch", 1290}, + {"courage", 1291}, + {"dim", 1292}, + {"guilt", 1293}, + {"howl", 1294}, + {"hum", 1295}, + {"husband", 1296}, + {"insult", 1297}, + {"led", 1298}, + {"lunch", 1299}, + {"mock", 1300}, + {"mostly", 1301}, + {"natural", 1302}, + {"nearly", 1303}, + {"needle", 1304}, + {"nerd", 1305}, + {"peaceful", 1306}, + {"perfection", 1307}, + {"pile", 1308}, + {"price", 1309}, + {"remove", 1310}, + {"roam", 1311}, + {"sanctuary", 1312}, + {"serious", 1313}, + {"shiny", 1314}, + {"shook", 1315}, + {"sob", 1316}, + {"stolen", 1317}, + {"tap", 1318}, + {"vain", 1319}, + {"void", 1320}, + {"warrior", 1321}, + {"wrinkle", 1322}, + {"affection", 1323}, + {"apologize", 1324}, + {"blossom", 1325}, + {"bounce", 1326}, + {"bridge", 1327}, + {"cheap", 1328}, + {"crumble", 1329}, + {"decision", 1330}, + {"descend", 1331}, + {"desperately", 1332}, + {"dig", 1333}, + {"dot", 1334}, + {"flip", 1335}, + {"frighten", 1336}, + {"heartbeat", 1337}, + {"huge", 1338}, + {"lazy", 1339}, + {"lick", 1340}, + {"odd", 1341}, + {"opinion", 1342}, + {"process", 1343}, + {"puzzle", 1344}, + {"quietly", 1345}, + {"retreat", 1346}, + {"score", 1347}, + {"sentence", 1348}, + {"separate", 1349}, + {"situation", 1350}, + {"skill", 1351}, + {"soak", 1352}, + {"square", 1353}, + {"stray", 1354}, + {"taint", 1355}, + {"task", 1356}, + {"tide", 1357}, + {"underneath", 1358}, + {"veil", 1359}, + {"whistle", 1360}, + {"anywhere", 1361}, + {"bedroom", 1362}, + {"bid", 1363}, + {"bloody", 1364}, + {"burden", 1365}, + {"careful", 1366}, + {"compare", 1367}, + {"concern", 1368}, + {"curtain", 1369}, + {"decay", 1370}, + {"defeat", 1371}, + {"describe", 1372}, + {"double", 1373}, + {"dreamer", 1374}, + {"driver", 1375}, + {"dwell", 1376}, + {"evening", 1377}, + {"flare", 1378}, + {"flicker", 1379}, + {"grandma", 1380}, + {"guitar", 1381}, + {"harm", 1382}, + {"horrible", 1383}, + {"hungry", 1384}, + {"indeed", 1385}, + {"lace", 1386}, + {"melody", 1387}, + {"monkey", 1388}, + {"nation", 1389}, + {"object", 1390}, + {"obviously", 1391}, + {"rainbow", 1392}, + {"salt", 1393}, + {"scratch", 1394}, + {"shown", 1395}, + {"shy", 1396}, + {"stage", 1397}, + {"stun", 1398}, + {"third", 1399}, + {"tickle", 1400}, + {"useless", 1401}, + {"weakness", 1402}, + {"worship", 1403}, + {"worthless", 1404}, + {"afternoon", 1405}, + {"beard", 1406}, + {"boyfriend", 1407}, + {"bubble", 1408}, + {"busy", 1409}, + {"certain", 1410}, + {"chin", 1411}, + {"concrete", 1412}, + {"desk", 1413}, + {"diamond", 1414}, + {"doom", 1415}, + {"drawn", 1416}, + {"due", 1417}, + {"felicity", 1418}, + {"freeze", 1419}, + {"frost", 1420}, + {"garden", 1421}, + {"glide", 1422}, + {"harmony", 1423}, + {"hopefully", 1424}, + {"hunt", 1425}, + {"jealous", 1426}, + {"lightning", 1427}, + {"mama", 1428}, + {"mercy", 1429}, + {"peel", 1430}, + {"physical", 1431}, + {"position", 1432}, + {"pulse", 1433}, + {"punch", 1434}, + {"quit", 1435}, + {"rant", 1436}, + {"respond", 1437}, + {"salty", 1438}, + {"sane", 1439}, + {"satisfy", 1440}, + {"savior", 1441}, + {"sheep", 1442}, + {"slept", 1443}, + {"social", 1444}, + {"sport", 1445}, + {"tuck", 1446}, + {"utter", 1447}, + {"valley", 1448}, + {"wolf", 1449}, + {"aim", 1450}, + {"alas", 1451}, + {"alter", 1452}, + {"arrow", 1453}, + {"awaken", 1454}, + {"beaten", 1455}, + {"belief", 1456}, + {"brand", 1457}, + {"ceiling", 1458}, + {"cheese", 1459}, + {"clue", 1460}, + {"confidence", 1461}, + {"connection", 1462}, + {"daily", 1463}, + {"disguise", 1464}, + {"eager", 1465}, + {"erase", 1466}, + {"essence", 1467}, + {"everytime", 1468}, + {"expression", 1469}, + {"fan", 1470}, + {"flag", 1471}, + {"flirt", 1472}, + {"foul", 1473}, + {"fur", 1474}, + {"giggle", 1475}, + {"glorious", 1476}, + {"ignorance", 1477}, + {"law", 1478}, + {"lifeless", 1479}, + {"measure", 1480}, + {"mighty", 1481}, + {"muse", 1482}, + {"north", 1483}, + {"opposite", 1484}, + {"paradise", 1485}, + {"patience", 1486}, + {"patient", 1487}, + {"pencil", 1488}, + {"petal", 1489}, + {"plate", 1490}, + {"ponder", 1491}, + {"possibly", 1492}, + {"practice", 1493}, + {"slice", 1494}, + {"spell", 1495}, + {"stock", 1496}, + {"strife", 1497}, + {"strip", 1498}, + {"suffocate", 1499}, + {"suit", 1500}, + {"tender", 1501}, + {"tool", 1502}, + {"trade", 1503}, + {"velvet", 1504}, + {"verse", 1505}, + {"waist", 1506}, + {"witch", 1507}, + {"aunt", 1508}, + {"bench", 1509}, + {"bold", 1510}, + {"cap", 1511}, + {"certainly", 1512}, + {"click", 1513}, + {"companion", 1514}, + {"creator", 1515}, + {"dart", 1516}, + {"delicate", 1517}, + {"determine", 1518}, + {"dish", 1519}, + {"dragon", 1520}, + {"drama", 1521}, + {"drum", 1522}, + {"dude", 1523}, + {"everybody", 1524}, + {"feast", 1525}, + {"forehead", 1526}, + {"former", 1527}, + {"fright", 1528}, + {"fully", 1529}, + {"gas", 1530}, + {"hook", 1531}, + {"hurl", 1532}, + {"invite", 1533}, + {"juice", 1534}, + {"manage", 1535}, + {"moral", 1536}, + {"possess", 1537}, + {"raw", 1538}, + {"rebel", 1539}, + {"royal", 1540}, + {"scale", 1541}, + {"scary", 1542}, + {"several", 1543}, + {"slight", 1544}, + {"stubborn", 1545}, + {"swell", 1546}, + {"talent", 1547}, + {"tea", 1548}, + {"terrible", 1549}, + {"thread", 1550}, + {"torment", 1551}, + {"trickle", 1552}, + {"usually", 1553}, + {"vast", 1554}, + {"violence", 1555}, + {"weave", 1556}, + {"acid", 1557}, + {"agony", 1558}, + {"ashamed", 1559}, + {"awe", 1560}, + {"belly", 1561}, + {"blend", 1562}, + {"blush", 1563}, + {"character", 1564}, + {"cheat", 1565}, + {"common", 1566}, + {"company", 1567}, + {"coward", 1568}, + {"creak", 1569}, + {"danger", 1570}, + {"deadly", 1571}, + {"defense", 1572}, + {"define", 1573}, + {"depend", 1574}, + {"desperate", 1575}, + {"destination", 1576}, + {"dew", 1577}, + {"duck", 1578}, + {"dusty", 1579}, + {"embarrass", 1580}, + {"engine", 1581}, + {"example", 1582}, + {"explore", 1583}, + {"foe", 1584}, + {"freely", 1585}, + {"frustrate", 1586}, + {"generation", 1587}, + {"glove", 1588}, + {"guilty", 1589}, + {"health", 1590}, + {"hurry", 1591}, + {"idiot", 1592}, + {"impossible", 1593}, + {"inhale", 1594}, + {"jaw", 1595}, + {"kingdom", 1596}, + {"mention", 1597}, + {"mist", 1598}, + {"moan", 1599}, + {"mumble", 1600}, + {"mutter", 1601}, + {"observe", 1602}, + {"ode", 1603}, + {"pathetic", 1604}, + {"pattern", 1605}, + {"pie", 1606}, + {"prefer", 1607}, + {"puff", 1608}, + {"rape", 1609}, + {"rare", 1610}, + {"revenge", 1611}, + {"rude", 1612}, + {"scrape", 1613}, + {"spiral", 1614}, + {"squeeze", 1615}, + {"strain", 1616}, + {"sunset", 1617}, + {"suspend", 1618}, + {"sympathy", 1619}, + {"thigh", 1620}, + {"throne", 1621}, + {"total", 1622}, + {"unseen", 1623}, + {"weapon", 1624}, + {"weary", 1625} + }; + + const std::string wordsArray[] = { + "like", + "just", + "love", + "know", + "never", + "want", + "time", + "out", + "there", + "make", + "look", + "eye", + "down", + "only", + "think", + "heart", + "back", + "then", + "into", + "about", + "more", + "away", + "still", + "them", + "take", + "thing", + "even", + "through", + "long", + "always", + "world", + "too", + "friend", + "tell", + "try", + "hand", + "thought", + "over", + "here", + "other", + "need", + "smile", + "again", + "much", + "cry", + "been", + "night", + "ever", + "little", + "said", + "end", + "some", + "those", + "around", + "mind", + "people", + "girl", + "leave", + "dream", + "left", + "turn", + "myself", + "give", + "nothing", + "really", + "off", + "before", + "something", + "find", + "walk", + "wish", + "good", + "once", + "place", + "ask", + "stop", + "keep", + "watch", + "seem", + "everything", + "wait", + "got", + "yet", + "made", + "remember", + "start", + "alone", + "run", + "hope", + "maybe", + "believe", + "body", + "hate", + "after", + "close", + "talk", + "stand", + "own", + "each", + "hurt", + "help", + "home", + "god", + "soul", + "new", + "many", + "two", + "inside", + "should", + "true", + "first", + "fear", + "mean", + "better", + "play", + "another", + "gone", + "change", + "use", + "wonder", + "someone", + "hair", + "cold", + "open", + "best", + "any", + "behind", + "happen", + "water", + "dark", + "laugh", + "stay", + "forever", + "name", + "work", + "show", + "sky", + "break", + "came", + "deep", + "door", + "put", + "black", + "together", + "upon", + "happy", + "such", + "great", + "white", + "matter", + "fill", + "past", + "please", + "burn", + "cause", + "enough", + "touch", + "moment", + "soon", + "voice", + "scream", + "anything", + "stare", + "sound", + "red", + "everyone", + "hide", + "kiss", + "truth", + "death", + "beautiful", + "mine", + "blood", + "broken", + "very", + "pass", + "next", + "forget", + "tree", + "wrong", + "air", + "mother", + "understand", + "lip", + "hit", + "wall", + "memory", + "sleep", + "free", + "high", + "realize", + "school", + "might", + "skin", + "sweet", + "perfect", + "blue", + "kill", + "breath", + "dance", + "against", + "fly", + "between", + "grow", + "strong", + "under", + "listen", + "bring", + "sometimes", + "speak", + "pull", + "person", + "become", + "family", + "begin", + "ground", + "real", + "small", + "father", + "sure", + "feet", + "rest", + "young", + "finally", + "land", + "across", + "today", + "different", + "guy", + "line", + "fire", + "reason", + "reach", + "second", + "slowly", + "write", + "eat", + "smell", + "mouth", + "step", + "learn", + "three", + "floor", + "promise", + "breathe", + "darkness", + "push", + "earth", + "guess", + "save", + "song", + "above", + "along", + "both", + "color", + "house", + "almost", + "sorry", + "anymore", + "brother", + "okay", + "dear", + "game", + "fade", + "already", + "apart", + "warm", + "beauty", + "heard", + "notice", + "question", + "shine", + "began", + "piece", + "whole", + "shadow", + "secret", + "street", + "within", + "finger", + "point", + "morning", + "whisper", + "child", + "moon", + "green", + "story", + "glass", + "kid", + "silence", + "since", + "soft", + "yourself", + "empty", + "shall", + "angel", + "answer", + "baby", + "bright", + "dad", + "path", + "worry", + "hour", + "drop", + "follow", + "power", + "war", + "half", + "flow", + "heaven", + "act", + "chance", + "fact", + "least", + "tired", + "children", + "near", + "quite", + "afraid", + "rise", + "sea", + "taste", + "window", + "cover", + "nice", + "trust", + "lot", + "sad", + "cool", + "force", + "peace", + "return", + "blind", + "easy", + "ready", + "roll", + "rose", + "drive", + "held", + "music", + "beneath", + "hang", + "mom", + "paint", + "emotion", + "quiet", + "clear", + "cloud", + "few", + "pretty", + "bird", + "outside", + "paper", + "picture", + "front", + "rock", + "simple", + "anyone", + "meant", + "reality", + "road", + "sense", + "waste", + "bit", + "leaf", + "thank", + "happiness", + "meet", + "men", + "smoke", + "truly", + "decide", + "self", + "age", + "book", + "form", + "alive", + "carry", + "escape", + "damn", + "instead", + "able", + "ice", + "minute", + "throw", + "catch", + "leg", + "ring", + "course", + "goodbye", + "lead", + "poem", + "sick", + "corner", + "desire", + "known", + "problem", + "remind", + "shoulder", + "suppose", + "toward", + "wave", + "drink", + "jump", + "woman", + "pretend", + "sister", + "week", + "human", + "joy", + "crack", + "grey", + "pray", + "surprise", + "dry", + "knee", + "less", + "search", + "bleed", + "caught", + "clean", + "embrace", + "future", + "king", + "son", + "sorrow", + "chest", + "hug", + "remain", + "sat", + "worth", + "blow", + "daddy", + "final", + "parent", + "tight", + "also", + "create", + "lonely", + "safe", + "cross", + "dress", + "evil", + "silent", + "bone", + "fate", + "perhaps", + "anger", + "class", + "scar", + "snow", + "tiny", + "tonight", + "continue", + "control", + "dog", + "edge", + "mirror", + "month", + "suddenly", + "comfort", + "given", + "loud", + "quickly", + "gaze", + "plan", + "rush", + "stone", + "town", + "battle", + "ignore", + "spirit", + "stood", + "stupid", + "yours", + "brown", + "build", + "dust", + "hey", + "kept", + "pay", + "phone", + "twist", + "although", + "ball", + "beyond", + "hidden", + "nose", + "taken", + "fail", + "float", + "pure", + "somehow", + "wash", + "wrap", + "angry", + "cheek", + "creature", + "forgotten", + "heat", + "rip", + "single", + "space", + "special", + "weak", + "whatever", + "yell", + "anyway", + "blame", + "job", + "choose", + "country", + "curse", + "drift", + "echo", + "figure", + "grew", + "laughter", + "neck", + "suffer", + "worse", + "yeah", + "disappear", + "foot", + "forward", + "knife", + "mess", + "somewhere", + "stomach", + "storm", + "beg", + "idea", + "lift", + "offer", + "breeze", + "field", + "five", + "often", + "simply", + "stuck", + "win", + "allow", + "confuse", + "enjoy", + "except", + "flower", + "seek", + "strength", + "calm", + "grin", + "gun", + "heavy", + "hill", + "large", + "ocean", + "shoe", + "sigh", + "straight", + "summer", + "tongue", + "accept", + "crazy", + "everyday", + "exist", + "grass", + "mistake", + "sent", + "shut", + "surround", + "table", + "ache", + "brain", + "destroy", + "heal", + "nature", + "shout", + "sign", + "stain", + "choice", + "doubt", + "glance", + "glow", + "mountain", + "queen", + "stranger", + "throat", + "tomorrow", + "city", + "either", + "fish", + "flame", + "rather", + "shape", + "spin", + "spread", + "ash", + "distance", + "finish", + "image", + "imagine", + "important", + "nobody", + "shatter", + "warmth", + "became", + "feed", + "flesh", + "funny", + "lust", + "shirt", + "trouble", + "yellow", + "attention", + "bare", + "bite", + "money", + "protect", + "amaze", + "appear", + "born", + "choke", + "completely", + "daughter", + "fresh", + "friendship", + "gentle", + "probably", + "six", + "deserve", + "expect", + "grab", + "middle", + "nightmare", + "river", + "thousand", + "weight", + "worst", + "wound", + "barely", + "bottle", + "cream", + "regret", + "relationship", + "stick", + "test", + "crush", + "endless", + "fault", + "itself", + "rule", + "spill", + "art", + "circle", + "join", + "kick", + "mask", + "master", + "passion", + "quick", + "raise", + "smooth", + "unless", + "wander", + "actually", + "broke", + "chair", + "deal", + "favorite", + "gift", + "note", + "number", + "sweat", + "box", + "chill", + "clothes", + "lady", + "mark", + "park", + "poor", + "sadness", + "tie", + "animal", + "belong", + "brush", + "consume", + "dawn", + "forest", + "innocent", + "pen", + "pride", + "stream", + "thick", + "clay", + "complete", + "count", + "draw", + "faith", + "press", + "silver", + "struggle", + "surface", + "taught", + "teach", + "wet", + "bless", + "chase", + "climb", + "enter", + "letter", + "melt", + "metal", + "movie", + "stretch", + "swing", + "vision", + "wife", + "beside", + "crash", + "forgot", + "guide", + "haunt", + "joke", + "knock", + "plant", + "pour", + "prove", + "reveal", + "steal", + "stuff", + "trip", + "wood", + "wrist", + "bother", + "bottom", + "crawl", + "crowd", + "fix", + "forgive", + "frown", + "grace", + "loose", + "lucky", + "party", + "release", + "surely", + "survive", + "teacher", + "gently", + "grip", + "speed", + "suicide", + "travel", + "treat", + "vein", + "written", + "cage", + "chain", + "conversation", + "date", + "enemy", + "however", + "interest", + "million", + "page", + "pink", + "proud", + "sway", + "themselves", + "winter", + "church", + "cruel", + "cup", + "demon", + "experience", + "freedom", + "pair", + "pop", + "purpose", + "respect", + "shoot", + "softly", + "state", + "strange", + "bar", + "birth", + "curl", + "dirt", + "excuse", + "lord", + "lovely", + "monster", + "order", + "pack", + "pants", + "pool", + "scene", + "seven", + "shame", + "slide", + "ugly", + "among", + "blade", + "blonde", + "closet", + "creek", + "deny", + "drug", + "eternity", + "gain", + "grade", + "handle", + "key", + "linger", + "pale", + "prepare", + "swallow", + "swim", + "tremble", + "wheel", + "won", + "cast", + "cigarette", + "claim", + "college", + "direction", + "dirty", + "gather", + "ghost", + "hundred", + "loss", + "lung", + "orange", + "present", + "swear", + "swirl", + "twice", + "wild", + "bitter", + "blanket", + "doctor", + "everywhere", + "flash", + "grown", + "knowledge", + "numb", + "pressure", + "radio", + "repeat", + "ruin", + "spend", + "unknown", + "buy", + "clock", + "devil", + "early", + "false", + "fantasy", + "pound", + "precious", + "refuse", + "sheet", + "teeth", + "welcome", + "add", + "ahead", + "block", + "bury", + "caress", + "content", + "depth", + "despite", + "distant", + "marry", + "purple", + "threw", + "whenever", + "bomb", + "dull", + "easily", + "grasp", + "hospital", + "innocence", + "normal", + "receive", + "reply", + "rhyme", + "shade", + "someday", + "sword", + "toe", + "visit", + "asleep", + "bought", + "center", + "consider", + "flat", + "hero", + "history", + "ink", + "insane", + "muscle", + "mystery", + "pocket", + "reflection", + "shove", + "silently", + "smart", + "soldier", + "spot", + "stress", + "train", + "type", + "view", + "whether", + "bus", + "energy", + "explain", + "holy", + "hunger", + "inch", + "magic", + "mix", + "noise", + "nowhere", + "prayer", + "presence", + "shock", + "snap", + "spider", + "study", + "thunder", + "trail", + "admit", + "agree", + "bag", + "bang", + "bound", + "butterfly", + "cute", + "exactly", + "explode", + "familiar", + "fold", + "further", + "pierce", + "reflect", + "scent", + "selfish", + "sharp", + "sink", + "spring", + "stumble", + "universe", + "weep", + "women", + "wonderful", + "action", + "ancient", + "attempt", + "avoid", + "birthday", + "branch", + "chocolate", + "core", + "depress", + "drunk", + "especially", + "focus", + "fruit", + "honest", + "match", + "palm", + "perfectly", + "pillow", + "pity", + "poison", + "roar", + "shift", + "slightly", + "thump", + "truck", + "tune", + "twenty", + "unable", + "wipe", + "wrote", + "coat", + "constant", + "dinner", + "drove", + "egg", + "eternal", + "flight", + "flood", + "frame", + "freak", + "gasp", + "glad", + "hollow", + "motion", + "peer", + "plastic", + "root", + "screen", + "season", + "sting", + "strike", + "team", + "unlike", + "victim", + "volume", + "warn", + "weird", + "attack", + "await", + "awake", + "built", + "charm", + "crave", + "despair", + "fought", + "grant", + "grief", + "horse", + "limit", + "message", + "ripple", + "sanity", + "scatter", + "serve", + "split", + "string", + "trick", + "annoy", + "blur", + "boat", + "brave", + "clearly", + "cling", + "connect", + "fist", + "forth", + "imagination", + "iron", + "jock", + "judge", + "lesson", + "milk", + "misery", + "nail", + "naked", + "ourselves", + "poet", + "possible", + "princess", + "sail", + "size", + "snake", + "society", + "stroke", + "torture", + "toss", + "trace", + "wise", + "bloom", + "bullet", + "cell", + "check", + "cost", + "darling", + "during", + "footstep", + "fragile", + "hallway", + "hardly", + "horizon", + "invisible", + "journey", + "midnight", + "mud", + "nod", + "pause", + "relax", + "shiver", + "sudden", + "value", + "youth", + "abuse", + "admire", + "blink", + "breast", + "bruise", + "constantly", + "couple", + "creep", + "curve", + "difference", + "dumb", + "emptiness", + "gotta", + "honor", + "plain", + "planet", + "recall", + "rub", + "ship", + "slam", + "soar", + "somebody", + "tightly", + "weather", + "adore", + "approach", + "bond", + "bread", + "burst", + "candle", + "coffee", + "cousin", + "crime", + "desert", + "flutter", + "frozen", + "grand", + "heel", + "hello", + "language", + "level", + "movement", + "pleasure", + "powerful", + "random", + "rhythm", + "settle", + "silly", + "slap", + "sort", + "spoken", + "steel", + "threaten", + "tumble", + "upset", + "aside", + "awkward", + "bee", + "blank", + "board", + "button", + "card", + "carefully", + "complain", + "crap", + "deeply", + "discover", + "drag", + "dread", + "effort", + "entire", + "fairy", + "giant", + "gotten", + "greet", + "illusion", + "jeans", + "leap", + "liquid", + "march", + "mend", + "nervous", + "nine", + "replace", + "rope", + "spine", + "stole", + "terror", + "accident", + "apple", + "balance", + "boom", + "childhood", + "collect", + "demand", + "depression", + "eventually", + "faint", + "glare", + "goal", + "group", + "honey", + "kitchen", + "laid", + "limb", + "machine", + "mere", + "mold", + "murder", + "nerve", + "painful", + "poetry", + "prince", + "rabbit", + "shelter", + "shore", + "shower", + "soothe", + "stair", + "steady", + "sunlight", + "tangle", + "tease", + "treasure", + "uncle", + "begun", + "bliss", + "canvas", + "cheer", + "claw", + "clutch", + "commit", + "crimson", + "crystal", + "delight", + "doll", + "existence", + "express", + "fog", + "football", + "gay", + "goose", + "guard", + "hatred", + "illuminate", + "mass", + "math", + "mourn", + "rich", + "rough", + "skip", + "stir", + "student", + "style", + "support", + "thorn", + "tough", + "yard", + "yearn", + "yesterday", + "advice", + "appreciate", + "autumn", + "bank", + "beam", + "bowl", + "capture", + "carve", + "collapse", + "confusion", + "creation", + "dove", + "feather", + "girlfriend", + "glory", + "government", + "harsh", + "hop", + "inner", + "loser", + "moonlight", + "neighbor", + "neither", + "peach", + "pig", + "praise", + "screw", + "shield", + "shimmer", + "sneak", + "stab", + "subject", + "throughout", + "thrown", + "tower", + "twirl", + "wow", + "army", + "arrive", + "bathroom", + "bump", + "cease", + "cookie", + "couch", + "courage", + "dim", + "guilt", + "howl", + "hum", + "husband", + "insult", + "led", + "lunch", + "mock", + "mostly", + "natural", + "nearly", + "needle", + "nerd", + "peaceful", + "perfection", + "pile", + "price", + "remove", + "roam", + "sanctuary", + "serious", + "shiny", + "shook", + "sob", + "stolen", + "tap", + "vain", + "void", + "warrior", + "wrinkle", + "affection", + "apologize", + "blossom", + "bounce", + "bridge", + "cheap", + "crumble", + "decision", + "descend", + "desperately", + "dig", + "dot", + "flip", + "frighten", + "heartbeat", + "huge", + "lazy", + "lick", + "odd", + "opinion", + "process", + "puzzle", + "quietly", + "retreat", + "score", + "sentence", + "separate", + "situation", + "skill", + "soak", + "square", + "stray", + "taint", + "task", + "tide", + "underneath", + "veil", + "whistle", + "anywhere", + "bedroom", + "bid", + "bloody", + "burden", + "careful", + "compare", + "concern", + "curtain", + "decay", + "defeat", + "describe", + "double", + "dreamer", + "driver", + "dwell", + "evening", + "flare", + "flicker", + "grandma", + "guitar", + "harm", + "horrible", + "hungry", + "indeed", + "lace", + "melody", + "monkey", + "nation", + "object", + "obviously", + "rainbow", + "salt", + "scratch", + "shown", + "shy", + "stage", + "stun", + "third", + "tickle", + "useless", + "weakness", + "worship", + "worthless", + "afternoon", + "beard", + "boyfriend", + "bubble", + "busy", + "certain", + "chin", + "concrete", + "desk", + "diamond", + "doom", + "drawn", + "due", + "felicity", + "freeze", + "frost", + "garden", + "glide", + "harmony", + "hopefully", + "hunt", + "jealous", + "lightning", + "mama", + "mercy", + "peel", + "physical", + "position", + "pulse", + "punch", + "quit", + "rant", + "respond", + "salty", + "sane", + "satisfy", + "savior", + "sheep", + "slept", + "social", + "sport", + "tuck", + "utter", + "valley", + "wolf", + "aim", + "alas", + "alter", + "arrow", + "awaken", + "beaten", + "belief", + "brand", + "ceiling", + "cheese", + "clue", + "confidence", + "connection", + "daily", + "disguise", + "eager", + "erase", + "essence", + "everytime", + "expression", + "fan", + "flag", + "flirt", + "foul", + "fur", + "giggle", + "glorious", + "ignorance", + "law", + "lifeless", + "measure", + "mighty", + "muse", + "north", + "opposite", + "paradise", + "patience", + "patient", + "pencil", + "petal", + "plate", + "ponder", + "possibly", + "practice", + "slice", + "spell", + "stock", + "strife", + "strip", + "suffocate", + "suit", + "tender", + "tool", + "trade", + "velvet", + "verse", + "waist", + "witch", + "aunt", + "bench", + "bold", + "cap", + "certainly", + "click", + "companion", + "creator", + "dart", + "delicate", + "determine", + "dish", + "dragon", + "drama", + "drum", + "dude", + "everybody", + "feast", + "forehead", + "former", + "fright", + "fully", + "gas", + "hook", + "hurl", + "invite", + "juice", + "manage", + "moral", + "possess", + "raw", + "rebel", + "royal", + "scale", + "scary", + "several", + "slight", + "stubborn", + "swell", + "talent", + "tea", + "terrible", + "thread", + "torment", + "trickle", + "usually", + "vast", + "violence", + "weave", + "acid", + "agony", + "ashamed", + "awe", + "belly", + "blend", + "blush", + "character", + "cheat", + "common", + "company", + "coward", + "creak", + "danger", + "deadly", + "defense", + "define", + "depend", + "desperate", + "destination", + "dew", + "duck", + "dusty", + "embarrass", + "engine", + "example", + "explore", + "foe", + "freely", + "frustrate", + "generation", + "glove", + "guilty", + "health", + "hurry", + "idiot", + "impossible", + "inhale", + "jaw", + "kingdom", + "mention", + "mist", + "moan", + "mumble", + "mutter", + "observe", + "ode", + "pathetic", + "pattern", + "pie", + "prefer", + "puff", + "rape", + "rare", + "revenge", + "rude", + "scrape", + "spiral", + "squeeze", + "strain", + "sunset", + "suspend", + "sympathy", + "thigh", + "throne", + "total", + "unseen", + "weapon", + "weary" + }; + } +} diff --git a/src/crypto/oaes_config.h b/src/crypto/oaes_config.h index 8a58c3cfc..3fc0e1be5 100644 --- a/src/crypto/oaes_config.h +++ b/src/crypto/oaes_config.h @@ -39,9 +39,9 @@ extern "C" { //#define OAES_HAVE_ISAAC 1 //#endif // OAES_HAVE_ISAAC -#ifndef OAES_DEBUG -#define OAES_DEBUG 0 -#endif // OAES_DEBUG +//#ifndef OAES_DEBUG +//#define OAES_DEBUG 0 +//#endif // OAES_DEBUG #ifdef __cplusplus } diff --git a/src/crypto/oaes_lib.c b/src/crypto/oaes_lib.c index 126f0a2f6..f3f2aac8c 100644 --- a/src/crypto/oaes_lib.c +++ b/src/crypto/oaes_lib.c @@ -64,31 +64,6 @@ static const char _NR[] = { # define min(a,b) (((a)<(b)) ? (a) : (b)) #endif /* min */ -typedef struct _oaes_key -{ - size_t data_len; - uint8_t *data; - size_t exp_data_len; - uint8_t *exp_data; - size_t num_keys; - size_t key_base; -} oaes_key; - -typedef struct _oaes_ctx -{ -#ifdef OAES_HAVE_ISAAC - randctx * rctx; -#endif // OAES_HAVE_ISAAC - -#ifdef OAES_DEBUG - oaes_step_cb step_cb; -#endif // OAES_DEBUG - - oaes_key * key; - OAES_OPTION options; - uint8_t iv[OAES_BLOCK_SIZE]; -} oaes_ctx; - // "OAES<8-bit header version><8-bit type><16-bit options><8-bit flags><56-bit reserved>" static uint8_t oaes_header[OAES_BLOCK_SIZE] = { // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, diff --git a/src/crypto/oaes_lib.h b/src/crypto/oaes_lib.h index 9155bce02..fd1942822 100644 --- a/src/crypto/oaes_lib.h +++ b/src/crypto/oaes_lib.h @@ -32,6 +32,7 @@ #define _OAES_LIB_H #include <stdint.h> +#include <stdlib.h> #ifdef __cplusplus extern "C" { @@ -101,6 +102,30 @@ typedef int ( * oaes_step_cb ) ( typedef uint16_t OAES_OPTION; +typedef struct _oaes_key +{ + size_t data_len; + uint8_t *data; + size_t exp_data_len; + uint8_t *exp_data; + size_t num_keys; + size_t key_base; +} oaes_key; + +typedef struct _oaes_ctx +{ +#ifdef OAES_HAVE_ISAAC + randctx * rctx; +#endif // OAES_HAVE_ISAAC + +#ifdef OAES_DEBUG + oaes_step_cb step_cb; +#endif // OAES_DEBUG + + oaes_key * key; + OAES_OPTION options; + uint8_t iv[OAES_BLOCK_SIZE]; +} oaes_ctx; /* * // usage: * diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index c7264bd96..35438dcab 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -11,143 +11,251 @@ #include "hash-ops.h" #include "oaes_lib.h" -static void (*const extra_hashes[4])(const void *, size_t, char *) = { - hash_extra_blake, hash_extra_groestl, hash_extra_jh, hash_extra_skein -}; +#include <emmintrin.h> + +#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 +#else +#include <wmmintrin.h> +#define STATIC static +#define INLINE inline +#if !defined(RDATA_ALIGN16) +#define RDATA_ALIGN16 __attribute__ ((aligned(16))) +#endif +#endif -#define MEMORY (1 << 21) /* 2 MiB */ +#define MEMORY (1 << 21) // 2MB scratchpad #define ITER (1 << 20) #define AES_BLOCK_SIZE 16 -#define AES_KEY_SIZE 32 /*16*/ +#define AES_KEY_SIZE 32 #define INIT_SIZE_BLK 8 #define INIT_SIZE_BYTE (INIT_SIZE_BLK * AES_BLOCK_SIZE) -static size_t e2i(const uint8_t* a, size_t count) { return (*((uint64_t*)a) / AES_BLOCK_SIZE) & (count - 1); } +#define U64(x) ((uint64_t *) (x)) +#define R128(x) ((__m128i *) (x)) -static void mul(const uint8_t* a, const uint8_t* b, uint8_t* res) { - uint64_t a0, b0; - uint64_t hi, lo; +extern int aesb_single_round(const uint8_t *in, uint8_t*out, const uint8_t *expandedKey); +extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey); - a0 = SWAP64LE(((uint64_t*)a)[0]); - b0 = SWAP64LE(((uint64_t*)b)[0]); - lo = mul128(a0, b0, &hi); - ((uint64_t*)res)[0] = SWAP64LE(hi); - ((uint64_t*)res)[1] = SWAP64LE(lo); +#pragma pack(push, 1) +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) + ); } +#endif + +STATIC INLINE void mul(const uint8_t *a, const uint8_t *b, uint8_t *res) +{ + uint64_t a0, b0; + uint64_t hi, lo; -static void sum_half_blocks(uint8_t* a, const uint8_t* b) { - uint64_t a0, a1, b0, b1; - - a0 = SWAP64LE(((uint64_t*)a)[0]); - a1 = SWAP64LE(((uint64_t*)a)[1]); - b0 = SWAP64LE(((uint64_t*)b)[0]); - b1 = SWAP64LE(((uint64_t*)b)[1]); - a0 += b0; - a1 += b1; - ((uint64_t*)a)[0] = SWAP64LE(a0); - ((uint64_t*)a)[1] = SWAP64LE(a1); + a0 = U64(a)[0]; + b0 = U64(b)[0]; + lo = mul128(a0, b0, &hi); + U64(res)[0] = hi; + U64(res)[1] = lo; } -static void copy_block(uint8_t* dst, const uint8_t* src) { - memcpy(dst, src, AES_BLOCK_SIZE); +STATIC INLINE void sum_half_blocks(uint8_t *a, const uint8_t *b) +{ + uint64_t a0, a1, b0, b1; + a0 = U64(a)[0]; + a1 = U64(a)[1]; + b0 = U64(b)[0]; + b1 = U64(b)[1]; + a0 += b0; + a1 += b1; + U64(a)[0] = a0; + U64(a)[1] = a1; } -static void swap_blocks(uint8_t* a, uint8_t* b) { - size_t i; - uint8_t t; - for (i = 0; i < AES_BLOCK_SIZE; i++) { - t = a[i]; - a[i] = b[i]; - b[i] = t; - } +STATIC INLINE void swap_blocks(uint8_t *a, uint8_t *b) +{ + uint64_t t[2]; + U64(t)[0] = U64(a)[0]; + U64(t)[1] = U64(a)[1]; + U64(a)[0] = U64(b)[0]; + U64(a)[1] = U64(b)[1]; + U64(b)[0] = U64(t)[0]; + U64(b)[1] = U64(t)[1]; } -static void xor_blocks(uint8_t* a, const uint8_t* b) { - size_t i; - for (i = 0; i < AES_BLOCK_SIZE; i++) { - a[i] ^= b[i]; - } +STATIC INLINE void xor_blocks(uint8_t *a, const uint8_t *b) +{ + U64(a)[0] ^= U64(b)[0]; + U64(a)[1] ^= U64(b)[1]; } -#pragma pack(push, 1) -union cn_slow_hash_state { - union hash_state hs; - struct { - uint8_t k[64]; - uint8_t init[INIT_SIZE_BYTE]; - }; -}; -#pragma pack(pop) +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]; + uint8_t aes_key[AES_KEY_SIZE]; + RDATA_ALIGN16 uint8_t expandedKey[256]; + + union cn_slow_hash_state state; -void cn_slow_hash(const void *data, size_t length, char *hash) { - uint8_t long_state[MEMORY]; - union cn_slow_hash_state state; - uint8_t text[INIT_SIZE_BYTE]; - uint8_t a[AES_BLOCK_SIZE]; - uint8_t b[AES_BLOCK_SIZE]; - uint8_t c[AES_BLOCK_SIZE]; - uint8_t d[AES_BLOCK_SIZE]; - size_t i, j; - uint8_t aes_key[AES_KEY_SIZE]; - OAES_CTX* aes_ctx; - - hash_process(&state.hs, data, length); - memcpy(text, state.init, INIT_SIZE_BYTE); - memcpy(aes_key, state.hs.b, AES_KEY_SIZE); - aes_ctx = oaes_alloc(); - for (i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) { - for (j = 0; j < INIT_SIZE_BLK; j++) { - oaes_key_import_data(aes_ctx, aes_key, AES_KEY_SIZE); - oaes_pseudo_encrypt_ecb(aes_ctx, &text[AES_BLOCK_SIZE * j]); - /*memcpy(aes_key, &text[AES_BLOCK_SIZE * j], AES_KEY_SIZE);*/ - memcpy(aes_key, state.hs.b, AES_KEY_SIZE); + 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); + } + } } - memcpy(&long_state[i * INIT_SIZE_BYTE], text, INIT_SIZE_BYTE); - } - - for (i = 0; i < 16; i++) { - a[i] = state.k[ i] ^ state.k[32 + i]; - b[i] = state.k[16 + i] ^ state.k[48 + i]; - } - - for (i = 0; i < ITER / 2; i++) { - /* Dependency chain: address -> read value ------+ - * written value <-+ hard function (AES or MUL) <+ - * next address <-+ - */ - /* Iteration 1 */ - j = e2i(a, MEMORY / AES_BLOCK_SIZE); - copy_block(c, &long_state[j * AES_BLOCK_SIZE]); - oaes_encryption_round(a, c); - xor_blocks(b, c); - swap_blocks(b, c); - copy_block(&long_state[j * AES_BLOCK_SIZE], c); - assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE)); - swap_blocks(a, b); - /* Iteration 2 */ - j = e2i(a, MEMORY / AES_BLOCK_SIZE); - copy_block(c, &long_state[j * AES_BLOCK_SIZE]); - mul(a, c, d); - sum_half_blocks(b, d); - swap_blocks(b, c); - xor_blocks(b, c); - copy_block(&long_state[j * AES_BLOCK_SIZE], c); - assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE)); - swap_blocks(a, b); - } - - memcpy(text, state.init, INIT_SIZE_BYTE); - for (i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) { - for (j = 0; j < INIT_SIZE_BLK; j++) { - /*oaes_key_import_data(aes_ctx, &long_state[i * INIT_SIZE_BYTE + j * AES_BLOCK_SIZE], AES_KEY_SIZE);*/ - oaes_key_import_data(aes_ctx, &state.hs.b[32], AES_KEY_SIZE); - xor_blocks(&text[j * AES_BLOCK_SIZE], &long_state[i * INIT_SIZE_BYTE + j * AES_BLOCK_SIZE]); - oaes_pseudo_encrypt_ecb(aes_ctx, &text[j * AES_BLOCK_SIZE]); + 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); + } + } } - } - memcpy(state.init, text, INIT_SIZE_BYTE); - hash_permutation(&state.hs); - /*memcpy(hash, &state, 32);*/ - extra_hashes[state.hs.b[0] & 3](&state, 200, hash); - oaes_free(&aes_ctx); + + 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); } diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 34d053aa8..295f59e51 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -8,7 +8,7 @@ #define CRYPTONOTE_MAX_BLOCK_SIZE 500000000 // block header blob limit, never used! #define CRYPTONOTE_MAX_TX_SIZE 1000000000 #define CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER 0 -#define CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX 18 // addresses start with "2" +#define CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX 18 // addresses start with "4" #define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW 60 #define CURRENT_TRANSACTION_VERSION 1 #define CURRENT_BLOCK_MAJOR_VERSION 1 @@ -26,8 +26,8 @@ #define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE 600 #define CRYPTONOTE_DISPLAY_DECIMAL_POINT 12 // COIN - number of smallest units in one coin -#define COIN ((uint64_t)100000000) // pow(10, 8) -#define DEFAULT_FEE ((uint64_t)1000000) // pow(10, 6) +#define COIN ((uint64_t)1000000000000) // pow(10, 12) +#define DEFAULT_FEE ((uint64_t)5000000000) // 5 * pow(10, 9) #define ORPHANED_BLOCKS_MAX_COUNT 100 @@ -51,6 +51,8 @@ #define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT 200 //by default, blocks count in blocks downloading #define CRYPTONOTE_PROTOCOL_HOP_RELAX_COUNT 3 //value of hop, after which we use only announce of new block +#define CRYPTONOTE_MEMPOOL_TX_LIVETIME 86400 //seconds, one day +#define CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME 604800 //seconds, one week #define P2P_DEFAULT_PORT 18080 #define RPC_DEFAULT_PORT 18081 diff --git a/src/cryptonote_core/account.cpp b/src/cryptonote_core/account.cpp index ba39b9b77..3bedd7404 100644 --- a/src/cryptonote_core/account.cpp +++ b/src/cryptonote_core/account.cpp @@ -10,6 +10,10 @@ #include "account.h" #include "warnings.h" #include "crypto/crypto.h" +extern "C" +{ +#include "crypto/keccak.h" +} #include "cryptonote_core/cryptonote_basic_impl.h" #include "cryptonote_core/cryptonote_format_utils.h" using namespace std; @@ -29,11 +33,17 @@ DISABLE_VS_WARNINGS(4244 4345) m_keys = account_keys(); } //----------------------------------------------------------------- - void account_base::generate() + crypto::secret_key account_base::generate(const crypto::secret_key& recovery_key, bool recover, bool two_random) { - generate_keys(m_keys.m_account_address.m_spend_public_key, m_keys.m_spend_secret_key); - generate_keys(m_keys.m_account_address.m_view_public_key, m_keys.m_view_secret_key); + crypto::secret_key first = generate_keys(m_keys.m_account_address.m_spend_public_key, m_keys.m_spend_secret_key, recovery_key, recover); + + // rng for generating second set of keys is hash of first rng. means only one set of electrum-style words needed for recovery + crypto::secret_key second; + keccak((uint8_t *)&first, sizeof(crypto::secret_key), (uint8_t *)&second, sizeof(crypto::secret_key)); + + generate_keys(m_keys.m_account_address.m_view_public_key, m_keys.m_view_secret_key, second, two_random ? false : true); m_creation_timestamp = time(NULL); + return first; } //----------------------------------------------------------------- const account_keys& account_base::get_keys() const diff --git a/src/cryptonote_core/account.h b/src/cryptonote_core/account.h index 8b525da97..cb77d7c4e 100644 --- a/src/cryptonote_core/account.h +++ b/src/cryptonote_core/account.h @@ -31,7 +31,7 @@ namespace cryptonote { public: account_base(); - void generate(); + crypto::secret_key generate(const crypto::secret_key& recovery_key = crypto::secret_key(), bool recover = false, bool two_random = false); const account_keys& get_keys() const; std::string get_public_address_str(); diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index 0e20b454b..9e9c77e85 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -372,7 +372,7 @@ bool blockchain_storage::rollback_blockchain_switching(std::list<block>& origina return true; } //------------------------------------------------------------------ -bool blockchain_storage::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_chain) +bool blockchain_storage::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_chain, bool discard_disconnected_chain) { CRITICAL_REGION_LOCAL(m_blockchain_lock); CHECK_AND_ASSERT_MES(alt_chain.size(), false, "switch_to_alternative_blockchain: empty chain passed"); @@ -414,16 +414,19 @@ bool blockchain_storage::switch_to_alternative_blockchain(std::list<blocks_ext_b } } - //pushing old chain as alternative chain - BOOST_FOREACH(auto& old_ch_ent, disconnected_chain) + if(!discard_disconnected_chain) { - block_verification_context bvc = boost::value_initialized<block_verification_context>(); - bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc); - if(!r) + //pushing old chain as alternative chain + BOOST_FOREACH(auto& old_ch_ent, disconnected_chain) { - LOG_ERROR("Failed to push ex-main chain blocks to alternative chain "); - rollback_blockchain_switching(disconnected_chain, split_height); - return false; + block_verification_context bvc = boost::value_initialized<block_verification_context>(); + bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc); + if(!r) + { + LOG_ERROR("Failed to push ex-main chain blocks to alternative chain "); + rollback_blockchain_switching(disconnected_chain, split_height); + return false; + } } } @@ -701,6 +704,22 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: { CRITICAL_REGION_LOCAL(m_blockchain_lock); + uint64_t block_height = get_block_height(b); + if(0 == block_height) + { + LOG_ERROR("Block with id: " << string_tools::pod_to_hex(id) << " (as alternative) have wrong miner transaction"); + bvc.m_verifivation_failed = true; + return false; + } + if (!m_checkpoints.is_alternative_block_allowed(get_current_blockchain_height(), block_height)) + { + LOG_PRINT_RED_L0("Block with id: " << id + << ENDL << " can't be accepted for alternative chain, block height: " << block_height + << ENDL << " blockchain height: " << get_current_blockchain_height()); + bvc.m_verifivation_failed = true; + return false; + } + //block is not related with head of main chain //first of all - look in alternative chains container auto it_main_prev = m_blocks_index.find(b.prev_id); @@ -746,31 +765,28 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: block_extended_info bei = boost::value_initialized<block_extended_info>(); bei.bl = b; bei.height = alt_chain.size() ? it_prev->second.height + 1 : it_main_prev->second + 1; + + bool is_a_checkpoint; + if(!m_checkpoints.check_block(bei.height, id, is_a_checkpoint)) + { + LOG_ERROR("CHECKPOINT VALIDATION FAILED"); + bvc.m_verifivation_failed = true; + return false; + } + + // Always check PoW for alternative blocks + m_is_in_checkpoint_zone = false; 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; - if(!m_checkpoints.is_in_checkpoint_zone(bei.height)) + get_block_longhash(bei.bl, proof_of_work, bei.height); + if(!check_hash(proof_of_work, current_diff)) { - m_is_in_checkpoint_zone = false; - get_block_longhash(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 - << ENDL << " expected difficulty: " << current_diff); - bvc.m_verifivation_failed = true; - return false; - } - }else - { - m_is_in_checkpoint_zone = true; - if(!m_checkpoints.check_block(bei.height, id)) - { - LOG_ERROR("CHECKPOINT VALIDATION FAILED"); - bvc.m_verifivation_failed = true; - return false; - } + LOG_PRINT_RED_L0("Block with id: " << id + << ENDL << " for alternative chain, have not enough proof of work: " << proof_of_work + << ENDL << " expected difficulty: " << current_diff); + bvc.m_verifivation_failed = true; + return false; } if(!prevalidate_miner_transaction(b, bei.height)) @@ -792,22 +808,33 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: auto i_res = m_alternative_chains.insert(blocks_ext_by_hash::value_type(id, bei)); CHECK_AND_ASSERT_MES(i_res.second, false, "insertion of new alternative block returned as it already exist"); alt_chain.push_back(i_res.first); - //check if difficulty bigger then in main chain - if(m_blocks.back().cumulative_difficulty < bei.cumulative_difficulty) + + if(is_a_checkpoint) { //do reorganize! - LOG_PRINT_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_blocks.size() -1 << " with cum_difficulty " << m_blocks.back().cumulative_difficulty + LOG_PRINT_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_blocks.size() - 1 << + ", checkpoint is found in alternative chain on height " << bei.height, LOG_LEVEL_0); + bool r = switch_to_alternative_blockchain(alt_chain, true); + if(r) bvc.m_added_to_main_chain = true; + else bvc.m_verifivation_failed = true; + return r; + }else if(m_blocks.back().cumulative_difficulty < bei.cumulative_difficulty) //check if difficulty bigger then in main chain + { + //do reorganize! + LOG_PRINT_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_blocks.size() - 1 << " with cum_difficulty " << m_blocks.back().cumulative_difficulty << ENDL << " alternative blockchain size: " << alt_chain.size() << " with cum_difficulty " << bei.cumulative_difficulty, LOG_LEVEL_0); - bool r = switch_to_alternative_blockchain(alt_chain); + bool r = switch_to_alternative_blockchain(alt_chain, false); if(r) bvc.m_added_to_main_chain = true; else bvc.m_verifivation_failed = true; return r; + }else + { + LOG_PRINT_BLUE("----- BLOCK ADDED AS ALTERNATIVE ON HEIGHT " << bei.height + << ENDL << "id:\t" << id + << ENDL << "PoW:\t" << proof_of_work + << ENDL << "difficulty:\t" << current_diff, LOG_LEVEL_0); + return true; } - LOG_PRINT_BLUE("----- BLOCK ADDED AS ALTERNATIVE ON HEIGHT " << bei.height - << ENDL << "id:\t" << id - << ENDL << "PoW:\t" << proof_of_work - << ENDL << "difficulty:\t" << current_diff, LOG_LEVEL_0); - return true; }else { //block orphaned @@ -815,7 +842,6 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: LOG_PRINT_RED_L0("Block recognized as orphaned and rejected, id = " << id); } - return true; } //------------------------------------------------------------------ @@ -1480,19 +1506,27 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt TIME_MEASURE_FINISH(target_calculating_time); TIME_MEASURE_START(longhash_calculating_time); crypto::hash proof_of_work = null_hash; - if(!m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height())) + + // Formerly the code below contained an if loop with the following condition + // !m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height()) + // however, this caused the daemon to not bother checking PoW for blocks + // before checkpoints, which is very dangerous behaviour. We moved the PoW + // validation out of the next chunk of code to make sure that we correctly + // check PoW now. + proof_of_work = get_block_longhash(bl, m_blocks.size()); + + if(!check_hash(proof_of_work, current_diffic)) { - proof_of_work = get_block_longhash(bl, m_blocks.size()); + LOG_PRINT_L0("Block with id: " << id << ENDL + << "have not enough proof of work: " << proof_of_work << ENDL + << "nexpected difficulty: " << current_diffic ); + bvc.m_verifivation_failed = true; + return false; + } - if(!check_hash(proof_of_work, current_diffic)) - { - LOG_PRINT_L0("Block with id: " << id << ENDL - << "have not enough proof of work: " << proof_of_work << ENDL - << "nexpected difficulty: " << current_diffic ); - bvc.m_verifivation_failed = true; - return false; - } - }else + // If we're at a checkpoint, ensure that our hardcoded checkpoint hash + // is correct. + if(m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height())) { if(!m_checkpoints.check_block(get_current_blockchain_height(), id)) { @@ -1501,6 +1535,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt return false; } } + TIME_MEASURE_FINISH(longhash_calculating_time); if(!prevalidate_miner_transaction(bl, m_blocks.size())) @@ -1648,4 +1683,4 @@ bool blockchain_storage::add_new_block(const block& bl_, block_verification_cont } return handle_block_to_main_chain(bl, id, bvc); -}
\ No newline at end of file +} diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h index 1ea5e29ea..b1fb5df41 100644 --- a/src/cryptonote_core/blockchain_storage.h +++ b/src/cryptonote_core/blockchain_storage.h @@ -13,6 +13,8 @@ #include <boost/foreach.hpp> #include <atomic> +#include "syncobj.h" +#include "string_tools.h" #include "tx_pool.h" #include "cryptonote_basic.h" #include "common/util.h" @@ -50,7 +52,7 @@ namespace cryptonote uint64_t already_generated_coins; }; - 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) + 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()); } @@ -119,7 +121,7 @@ namespace cryptonote missed_bs.push_back(bl_id); else { - CHECK_AND_ASSERT_MES(it->second < m_blocks.size(), false, "Internal error: bl_id=" << string_tools::pod_to_hex(bl_id) + CHECK_AND_ASSERT_MES(it->second < m_blocks.size(), false, "Internal error: bl_id=" << epee::string_tools::pod_to_hex(bl_id) << " have index record with offset="<<it->second<< ", bigger then m_blocks.size()=" << m_blocks.size()); blocks.push_back(m_blocks[it->second].bl); } @@ -163,7 +165,7 @@ namespace cryptonote typedef std::map<uint64_t, std::vector<std::pair<crypto::hash, size_t>>> outputs_container; //crypto::hash - tx hash, size_t - index of out in transaction tx_memory_pool& m_tx_pool; - critical_section m_blockchain_lock; // TODO: add here reader/writer lock + epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock // main chain blocks_container m_blocks; // height -> block_extended_info @@ -186,7 +188,7 @@ namespace cryptonote std::atomic<bool> m_is_in_checkpoint_zone; std::atomic<bool> m_is_blockchain_storing; - bool switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_chain); + bool switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_chain, bool discard_disconnected_chain); bool pop_block_from_blockchain(); bool purge_block_data_from_blockchain(const block& b, size_t processed_tx_count); bool purge_transaction_from_blockchain(const crypto::hash& tx_id); @@ -301,7 +303,7 @@ namespace cryptonote return false; } transactions_container::iterator tx_it = m_transactions.find(amount_outs_vec[i].first); - CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "Wrong transaction id in output indexes: " <<string_tools::pod_to_hex(amount_outs_vec[i].first)); + CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "Wrong transaction id in output indexes: " << epee::string_tools::pod_to_hex(amount_outs_vec[i].first)); CHECK_AND_ASSERT_MES(amount_outs_vec[i].second < tx_it->second.tx.vout.size(), false, "Wrong index in transaction outputs: " << amount_outs_vec[i].second << ", expected less then " << tx_it->second.tx.vout.size()); if(!vis.handle_output(tx_it->second.tx, tx_it->second.tx.vout[amount_outs_vec[i].second])) diff --git a/src/cryptonote_core/checkpoints.cpp b/src/cryptonote_core/checkpoints.cpp index 54c2f3a6d..33a2d2986 100644 --- a/src/cryptonote_core/checkpoints.cpp +++ b/src/cryptonote_core/checkpoints.cpp @@ -29,10 +29,11 @@ namespace cryptonote return !m_points.empty() && (height <= (--m_points.end())->first); } //--------------------------------------------------------------------------- - bool checkpoints::check_block(uint64_t height, const crypto::hash& h) const + bool checkpoints::check_block(uint64_t height, const crypto::hash& h, bool& is_a_checkpoint) const { auto it = m_points.find(height); - if(it == m_points.end()) + is_a_checkpoint = it != m_points.end(); + if(!is_a_checkpoint) return true; if(it->second == h) @@ -45,4 +46,25 @@ namespace cryptonote return false; } } + //--------------------------------------------------------------------------- + bool checkpoints::check_block(uint64_t height, const crypto::hash& h) const + { + bool ignored; + return check_block(height, h, ignored); + } + //--------------------------------------------------------------------------- + bool checkpoints::is_alternative_block_allowed(uint64_t blockchain_height, uint64_t block_height) const + { + if (0 == block_height) + return false; + + auto it = m_points.upper_bound(blockchain_height); + // Is blockchain_height before the first checkpoint? + if (it == m_points.begin()) + return true; + + --it; + uint64_t checkpoint_height = it->first; + return checkpoint_height < block_height; + } } diff --git a/src/cryptonote_core/checkpoints.h b/src/cryptonote_core/checkpoints.h index 20014b1c8..1bc055d91 100644 --- a/src/cryptonote_core/checkpoints.h +++ b/src/cryptonote_core/checkpoints.h @@ -16,6 +16,9 @@ namespace cryptonote bool add_checkpoint(uint64_t height, const std::string& hash_str); bool is_in_checkpoint_zone(uint64_t height) const; bool check_block(uint64_t height, const crypto::hash& h) const; + bool check_block(uint64_t height, const crypto::hash& h, bool& is_a_checkpoint) const; + bool is_alternative_block_allowed(uint64_t blockchain_height, uint64_t block_height) const; + private: std::map<uint64_t, crypto::hash> m_points; }; diff --git a/src/cryptonote_core/checkpoints_create.h b/src/cryptonote_core/checkpoints_create.h index 9088b092d..a92fefd3f 100644 --- a/src/cryptonote_core/checkpoints_create.h +++ b/src/cryptonote_core/checkpoints_create.h @@ -13,6 +13,9 @@ namespace cryptonote { inline bool create_checkpoints(cryptonote::checkpoints& checkpoints) { ADD_CHECKPOINT(22231, "7cb10e29d67e1c069e6e11b17d30b809724255fee2f6868dc14cfc6ed44dfb25"); + ADD_CHECKPOINT(29556, "53c484a8ed91e4da621bb2fa88106dbde426fe90d7ef07b9c1e5127fb6f3a7f6"); + ADD_CHECKPOINT(50000, "0fe8758ab06a8b9cb35b7328fd4f757af530a5d37759f9d3e421023231f7b31c"); + ADD_CHECKPOINT(80000, "a62dcd7b536f22e003ebae8726e9e7276f63d594e264b6f0cd7aab27b66e75e3"); return true; } } diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index a09f25d31..2609fc13e 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -28,7 +28,8 @@ namespace cryptonote m_blockchain_storage(m_mempool), m_miner(this), m_miner_address(boost::value_initialized<account_public_address>()), - m_starter_message_showed(false) + m_starter_message_showed(false), + m_target_blockchain_height(0) { set_cryptonote_protocol(pprotocol); } @@ -431,6 +432,18 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + // Used by the RPC server to check the size of an incoming + // block_blob + bool core::check_incoming_block_size(const blobdata& block_blob) + { + if(block_blob.size() > get_max_block_size()) + { + LOG_PRINT_L0("WRONG BLOCK BLOB, too big size " << block_blob.size() << ", rejected"); + return false; + } + return true; + } + //----------------------------------------------------------------------------------------------- crypto::hash core::get_tail_id() { return m_blockchain_storage.get_tail_id(); @@ -502,7 +515,7 @@ namespace cryptonote LOG_PRINT_L0(ENDL << "**********************************************************************" << ENDL << "The daemon will start synchronizing with the network. It may take up to several hours." << ENDL << ENDL - << "You can set the level of process detailization by using command \"set_log <level>\", where <level> is either 0 (no details), 1 (current block height synchronized), or 2 (all details)." << ENDL + << "You can set the level of process detailization* through \"set_log <level>\" command*, where <level> is between 0 (no details) and 4 (very verbose)." << ENDL << ENDL << "Use \"help\" command to see the list of available commands." << ENDL << ENDL @@ -513,7 +526,15 @@ namespace cryptonote m_store_blockchain_interval.do_call(boost::bind(&blockchain_storage::store_blockchain, &m_blockchain_storage)); m_miner.on_idle(); + m_mempool.on_idle(); return true; } //----------------------------------------------------------------------------------------------- + void core::set_target_blockchain_height(uint64_t target_blockchain_height) { + m_target_blockchain_height = target_blockchain_height; + } + //----------------------------------------------------------------------------------------------- + uint64_t core::get_target_blockchain_height() const { + return m_target_blockchain_height; + } } diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index c298451e8..1f1b6eec6 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -34,6 +34,7 @@ namespace cryptonote bool on_idle(); bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block); bool handle_incoming_block(const blobdata& block_blob, block_verification_context& bvc, bool update_miner_blocktemplate = true); + bool check_incoming_block_size(const blobdata& block_blob); i_cryptonote_protocol* get_protocol(){return m_pprotocol;} //-------------------- i_miner_handler ----------------------- @@ -90,6 +91,9 @@ namespace cryptonote void print_blockchain_outs(const std::string& file); void on_synchronized(); + void set_target_blockchain_height(uint64_t target_blockchain_height); + uint64_t get_target_blockchain_height() const; + private: bool add_new_tx(const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block); bool add_new_tx(const transaction& tx, tx_verification_context& tvc, bool keeped_by_block); @@ -115,15 +119,17 @@ namespace cryptonote tx_memory_pool m_mempool; blockchain_storage m_blockchain_storage; i_cryptonote_protocol* m_pprotocol; - critical_section m_incoming_tx_lock; + epee::critical_section m_incoming_tx_lock; //m_miner and m_miner_addres are probably temporary here miner m_miner; account_public_address m_miner_address; std::string m_config_folder; cryptonote_protocol_stub m_protocol_stub; - math_helper::once_a_time_seconds<60*60*12, false> m_store_blockchain_interval; + epee::math_helper::once_a_time_seconds<60*60*12, false> m_store_blockchain_interval; friend class tx_validate_inputs; std::atomic<bool> m_starter_message_showed; + + uint64_t m_target_blockchain_height; }; } diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index b2eaf18a2..7b7f18844 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -80,7 +80,7 @@ namespace cryptonote #endif block_reward += fee; - std::vector<size_t> out_amounts; + std::vector<uint64_t> out_amounts; decompose_amount_into_digits(block_reward, DEFAULT_FEE, [&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); }, [&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); }); @@ -92,7 +92,7 @@ namespace cryptonote out_amounts.resize(out_amounts.size() - 1); } - size_t summary_amounts = 0; + uint64_t summary_amounts = 0; for (size_t no = 0; no < out_amounts.size(); no++) { crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);; @@ -239,8 +239,7 @@ namespace cryptonote crypto::public_key get_tx_pub_key_from_extra(const std::vector<uint8_t>& tx_extra) { std::vector<tx_extra_field> tx_extra_fields; - if (!parse_tx_extra(tx_extra, tx_extra_fields)) - return null_pkey; + parse_tx_extra(tx_extra, tx_extra_fields); tx_extra_pub_key pub_key_field; if(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field)) diff --git a/src/cryptonote_core/miner.cpp b/src/cryptonote_core/miner.cpp index 56b459d6e..2055bb15d 100644 --- a/src/cryptonote_core/miner.cpp +++ b/src/cryptonote_core/miner.cpp @@ -188,10 +188,19 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------------- - bool miner::is_mining() + bool miner::is_mining() const { return !m_stop; } + //----------------------------------------------------------------------------------------------------- + const account_public_address& miner::get_mining_address() const + { + return m_mine_address; + } + //----------------------------------------------------------------------------------------------------- + uint32_t miner::get_threads_count() const { + return m_threads_total; + } //----------------------------------------------------------------------------------------------------- bool miner::start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs) { @@ -226,12 +235,14 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------------- - uint64_t miner::get_speed() + uint64_t miner::get_speed() const { - if(is_mining()) + if(is_mining()) { return m_current_hash_rate; - else + } + else { return 0; + } } //----------------------------------------------------------------------------------------------------- void miner::send_stop_signal() diff --git a/src/cryptonote_core/miner.h b/src/cryptonote_core/miner.h index da4578b06..d9ac5a501 100644 --- a/src/cryptonote_core/miner.h +++ b/src/cryptonote_core/miner.h @@ -4,7 +4,6 @@ #pragma once -#include <boost/atomic.hpp> #include <boost/program_options.hpp> #include <atomic> #include "cryptonote_basic.h" @@ -36,10 +35,12 @@ namespace cryptonote bool set_block_template(const block& bl, const difficulty_type& diffic, uint64_t height); bool on_block_chain_update(); bool start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs); - uint64_t get_speed(); + uint64_t get_speed() const; + uint32_t get_threads_count() const; void send_stop_signal(); bool stop(); - bool is_mining(); + bool is_mining() const; + const account_public_address& get_mining_address() const; bool on_idle(); void on_synchronized(); //synchronous analog (for fast calls) @@ -64,7 +65,7 @@ namespace cryptonote volatile uint32_t m_stop; - ::critical_section m_template_lock; + epee::critical_section m_template_lock; block m_template; std::atomic<uint32_t> m_template_no; std::atomic<uint32_t> m_starter_nonce; @@ -73,21 +74,21 @@ namespace cryptonote volatile uint32_t m_thread_index; volatile uint32_t m_threads_total; std::atomic<int32_t> m_pausers_count; - ::critical_section m_miners_count_lock; + epee::critical_section m_miners_count_lock; std::list<boost::thread> m_threads; - ::critical_section m_threads_lock; + epee::critical_section m_threads_lock; i_miner_handler* m_phandler; account_public_address m_mine_address; - math_helper::once_a_time_seconds<5> m_update_block_template_interval; - math_helper::once_a_time_seconds<2> m_update_merge_hr_interval; + epee::math_helper::once_a_time_seconds<5> m_update_block_template_interval; + 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::atomic<uint64_t> m_last_hr_merge_time; std::atomic<uint64_t> m_hashes; std::atomic<uint64_t> m_current_hash_rate; - critical_section m_last_hash_rates_lock; + epee::critical_section m_last_hash_rates_lock; std::list<uint64_t> m_last_hash_rates; bool m_do_print_hashrate; bool m_do_mining; diff --git a/src/cryptonote_core/tx_extra.h b/src/cryptonote_core/tx_extra.h index 8cff085dc..37a04a41e 100644 --- a/src/cryptonote_core/tx_extra.h +++ b/src/cryptonote_core/tx_extra.h @@ -11,6 +11,7 @@ #define TX_EXTRA_TAG_PADDING 0x00 #define TX_EXTRA_TAG_PUBKEY 0x01 #define TX_EXTRA_NONCE 0x02 +#define TX_EXTRA_MERGE_MINING_TAG 0x03 #define TX_EXTRA_NONCE_PAYMENT_ID 0x00 @@ -82,13 +83,62 @@ namespace cryptonote END_SERIALIZE() }; + struct tx_extra_merge_mining_tag + { + struct serialize_helper + { + tx_extra_merge_mining_tag& mm_tag; + + serialize_helper(tx_extra_merge_mining_tag& mm_tag_) : mm_tag(mm_tag_) + { + } + + BEGIN_SERIALIZE() + VARINT_FIELD_N("depth", mm_tag.depth) + FIELD_N("merkle_root", mm_tag.merkle_root) + END_SERIALIZE() + }; + + size_t depth; + crypto::hash merkle_root; + + // load + template <template <bool> class Archive> + bool do_serialize(Archive<false>& ar) + { + std::string field; + if(!::do_serialize(ar, field)) + return false; + + std::istringstream iss(field); + binary_archive<false> iar(iss); + serialize_helper helper(*this); + return ::serialization::serialize(iar, helper); + } + + // store + template <template <bool> class Archive> + bool do_serialize(Archive<true>& ar) + { + std::ostringstream oss; + binary_archive<true> oar(oss); + serialize_helper helper(*this); + if(!::do_serialize(oar, helper)) + return false; + + std::string field = oss.str(); + return ::serialization::serialize(ar, field); + } + }; + // tx_extra_field format, except tx_extra_padding and tx_extra_pub_key: // varint tag; // varint size; // varint data[]; - typedef boost::variant<tx_extra_padding, tx_extra_pub_key, tx_extra_nonce> tx_extra_field; + typedef boost::variant<tx_extra_padding, tx_extra_pub_key, tx_extra_nonce, tx_extra_merge_mining_tag> tx_extra_field; } VARIANT_TAG(binary_archive, cryptonote::tx_extra_padding, TX_EXTRA_TAG_PADDING); VARIANT_TAG(binary_archive, cryptonote::tx_extra_pub_key, TX_EXTRA_TAG_PUBKEY); VARIANT_TAG(binary_archive, cryptonote::tx_extra_nonce, TX_EXTRA_NONCE); +VARIANT_TAG(binary_archive, cryptonote::tx_extra_merge_mining_tag, TX_EXTRA_MERGE_MINING_TAG); diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 24e5752ad..ce1bc1ad2 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -83,6 +83,7 @@ namespace cryptonote txd_p.first->second.max_used_block_id = null_hash; txd_p.first->second.max_used_block_height = 0; txd_p.first->second.kept_by_block = kept_by_block; + txd_p.first->second.receive_time = time(nullptr); tvc.m_verifivation_impossible = true; tvc.m_added_to_pool = true; }else @@ -104,6 +105,7 @@ namespace cryptonote txd_p.first->second.max_used_block_height = max_used_block_height; txd_p.first->second.last_failed_height = 0; txd_p.first->second.last_failed_id = null_hash; + txd_p.first->second.receive_time = time(nullptr); tvc.m_added_to_pool = true; if(txd_p.first->second.fee > 0) @@ -178,6 +180,30 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- + void tx_memory_pool::on_idle() + { + m_remove_stuck_tx_interval.do_call([this](){return remove_stuck_transactions();}); + } + //--------------------------------------------------------------------------------- + //proper tx_pool handling courtesy of CryptoZoidberg and Boolberry + bool tx_memory_pool::remove_stuck_transactions() + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + for(auto it = m_transactions.begin(); it!= m_transactions.end();) + { + uint64_t tx_age = time(nullptr) - it->second.receive_time; + + if((tx_age > CRYPTONOTE_MEMPOOL_TX_LIVETIME && !it->second.kept_by_block) || + (tx_age > CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME && it->second.kept_by_block) ) + { + LOG_PRINT_L0("Tx " << it->first << " removed from tx pool due to outdated, age: " << tx_age ); + m_transactions.erase(it++); + }else + ++it; + } + return true; + } + //--------------------------------------------------------------------------------- size_t tx_memory_pool::get_transactions_count() { CRITICAL_REGION_LOCAL(m_transactions_lock); @@ -350,18 +376,69 @@ namespace cryptonote //--------------------------------------------------------------------------------- bool tx_memory_pool::fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee) { + // Warning: This function takes already_generated_ + // coins as an argument and appears to do nothing + // with it. + CRITICAL_REGION_LOCAL(m_transactions_lock); total_size = 0; fee = 0; - size_t max_total_size = 2 * median_size - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + size_t max_total_size = 2 * median_size - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; // Max block size std::unordered_set<crypto::key_image> k_images; + + // Tx size limit as in wallet2.h + // tx_pool.cpp uses size_t for tx sizes, whereas + // wallet2.h uses uint64_t; just use size_t here + // for now + size_t upper_transaction_size_limit = ((CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + + // Calculate size limit based on median too; useful + // for when we actually fix wallet2.h's maximum + // allowable tx size + // + // Can be removed when wallet2.h calculates max + // tx size based on the median too; just use + // upper_transaction_size_limit_median in all cases + size_t upper_transaction_size_limit_median = ((median_size * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + if (upper_transaction_size_limit_median > upper_transaction_size_limit) + upper_transaction_size_limit = upper_transaction_size_limit_median; + BOOST_FOREACH(transactions_container::value_type& tx, m_transactions) { + // Can not exceed maximum block size if (max_total_size < total_size + tx.second.blob_size) continue; + // Check to see if the minimum fee is included; + // exclude tx missing minimum fee + if (tx.second.fee < DEFAULT_FEE) + continue; + + // Skip transactions that are too large + // TODO: Correct upper_transactions_size_limit + // such that it is based on median block size; + // We need to make a similar patch for + // wallet2.h + if (tx.second.blob_size > upper_transaction_size_limit) + continue; + + // If adding this tx will make the block size + // greater than 130% of the median, reject the + // tx; this will keep down largely punitive tx + // from being included + if ( (total_size + tx.second.blob_size) > ((130 * median_size) / 100) ) + continue; + + // If we've exceeded the penalty free size, + // stop including more tx + if (total_size > median_size) + break; + + // Skip transactions that are not ready to be + // included into the blockchain or that are + // missing key images if (!is_transaction_ready_to_go(tx.second) || have_key_images(k_images, tx.second.tx)) continue; diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 3978dfb96..649af41a3 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -4,8 +4,6 @@ #pragma once #include "include_base_utils.h" -using namespace epee; - #include <set> #include <unordered_map> @@ -15,6 +13,7 @@ using namespace epee; #include "string_tools.h" #include "syncobj.h" +#include "math_helper.h" #include "cryptonote_basic_impl.h" #include "verification_context.h" #include "crypto/hash.h" @@ -42,6 +41,7 @@ namespace cryptonote bool on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id); bool on_blockchain_dec(uint64_t new_block_height, const crypto::hash& top_block_id); + void on_idle(); void lock(); void unlock(); @@ -61,7 +61,7 @@ namespace cryptonote /*bool flush_pool(const std::strig& folder); bool inflate_pool(const std::strig& folder);*/ -#define CURRENT_MEMPOOL_ARCHIVE_VER 7 +#define CURRENT_MEMPOOL_ARCHIVE_VER 8 template<class archive_t> void serialize(archive_t & a, const unsigned int version) @@ -84,9 +84,11 @@ namespace cryptonote // uint64_t last_failed_height; crypto::hash last_failed_id; + time_t receive_time; }; private: + bool remove_stuck_transactions(); bool is_transaction_ready_to_go(tx_details& txd); typedef std::unordered_map<crypto::hash, tx_details > transactions_container; typedef std::unordered_map<crypto::key_image, std::unordered_set<crypto::hash> > key_images_container; @@ -94,6 +96,7 @@ namespace cryptonote epee::critical_section m_transactions_lock; transactions_container m_transactions; key_images_container m_spent_key_images; + epee::math_helper::once_a_time_seconds<30> m_remove_stuck_tx_interval; //transactions_container m_alternative_transactions; @@ -161,6 +164,7 @@ namespace boost ar & td.max_used_block_id; ar & td.last_failed_height; ar & td.last_failed_id; + ar & td.receive_time; } } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index 178ec2eb1..80538677c 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -81,7 +81,7 @@ namespace cryptonote template<class t_parametr> bool post_notify(typename t_parametr::request& arg, cryptonote_connection_context& context) { - LOG_PRINT_L2("[" << net_utils::print_connection_context_short(context) << "] post " << typeid(t_parametr).name() << " -->"); + LOG_PRINT_L2("[" << epee::net_utils::print_connection_context_short(context) << "] post " << typeid(t_parametr).name() << " -->"); std::string blob; epee::serialization::store_t_to_binary(arg, blob); return m_p2p->invoke_notify_to_peer(t_parametr::ID, blob, context); @@ -90,7 +90,7 @@ namespace cryptonote template<class t_parametr> bool relay_post_notify(typename t_parametr::request& arg, cryptonote_connection_context& exlude_context) { - LOG_PRINT_L2("[" << net_utils::print_connection_context_short(exlude_context) << "] post relay " << typeid(t_parametr).name() << " -->"); + LOG_PRINT_L2("[" << epee::net_utils::print_connection_context_short(exlude_context) << "] post relay " << typeid(t_parametr).name() << " -->"); std::string arg_buff; epee::serialization::store_t_to_binary(arg, arg_buff); return m_p2p->relay_notify_to_all(t_parametr::ID, arg_buff, exlude_context); diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index a0838e690..a2f0bb3ad 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -81,7 +81,7 @@ namespace cryptonote m_p2p->for_each_connection([&](const connection_context& cntxt, nodetool::peerid_type peer_id) { ss << std::setw(25) << std::left << std::string(cntxt.m_is_income ? " [INC]":"[OUT]") + - string_tools::get_ip_string_from_int32(cntxt.m_remote_ip) + ":" + std::to_string(cntxt.m_remote_port) + epee::string_tools::get_ip_string_from_int32(cntxt.m_remote_ip) + ":" + std::to_string(cntxt.m_remote_port) << std::setw(20) << std::hex << peer_id << std::setw(25) << std::to_string(cntxt.m_recv_cnt)+ "(" + std::to_string(time(NULL) - cntxt.m_last_recv) + ")" + "/" + std::to_string(cntxt.m_send_cnt) + "(" + std::to_string(time(NULL) - cntxt.m_last_send) + ")" << std::setw(25) << get_protocol_state_string(cntxt.m_state) @@ -108,9 +108,17 @@ namespace cryptonote return true; } - LOG_PRINT_CCONTEXT_YELLOW("Sync data returned unknown top block: " << m_core.get_current_blockchain_height() << "->" << hshd.current_height - << "[" << static_cast<int64_t>(hshd.current_height - m_core.get_current_blockchain_height()) << " blocks(" << (hshd.current_height - m_core.get_current_blockchain_height()) /1440 << " days) behind] " << ENDL - << "remote top: " << hshd.top_id << "[" << hshd.current_height << "]" << ", set SYNCHRONIZATION mode", (is_inital ? LOG_LEVEL_0:LOG_LEVEL_1)); + /* As I don't know if accessing hshd from core could be a good practice, + I prefer pushing target height to the core at the same time it is pushed to the user. + Nz. */ + m_core.set_target_blockchain_height(static_cast<int64_t>(hshd.current_height)); + + int64_t diff = static_cast<int64_t>(hshd.current_height) - static_cast<int64_t>(m_core.get_current_blockchain_height()); + LOG_PRINT_CCONTEXT_YELLOW("Sync data returned unknown top block: " << m_core.get_current_blockchain_height() << " -> " << hshd.current_height + << " [" << std::abs(diff) << " blocks (" << diff / (24 * 60 * 60 / DIFFICULTY_TARGET) << " days) " + << (0 <= diff ? std::string("behind") : std::string("ahead")) + << "] " << ENDL << "SYNCHRONIZATION started", (is_inital ? LOG_LEVEL_0:LOG_LEVEL_1)); + LOG_PRINT_L1("Remote top block height: " << hshd.current_height << ", id: " << hshd.top_id); context.m_state = cryptonote_connection_context::state_synchronizing; context.m_remote_blockchain_height = hshd.current_height; //let the socket to send response to handshake, but request callback, to let send request data after response @@ -254,7 +262,7 @@ namespace cryptonote if(!parse_and_validate_block_from_blob(block_entry.block, b)) { LOG_ERROR_CCONTEXT("sent wrong block: failed to parse and validate block: \r\n" - << string_tools::buff_to_hex_nodelimer(block_entry.block) << "\r\n dropping connection"); + << epee::string_tools::buff_to_hex_nodelimer(block_entry.block) << "\r\n dropping connection"); m_p2p->drop_connection(context); return 1; } @@ -274,14 +282,14 @@ namespace cryptonote auto req_it = context.m_requested_objects.find(get_block_hash(b)); if(req_it == context.m_requested_objects.end()) { - LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << string_tools::pod_to_hex(get_blob_hash(block_entry.block)) + LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << epee::string_tools::pod_to_hex(get_blob_hash(block_entry.block)) << " wasn't requested, dropping connection"); m_p2p->drop_connection(context); return 1; } if(b.tx_hashes.size() != block_entry.txs.size()) { - LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << string_tools::pod_to_hex(get_blob_hash(block_entry.block)) + LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << epee::string_tools::pod_to_hex(get_blob_hash(block_entry.block)) << ", tx_hashes.size()=" << b.tx_hashes.size() << " mismatch with block_complete_entry.m_txs.size()=" << block_entry.txs.size() << ", dropping connection"); m_p2p->drop_connection(context); return 1; @@ -300,7 +308,7 @@ namespace cryptonote { m_core.pause_mine(); - misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler( + epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler( boost::bind(&t_core::resume_mine, &m_core)); BOOST_FOREACH(const block_complete_entry& block_entry, arg.blocks) @@ -314,7 +322,7 @@ namespace cryptonote if(tvc.m_verifivation_failed) { LOG_ERROR_CCONTEXT("transaction verification failed on NOTIFY_RESPONSE_GET_OBJECTS, \r\ntx_id = " - << string_tools::pod_to_hex(get_blob_hash(tx_blob)) << ", dropping connection"); + << epee::string_tools::pod_to_hex(get_blob_hash(tx_blob)) << ", dropping connection"); m_p2p->drop_connection(context); return 1; } @@ -408,7 +416,7 @@ namespace cryptonote << "\r\nm_remote_blockchain_height=" << context.m_remote_blockchain_height << "\r\nm_needed_objects.size()=" << context.m_needed_objects.size() << "\r\nm_requested_objects.size()=" << context.m_requested_objects.size() - << "\r\non connection [" << net_utils::print_connection_context_short(context)<< "]"); + << "\r\non connection [" << epee::net_utils::print_connection_context_short(context)<< "]"); context.m_state = cryptonote_connection_context::state_normal; LOG_PRINT_CCONTEXT_GREEN(" SYNCHRONIZED OK", LOG_LEVEL_0); @@ -464,7 +472,7 @@ namespace cryptonote if(!m_core.have_block(arg.m_block_ids.front())) { LOG_ERROR_CCONTEXT("sent m_block_ids starting from unknown id: " - << string_tools::pod_to_hex(arg.m_block_ids.front()) << " , dropping connection"); + << epee::string_tools::pod_to_hex(arg.m_block_ids.front()) << " , dropping connection"); m_p2p->drop_connection(context); return 1; } diff --git a/src/daemon/daemon_commands_handler.h b/src/daemon/daemon_commands_handler.h index dca524f77..135ec68f8 100644 --- a/src/daemon/daemon_commands_handler.h +++ b/src/daemon/daemon_commands_handler.h @@ -11,6 +11,7 @@ #include "cryptonote_protocol/cryptonote_protocol_handler.h" #include "common/util.h" #include "crypto/hash.h" +#include "version.h" class daemon_cmmands_handler @@ -34,6 +35,8 @@ public: m_cmd_binder.set_handler("show_hr", boost::bind(&daemon_cmmands_handler::show_hr, this, _1), "Start showing hash rate"); m_cmd_binder.set_handler("hide_hr", boost::bind(&daemon_cmmands_handler::hide_hr, this, _1), "Stop showing hash rate"); m_cmd_binder.set_handler("save", boost::bind(&daemon_cmmands_handler::save, this, _1), "Save blockchain"); + m_cmd_binder.set_handler("set_log", boost::bind(&daemon_cmmands_handler::set_log, this, _1), "set_log <level> - Change current log detalization level, <level> is a number 0-4"); + m_cmd_binder.set_handler("diff", boost::bind(&daemon_cmmands_handler::diff, this, _1), "Show difficulty"); } bool start_handling() @@ -54,6 +57,7 @@ private: std::string get_commands_str() { std::stringstream ss; + ss << CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG << ENDL; ss << "Commands: " << ENDL; std::string usage = m_cmd_binder.get_usage(); boost::replace_all(usage, "\n", "\n "); @@ -98,6 +102,17 @@ private: return true; } //-------------------------------------------------------------------------------- + bool diff(const std::vector<std::string>& args) + { + cryptonote::difficulty_type difficulty = m_srv.get_payload_object().get_core().get_blockchain_storage().get_difficulty_for_next_block(); + uint64_t height = m_srv.get_payload_object().get_core().get_blockchain_storage().get_current_blockchain_height(); + + LOG_PRINT_GREEN("BH: " << height << ", DIFF: " << difficulty + << ", HR: " << (int) difficulty / 60L << " H/s", LOG_LEVEL_0); + + return true; + } + //-------------------------------------------------------------------------------- bool print_bc_outs(const std::vector<std::string>& args) { if(args.size() != 1) @@ -123,19 +138,34 @@ private: return false; } uint64_t start_index = 0; + uint64_t end_index = 0; uint64_t end_block_parametr = m_srv.get_payload_object().get_core().get_current_blockchain_height(); if(!string_tools::get_xtype_from_string(start_index, args[0])) { std::cout << "wrong starter block index parameter" << ENDL; return false; } - if(args.size() >1 && !string_tools::get_xtype_from_string(end_block_parametr, args[1])) + if(args.size() >1 && !string_tools::get_xtype_from_string(end_index, args[1])) { std::cout << "wrong end block index parameter" << ENDL; return false; } + if (end_index == 0) + { + end_index = end_block_parametr; + } + if (end_index > end_block_parametr) + { + std::cout << "end block index parameter shouldn't be greater than " << end_block_parametr << ENDL; + return false; + } + if (end_index <= start_index) + { + std::cout << "end block index should be greater than starter block index" << ENDL; + return false; + } - m_srv.get_payload_object().get_core().print_blockchain(start_index, end_block_parametr); + m_srv.get_payload_object().get_core().print_blockchain(start_index, end_index); return true; } //-------------------------------------------------------------------------------- @@ -144,6 +174,33 @@ private: m_srv.get_payload_object().get_core().print_blockchain_index(); return true; } + + bool set_log(const std::vector<std::string>& args) + { + if(args.size() != 1) + { + std::cout << "use: set_log <log_level_number_0-4>" << ENDL; + return true; + } + + uint16_t l = 0; + if(!string_tools::get_xtype_from_string(l, args[0])) + { + std::cout << "wrong number format, use: set_log <log_level_number_0-4>" << ENDL; + return true; + } + + if(LOG_LEVEL_4 < l) + { + std::cout << "wrong number range, use: set_log <log_level_number_0-4>" << ENDL; + return true; + } + + log_space::log_singletone::get_set_log_detalisation_level(true, l); + + return true; + } + //-------------------------------------------------------------------------------- template <typename T> static bool print_as_json(T& obj) diff --git a/src/miner/simpleminer.cpp b/src/miner/simpleminer.cpp index 3af04954f..a1c1ad5c6 100644 --- a/src/miner/simpleminer.cpp +++ b/src/miner/simpleminer.cpp @@ -109,7 +109,7 @@ namespace mining if(!m_http_client.is_connected()) { LOG_PRINT_L0("Connecting " << m_pool_ip << ":" << m_pool_port << "...."); - if(!m_http_client.connect(m_pool_ip, m_pool_port, 1000)) + if(!m_http_client.connect(m_pool_ip, m_pool_port, 20000)) { LOG_PRINT_L0("Failed to connect " << m_pool_ip << ":" << m_pool_port << ", sleep...."); epee::misc_utils::sleep_no_w(1000); @@ -198,7 +198,7 @@ namespace mining LOG_PRINT_L0("Can't get new job! Disconnect and sleep...."); m_http_client.disconnect(); epee::misc_utils::sleep_no_w(1000); - break; + continue; } if (getjob_response.blob.empty() && getjob_response.target.empty() && getjob_response.job_id.empty()) { diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 794b97429..5a03b049f 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -24,8 +24,7 @@ #include "p2p_networks.h" #include "math_helper.h" #include "net_node_common.h" - -using namespace epee; +#include "common/command_line.h" PUSH_WARNINGS DISABLE_VS_WARNINGS(4355) @@ -39,7 +38,7 @@ namespace nodetool }; template<class t_payload_net_handler> - class node_server: public levin::levin_commands_handler<p2p_connection_context_t<typename t_payload_net_handler::connection_context> >, + class node_server: public epee::levin::levin_commands_handler<p2p_connection_context_t<typename t_payload_net_handler::connection_context> >, public i_p2p_endpoint<typename t_payload_net_handler::connection_context> { struct by_conn_id{}; @@ -126,7 +125,7 @@ namespace nodetool bool parse_peer_from_string(nodetool::net_address& pe, const std::string& node_addr); bool handle_command_line(const boost::program_options::variables_map& vm); bool idle_worker(); - bool handle_remote_peerlist(const std::list<peerlist_entry>& peerlist, time_t local_time, const net_utils::connection_context_base& context); + bool handle_remote_peerlist(const std::list<peerlist_entry>& peerlist, time_t local_time, const epee::net_utils::connection_context_base& context); bool get_local_node_data(basic_node_data& node_data); //bool get_local_handshake_data(handshake_data& hshd); @@ -136,7 +135,7 @@ namespace nodetool bool connections_maker(); bool peer_sync_idle_maker(); bool do_handshake_with_peer(peerid_type& pi, p2p_connection_context& context, bool just_take_peerlist = false); - bool do_peer_timed_sync(const net_utils::connection_context_base& context, peerid_type peer_id); + bool do_peer_timed_sync(const epee::net_utils::connection_context_base& context, peerid_type peer_id); bool make_new_connection_from_peerlist(bool use_white_list); bool try_to_connect_and_handshake_with_new_peer(const net_address& na, bool just_take_peerlist = false, uint64_t last_seen_stamp = 0, bool white = true); @@ -146,12 +145,19 @@ namespace nodetool template<class t_callback> bool try_ping(basic_node_data& node_data, p2p_connection_context& context, t_callback cb); bool make_expected_connections_count(bool white_list, size_t expected_connections); + bool is_priority_node(const net_address& na); + + template <class Container> + bool connect_to_peerlist(const Container& peers); + + template <class Container> + bool parse_peers_and_add_to_container(const boost::program_options::variables_map& vm, const command_line::arg_descriptor<std::vector<std::string> > & arg, Container& container); //debug functions std::string print_connections_container(); - typedef net_utils::boosted_tcp_server<levin::async_protocol_handler<p2p_connection_context> > net_server; + typedef epee::net_utils::boosted_tcp_server<epee::levin::async_protocol_handler<p2p_connection_context> > net_server; struct config { @@ -181,9 +187,9 @@ namespace nodetool t_payload_net_handler& m_payload_handler; peerlist_manager m_peerlist; - math_helper::once_a_time_seconds<P2P_DEFAULT_HANDSHAKE_INTERVAL> m_peer_handshake_idle_maker_interval; - math_helper::once_a_time_seconds<1> m_connections_maker_interval; - math_helper::once_a_time_seconds<60*30, false> m_peerlist_store_interval; + epee::math_helper::once_a_time_seconds<P2P_DEFAULT_HANDSHAKE_INTERVAL> m_peer_handshake_idle_maker_interval; + epee::math_helper::once_a_time_seconds<1> m_connections_maker_interval; + epee::math_helper::once_a_time_seconds<60*30, false> m_peerlist_store_interval; std::string m_bind_ip; std::string m_port; @@ -191,6 +197,7 @@ namespace nodetool uint64_t m_last_stat_request_time; #endif std::list<net_address> m_priority_peers; + std::vector<net_address> m_exclusive_peers; std::vector<net_address> m_seed_nodes; std::list<nodetool::peerlist_entry> m_command_line_peers; uint64_t m_peer_livetime; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 31d3e6597..16140cf35 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -4,9 +4,10 @@ #pragma once +#include <algorithm> + #include "version.h" #include "string_tools.h" -#include "common/command_line.h" #include "common/util.h" #include "net/net_helper.h" #include "math_helper.h" @@ -31,8 +32,11 @@ namespace nodetool const command_line::arg_descriptor<bool> arg_p2p_allow_local_ip = {"allow-local-ip", "Allow local ip add to peer list, mostly in debug purposes"}; const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_peer = {"add-peer", "Manually add peer to local peerlist"}; const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_priority_node = {"add-priority-node", "Specify list of peers to connect to and attempt to keep the connection open"}; + const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_exclusive_node = {"add-exclusive-node", "Specify list of peers to connect to only." + " If this option is given the options add-priority-node and seed-node are ignored"}; const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect"}; - const command_line::arg_descriptor<bool> arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; } + const command_line::arg_descriptor<bool> arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; + } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> @@ -44,6 +48,7 @@ namespace nodetool command_line::add_arg(desc, arg_p2p_allow_local_ip); command_line::add_arg(desc, arg_p2p_add_peer); command_line::add_arg(desc, arg_p2p_add_priority_node); + command_line::add_arg(desc, arg_p2p_add_exclusive_node); command_line::add_arg(desc, arg_p2p_seed_node); command_line::add_arg(desc, arg_p2p_hide_my_port); } //----------------------------------------------------------------------------------- @@ -96,7 +101,7 @@ namespace nodetool template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::parse_peer_from_string(nodetool::net_address& pe, const std::string& node_addr) { - return string_tools::parse_peer_from_string(pe.ip, pe.port, node_addr); + return epee::string_tools::parse_peer_from_string(pe.ip, pe.port, node_addr); } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> @@ -120,30 +125,26 @@ namespace nodetool } } - if (command_line::has_arg(vm, arg_p2p_add_priority_node)) - { - std::vector<std::string> perrs = command_line::get_arg(vm, arg_p2p_add_priority_node); - for(const std::string& pr_str: perrs) - { - nodetool::net_address na = AUTO_VAL_INIT(na); - bool r = parse_peer_from_string(na, pr_str); - CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str); - m_priority_peers.push_back(na); - } + if (command_line::has_arg(vm,arg_p2p_add_exclusive_node)) + { + if (!parse_peers_and_add_to_container(vm, arg_p2p_add_exclusive_node, m_exclusive_peers)) + return false; + } + else if (command_line::has_arg(vm, arg_p2p_add_priority_node)) + { + if (!parse_peers_and_add_to_container(vm, arg_p2p_add_priority_node, m_priority_peers)) + return false; } if (command_line::has_arg(vm, arg_p2p_seed_node)) { - std::vector<std::string> seed_perrs = command_line::get_arg(vm, arg_p2p_seed_node); - for(const std::string& pr_str: seed_perrs) - { - nodetool::net_address na = AUTO_VAL_INIT(na); - bool r = parse_peer_from_string(na, pr_str); - CHECK_AND_ASSERT_MES(r, false, "Failed to parse seed address from string: " << pr_str); - m_seed_nodes.push_back(na); - } + if (!parse_peers_and_add_to_container(vm, arg_p2p_seed_node, m_seed_nodes)) + return false; } + if(command_line::has_arg(vm, arg_p2p_hide_my_port)) - m_hide_my_port = true; return true; + m_hide_my_port = true; + + return true; } //----------------------------------------------------------------------------------- namespace @@ -192,6 +193,15 @@ namespace nodetool template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::init(const boost::program_options::variables_map& vm) { + ADD_HARDCODED_SEED_NODE("62.210.78.186:18080"); + ADD_HARDCODED_SEED_NODE("195.12.60.154:18080"); + ADD_HARDCODED_SEED_NODE("54.241.246.125:18080"); + ADD_HARDCODED_SEED_NODE("107.170.157.169:18080"); + ADD_HARDCODED_SEED_NODE("54.207.112.216:18080"); + ADD_HARDCODED_SEED_NODE("78.27.112.54:18080"); + ADD_HARDCODED_SEED_NODE("209.222.30.57:18080"); + ADD_HARDCODED_SEED_NODE("80.71.13.55:18080"); + ADD_HARDCODED_SEED_NODE("107.178.112.126:18080"); ADD_HARDCODED_SEED_NODE("107.158.233.98:18080"); ADD_HARDCODED_SEED_NODE("64.22.111.2:18080"); @@ -353,23 +363,23 @@ namespace nodetool get_local_node_data(arg.node_data); m_payload_handler.get_payload_sync_data(arg.payload_data); - simple_event ev; + epee::simple_event ev; std::atomic<bool> hsh_result(false); - bool r = net_utils::async_invoke_remote_command2<typename COMMAND_HANDSHAKE::response>(context_.m_connection_id, COMMAND_HANDSHAKE::ID, arg, m_net_server.get_config_object(), + bool r = epee::net_utils::async_invoke_remote_command2<typename COMMAND_HANDSHAKE::response>(context_.m_connection_id, COMMAND_HANDSHAKE::ID, arg, m_net_server.get_config_object(), [this, &pi, &ev, &hsh_result, &just_take_peerlist](int code, const typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context) { - misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler([&](){ev.raise();}); + epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ev.raise();}); if(code < 0) { - LOG_PRINT_CC_RED(context, "COMMAND_HANDSHAKE invoke failed. (" << code << ", " << levin::get_err_descr(code) << ")", LOG_LEVEL_1); + LOG_PRINT_CC_RED(context, "COMMAND_HANDSHAKE invoke failed. (" << code << ", " << epee::levin::get_err_descr(code) << ")", LOG_LEVEL_1); return; } if(rsp.node_data.network_id != BYTECOIN_NETWORK) { - LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE Failed, wrong network! (" << string_tools::get_str_from_guid_a(rsp.node_data.network_id) << "), closing connection."); + LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE Failed, wrong network! (" << epee::string_tools::get_str_from_guid_a(rsp.node_data.network_id) << "), closing connection."); return; } @@ -397,10 +407,10 @@ namespace nodetool hsh_result = false; return; } - LOG_PRINT_CCONTEXT_L0(" COMMAND_HANDSHAKE INVOKED OK"); + LOG_PRINT_CCONTEXT_L1(" COMMAND_HANDSHAKE INVOKED OK"); }else { - LOG_PRINT_CCONTEXT_L0(" COMMAND_HANDSHAKE(AND CLOSE) INVOKED OK"); + LOG_PRINT_CCONTEXT_L1(" COMMAND_HANDSHAKE(AND CLOSE) INVOKED OK"); } }, P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT); @@ -411,7 +421,7 @@ namespace nodetool if(!hsh_result) { - LOG_PRINT_CC_L0(context_, "COMMAND_HANDSHAKE Failed"); + LOG_PRINT_CC_L1(context_, "COMMAND_HANDSHAKE Failed"); m_net_server.get_config_object().close(context_.m_connection_id); } @@ -419,17 +429,17 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::do_peer_timed_sync(const net_utils::connection_context_base& context_, peerid_type peer_id) + bool node_server<t_payload_net_handler>::do_peer_timed_sync(const epee::net_utils::connection_context_base& context_, peerid_type peer_id) { typename COMMAND_TIMED_SYNC::request arg = AUTO_VAL_INIT(arg); m_payload_handler.get_payload_sync_data(arg.payload_data); - bool r = net_utils::async_invoke_remote_command2<typename COMMAND_TIMED_SYNC::response>(context_.m_connection_id, COMMAND_TIMED_SYNC::ID, arg, m_net_server.get_config_object(), + bool r = epee::net_utils::async_invoke_remote_command2<typename COMMAND_TIMED_SYNC::response>(context_.m_connection_id, COMMAND_TIMED_SYNC::ID, arg, m_net_server.get_config_object(), [this](int code, const typename COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context) { if(code < 0) { - LOG_PRINT_CC_RED(context, "COMMAND_TIMED_SYNC invoke failed. (" << code << ", " << levin::get_err_descr(code) << ")", LOG_LEVEL_1); + LOG_PRINT_CC_RED(context, "COMMAND_TIMED_SYNC invoke failed. (" << code << ", " << epee::levin::get_err_descr(code) << ")", LOG_LEVEL_1); return; } @@ -502,36 +512,53 @@ namespace nodetool return connected; } - //----------------------------------------------------------------------------------- +#define LOG_PRINT_CC_PRIORITY_NODE(priority, con, msg) \ + do { \ + if (priority) {\ + LOG_PRINT_CC_L0(con, msg); \ + } else {\ + LOG_PRINT_CC_L1(con, msg); \ + } \ + } while(0) + template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::try_to_connect_and_handshake_with_new_peer(const net_address& na, bool just_take_peerlist, uint64_t last_seen_stamp, bool white) { - LOG_PRINT_L0("Connecting to " << string_tools::get_ip_string_from_int32(na.ip) << ":" << string_tools::num_to_string_fast(na.port) << "(white=" << white << ", last_seen: " << (last_seen_stamp?misc_utils::get_time_interval_string(time(NULL) - last_seen_stamp):"never" ) << ")..."); + LOG_PRINT_L1("Connecting to " << epee::string_tools::get_ip_string_from_int32(na.ip) << ":" + << epee::string_tools::num_to_string_fast(na.port) << "(white=" << white << ", last_seen: " + << (last_seen_stamp ? epee::misc_utils::get_time_interval_string(time(NULL) - last_seen_stamp):"never") + << ")..."); typename net_server::t_connection_context con = AUTO_VAL_INIT(con); - bool res = m_net_server.connect(string_tools::get_ip_string_from_int32(na.ip), - string_tools::num_to_string_fast(na.port), + bool res = m_net_server.connect(epee::string_tools::get_ip_string_from_int32(na.ip), + epee::string_tools::num_to_string_fast(na.port), m_config.m_net_config.connection_timeout, con); + if(!res) { - LOG_PRINT_L0("Connect failed to " - << string_tools::get_ip_string_from_int32(na.ip) - << ":" << string_tools::num_to_string_fast(na.port) + bool is_priority = is_priority_node(na); + LOG_PRINT_CC_PRIORITY_NODE(is_priority, con, "Connect failed to " + << epee::string_tools::get_ip_string_from_int32(na.ip) + << ":" << epee::string_tools::num_to_string_fast(na.port) /*<< ", try " << try_count*/); //m_peerlist.set_peer_unreachable(pe); return false; } + peerid_type pi = AUTO_VAL_INIT(pi); res = do_handshake_with_peer(pi, con, just_take_peerlist); + if(!res) { - LOG_PRINT_CC_L0(con, "Failed to HANDSHAKE with peer " - << string_tools::get_ip_string_from_int32(na.ip) - << ":" << string_tools::num_to_string_fast(na.port) + bool is_priority = is_priority_node(na); + LOG_PRINT_CC_PRIORITY_NODE(is_priority, con, "Failed to HANDSHAKE with peer " + << epee::string_tools::get_ip_string_from_int32(na.ip) + << ":" << epee::string_tools::num_to_string_fast(na.port) /*<< ", try " << try_count*/); return false; } + if(just_take_peerlist) { m_net_server.get_config_object().close(con.m_connection_id); @@ -549,6 +576,9 @@ namespace nodetool LOG_PRINT_CC_GREEN(con, "CONNECTION HANDSHAKED OK.", LOG_LEVEL_2); return true; } + +#undef LOG_PRINT_CC_PRIORITY_NODE + //----------------------------------------------------------------------------------- template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::make_new_connection_from_peerlist(bool use_white_list) @@ -582,7 +612,10 @@ namespace nodetool if(is_peer_used(pe)) continue; - LOG_PRINT_L1("Selected peer: " << pe.id << " " << string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << boost::lexical_cast<std::string>(pe.adr.port) << "[white=" << use_white_list << "] last_seen: " << (pe.last_seen ? misc_utils::get_time_interval_string(time(NULL) - pe.last_seen) : "never")); + LOG_PRINT_L1("Selected peer: " << pe.id << " " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) + << ":" << boost::lexical_cast<std::string>(pe.adr.port) + << "[white=" << use_white_list + << "] last_seen: " << (pe.last_seen ? epee::misc_utils::get_time_interval_string(time(NULL) - pe.last_seen) : "never")); if(!try_to_connect_and_handshake_with_new_peer(pe.adr, false, pe.last_seen, use_white_list)) continue; @@ -595,6 +628,10 @@ namespace nodetool template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::connections_maker() { + if (!connect_to_peerlist(m_exclusive_peers)) return false; + + if (!m_exclusive_peers.empty()) return true; + if(!m_peerlist.get_white_peers_count() && m_seed_nodes.size()) { size_t try_count = 0; @@ -616,15 +653,7 @@ namespace nodetool } } - for(const net_address& na: m_priority_peers) - { - if(m_net_server.is_stop_signal_sent()) - return false; - - if(is_addr_connected(na)) - continue; - try_to_connect_and_handshake_with_new_peer(na); - } + if (!connect_to_peerlist(m_priority_peers)) return false; size_t expected_white_connections = (m_config.m_net_config.connections_count*P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT)/100; @@ -698,7 +727,7 @@ namespace nodetool bool node_server<t_payload_net_handler>::peer_sync_idle_maker() { LOG_PRINT_L2("STARTED PEERLIST IDLE HANDSHAKE"); - typedef std::list<std::pair<net_utils::connection_context_base, peerid_type> > local_connects_type; + typedef std::list<std::pair<epee::net_utils::connection_context_base, peerid_type> > local_connects_type; local_connects_type cncts; m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) { @@ -725,7 +754,7 @@ namespace nodetool { if(be.last_seen > local_time) { - LOG_PRINT_RED_L0("FOUND FUTURE peerlist for entry " << string_tools::get_ip_string_from_int32(be.adr.ip) << ":" << be.adr.port << " last_seen: " << be.last_seen << ", local_time(on remote node):" << local_time); + LOG_PRINT_RED_L0("FOUND FUTURE peerlist for entry " << epee::string_tools::get_ip_string_from_int32(be.adr.ip) << ":" << be.adr.port << " last_seen: " << be.last_seen << ", local_time(on remote node):" << local_time); return false; } be.last_seen += delta; @@ -734,7 +763,7 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::handle_remote_peerlist(const std::list<peerlist_entry>& peerlist, time_t local_time, const net_utils::connection_context_base& context) + bool node_server<t_payload_net_handler>::handle_remote_peerlist(const std::list<peerlist_entry>& peerlist, time_t local_time, const epee::net_utils::connection_context_base& context) { int64_t delta = 0; std::list<peerlist_entry> peerlist_ = peerlist; @@ -782,7 +811,7 @@ namespace nodetool return false; } crypto::public_key pk = AUTO_VAL_INIT(pk); - string_tools::hex_to_pod(P2P_STAT_TRUSTED_PUB_KEY, pk); + epee::string_tools::hex_to_pod(P2P_STAT_TRUSTED_PUB_KEY, pk); crypto::hash h = tools::get_proof_of_trust_hash(tr); if(!crypto::check_signature(h, pk, tr.sign)) { @@ -903,8 +932,8 @@ namespace nodetool uint32_t actual_ip = context.m_remote_ip; if(!m_peerlist.is_ip_allowed(actual_ip)) return false; - std::string ip = string_tools::get_ip_string_from_int32(actual_ip); - std::string port = string_tools::num_to_string_fast(node_data.my_port); + std::string ip = epee::string_tools::get_ip_string_from_int32(actual_ip); + std::string port = epee::string_tools::num_to_string_fast(node_data.my_port); peerid_type pr = node_data.peer_id; bool r = m_net_server.connect_async(ip, port, m_config.m_net_config.ping_connection_timeout, [cb, /*context,*/ ip, port, pr, this]( const typename net_server::t_connection_context& ping_context, @@ -922,12 +951,12 @@ namespace nodetool std::string port_=port; peerid_type pr_ = pr; auto cb_ = cb;*/ - bool inv_call_res = net_utils::async_invoke_remote_command2<COMMAND_PING::response>(ping_context.m_connection_id, COMMAND_PING::ID, req, m_net_server.get_config_object(), + bool inv_call_res = epee::net_utils::async_invoke_remote_command2<COMMAND_PING::response>(ping_context.m_connection_id, COMMAND_PING::ID, req, m_net_server.get_config_object(), [=](int code, const COMMAND_PING::response& rsp, p2p_connection_context& context) { if(code <= 0) { - LOG_PRINT_CC_L2(ping_context, "Failed to invoke COMMAND_PING to " << ip << ":" << port << "(" << code << ", " << levin::get_err_descr(code) << ")"); + LOG_PRINT_CC_L2(ping_context, "Failed to invoke COMMAND_PING to " << ip << ":" << port << "(" << code << ", " << epee::levin::get_err_descr(code) << ")"); return; } @@ -979,7 +1008,7 @@ namespace nodetool if(arg.node_data.network_id != BYTECOIN_NETWORK) { - LOG_PRINT_CCONTEXT_L0("WRONG NETWORK AGENT CONNECTED! id=" << string_tools::get_str_from_guid_a(arg.node_data.network_id)); + LOG_PRINT_CCONTEXT_L0("WRONG NETWORK AGENT CONNECTED! id=" << epee::string_tools::get_str_from_guid_a(arg.node_data.network_id)); drop_connection(context); return 1; } @@ -1021,7 +1050,7 @@ namespace nodetool time(&pe.last_seen); pe.id = peer_id_l; this->m_peerlist.append_with_peer_white(pe); - LOG_PRINT_CCONTEXT_L2("PING SUCCESS " << string_tools::get_ip_string_from_int32(context.m_remote_ip) << ":" << port_l); + LOG_PRINT_CCONTEXT_L2("PING SUCCESS " << epee::string_tools::get_ip_string_from_int32(context.m_remote_ip) << ":" << port_l); }); } @@ -1066,9 +1095,9 @@ namespace nodetool std::stringstream ss; m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) { - ss << string_tools::get_ip_string_from_int32(cntxt.m_remote_ip) << ":" << cntxt.m_remote_port + ss << epee::string_tools::get_ip_string_from_int32(cntxt.m_remote_ip) << ":" << cntxt.m_remote_port << " \t\tpeer_id " << cntxt.peer_id - << " \t\tconn_id " << string_tools::get_str_from_guid_a(cntxt.m_connection_id) << (cntxt.m_is_income ? " INC":" OUT") + << " \t\tconn_id " << epee::string_tools::get_str_from_guid_a(cntxt.m_connection_id) << (cntxt.m_is_income ? " INC":" OUT") << std::endl; return true; }); @@ -1079,13 +1108,51 @@ namespace nodetool template<class t_payload_net_handler> void node_server<t_payload_net_handler>::on_connection_new(p2p_connection_context& context) { - LOG_PRINT_L2("["<< net_utils::print_connection_context(context) << "] NEW CONNECTION"); + LOG_PRINT_L2("["<< epee::net_utils::print_connection_context(context) << "] NEW CONNECTION"); } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> void node_server<t_payload_net_handler>::on_connection_close(p2p_connection_context& context) { - LOG_PRINT_L2("["<< net_utils::print_connection_context(context) << "] CLOSE CONNECTION"); + LOG_PRINT_L2("["<< epee::net_utils::print_connection_context(context) << "] CLOSE CONNECTION"); + } + + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::is_priority_node(const net_address& na) + { + return (std::find(m_priority_peers.begin(), m_priority_peers.end(), na) != m_priority_peers.end()) || (std::find(m_exclusive_peers.begin(), m_exclusive_peers.end(), na) != m_exclusive_peers.end()); + } + + template<class t_payload_net_handler> template <class Container> + bool node_server<t_payload_net_handler>::connect_to_peerlist(const Container& peers) + { + for(const net_address& na: peers) + { + if(m_net_server.is_stop_signal_sent()) + return false; + + if(is_addr_connected(na)) + continue; + + try_to_connect_and_handshake_with_new_peer(na); + } + + return true; + } + + template<class t_payload_net_handler> template <class Container> + bool node_server<t_payload_net_handler>::parse_peers_and_add_to_container(const boost::program_options::variables_map& vm, const command_line::arg_descriptor<std::vector<std::string> > & arg, Container& container) + { + std::vector<std::string> perrs = command_line::get_arg(vm, arg); + + for(const std::string& pr_str: perrs) + { + nodetool::net_address na = AUTO_VAL_INIT(na); + bool r = parse_peer_from_string(na, pr_str); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str); + container.push_back(na); + } + + return true; } - //----------------------------------------------------------------------------------- } diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 631fb5cf8..c3f98563d 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -48,24 +48,30 @@ namespace cryptonote return epee::http_server_impl_base<core_rpc_server, connection_context>::init(m_port, m_bind_ip); } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::check_core_ready() + bool core_rpc_server::check_core_busy() { - if(!m_p2p.get_payload_object().is_synchronized()) + if(m_p2p.get_payload_object().get_core().get_blockchain_storage().is_storing_blockchain()) { return false; } - if(m_p2p.get_payload_object().get_core().get_blockchain_storage().is_storing_blockchain()) + return true; + } +#define CHECK_CORE_BUSY() if(!check_core_busy()){res.status = CORE_RPC_STATUS_BUSY;return true;} + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::check_core_ready() + { + if(!m_p2p.get_payload_object().is_synchronized()) { return false; } - return true; + return check_core_busy(); } #define CHECK_CORE_READY() if(!check_core_ready()){res.status = CORE_RPC_STATUS_BUSY;return true;} //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res, connection_context& cntx) { - CHECK_CORE_READY(); + CHECK_CORE_BUSY(); res.height = m_core.get_current_blockchain_height(); res.status = CORE_RPC_STATUS_OK; return true; @@ -73,8 +79,9 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, connection_context& cntx) { - CHECK_CORE_READY(); + CHECK_CORE_BUSY(); res.height = m_core.get_current_blockchain_height(); + res.target_height = m_core.get_target_blockchain_height(); res.difficulty = m_core.get_blockchain_storage().get_difficulty_for_next_block(); res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase res.tx_pool_size = m_core.get_pool_transactions_count(); @@ -90,7 +97,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res, connection_context& cntx) { - CHECK_CORE_READY(); + CHECK_CORE_BUSY(); std::list<std::pair<block, std::list<transaction> > > bs; if(!m_core.find_blockchain_supplement(req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) { @@ -114,7 +121,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res, connection_context& cntx) { - CHECK_CORE_READY(); + CHECK_CORE_BUSY(); res.status = "Failed"; if(!m_core.get_random_outs_for_amounts(req, res)) { @@ -143,7 +150,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res, connection_context& cntx) { - CHECK_CORE_READY(); + CHECK_CORE_BUSY(); bool r = m_core.get_tx_outputs_gindexs(req.txid, res.o_indexes); if(!r) { @@ -157,7 +164,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res, connection_context& cntx) { - CHECK_CORE_READY(); + CHECK_CORE_BUSY(); std::vector<crypto::hash> vh; BOOST_FOREACH(const auto& tx_hex_str, req.txs_hashes) { @@ -232,7 +239,6 @@ namespace cryptonote return true; } - NOTIFY_NEW_TRANSACTIONS::request r; r.txs.push_back(tx_blob); m_core.get_protocol()->relay_transactions(r, fake_context); @@ -265,7 +271,6 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res, connection_context& cntx) { - CHECK_CORE_READY(); if(!m_core.get_miner().stop()) { res.status = "Failed, mining not stopped"; @@ -275,9 +280,39 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res, connection_context& cntx) + bool core_rpc_server::on_mining_status(const COMMAND_RPC_MINING_STATUS::request& req, COMMAND_RPC_MINING_STATUS::response& res, connection_context& cntx) { CHECK_CORE_READY(); + + const miner& lMiner = m_core.get_miner(); + res.active = lMiner.is_mining(); + + if ( lMiner.is_mining() ) { + res.speed = lMiner.get_speed(); + res.threads_count = lMiner.get_threads_count(); + const account_public_address& lMiningAdr = lMiner.get_mining_address(); + res.address = get_account_address_as_str(lMiningAdr); + } + + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res, connection_context& cntx) + { + CHECK_CORE_BUSY(); + if( !m_core.get_blockchain_storage().store_blockchain() ) + { + res.status = "Error while storing blockhain"; + return true; + } + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res, connection_context& cntx) + { + CHECK_CORE_BUSY(); res.count = m_core.get_current_blockchain_height(); res.status = CORE_RPC_STATUS_OK; return true; @@ -285,7 +320,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp, connection_context& cntx) { - if(!check_core_ready()) + if(!check_core_busy()) { error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; error_resp.message = "Core is busy"; @@ -383,7 +418,7 @@ namespace cryptonote return false; } res.blocktemplate_blob = string_tools::buff_to_hex_nodelimer(block_blob); - + res.status = CORE_RPC_STATUS_OK; return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -403,15 +438,33 @@ namespace cryptonote error_resp.message = "Wrong block blob"; return false; } - cryptonote::block_verification_context bvc = AUTO_VAL_INIT(bvc); - m_core.handle_incoming_block(blockblob, bvc); - if(!bvc.m_added_to_main_chain) + + // Fixing of high orphan issue for most pools + // Thanks Boolberry! + block b = AUTO_VAL_INIT(b); + if(!parse_and_validate_block_from_blob(blockblob, b)) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB; + error_resp.message = "Wrong block blob"; + return false; + } + + // Fix from Boolberry neglects to check block + // size, do that with the function below + if(!m_core.check_incoming_block_size(blockblob)) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB_SIZE; + error_resp.message = "Block bloc size is too big, rejecting block"; + return false; + } + + if(!m_core.handle_block_found(b)) { error_resp.code = CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED; error_resp.message = "Block not accepted"; return false; } - res.status = "OK"; + res.status = CORE_RPC_STATUS_OK; return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -443,7 +496,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res, epee::json_rpc::error& error_resp, connection_context& cntx) { - if(!check_core_ready()) + if(!check_core_busy()) { error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; error_resp.message = "Core is busy."; @@ -478,7 +531,7 @@ namespace cryptonote } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_block_header_by_hash(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response& res, epee::json_rpc::error& error_resp, connection_context& cntx){ - if(!check_core_ready()) + if(!check_core_busy()) { error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; error_resp.message = "Core is busy."; @@ -519,7 +572,7 @@ namespace cryptonote } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response& res, epee::json_rpc::error& error_resp, connection_context& cntx){ - if(!check_core_ready()) + if(!check_core_busy()) { error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; error_resp.message = "Core is busy."; diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 7b14e741a..abd9e3de7 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -40,6 +40,8 @@ namespace cryptonote MAP_URI_AUTO_JON2("/sendrawtransaction", on_send_raw_tx, COMMAND_RPC_SEND_RAW_TX) MAP_URI_AUTO_JON2("/start_mining", on_start_mining, COMMAND_RPC_START_MINING) MAP_URI_AUTO_JON2("/stop_mining", on_stop_mining, COMMAND_RPC_STOP_MINING) + MAP_URI_AUTO_JON2("/mining_status", on_mining_status, COMMAND_RPC_MINING_STATUS) + MAP_URI_AUTO_JON2("/save_bc", on_save_bc, COMMAND_RPC_SAVE_BC) MAP_URI_AUTO_JON2("/getinfo", on_get_info, COMMAND_RPC_GET_INFO) BEGIN_JSON_RPC_MAP("/json_rpc") MAP_JON_RPC("getblockcount", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT) @@ -59,8 +61,10 @@ namespace cryptonote bool on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res, connection_context& cntx); bool on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res, connection_context& cntx); bool on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res, connection_context& cntx); + bool on_mining_status(const COMMAND_RPC_MINING_STATUS::request& req, COMMAND_RPC_MINING_STATUS::response& res, connection_context& cntx); bool on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res, connection_context& cntx); bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, connection_context& cntx); + bool on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res, connection_context& cntx); //json_rpc bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res, connection_context& cntx); @@ -72,6 +76,7 @@ namespace cryptonote bool on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); //----------------------- bool handle_command_line(const boost::program_options::variables_map& vm); + bool check_core_busy(); bool check_core_ready(); //utils diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index d67ebfdd7..6e87fb48c 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -214,6 +214,7 @@ namespace cryptonote { std::string status; uint64_t height; + uint64_t target_height; uint64_t difficulty; uint64_t tx_count; uint64_t tx_pool_size; @@ -226,6 +227,7 @@ namespace cryptonote BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE(height) + KV_SERIALIZE(target_height) KV_SERIALIZE(difficulty) KV_SERIALIZE(tx_count) KV_SERIALIZE(tx_pool_size) @@ -260,7 +262,56 @@ namespace cryptonote }; }; + //----------------------------------------------- + struct COMMAND_RPC_MINING_STATUS + { + struct request + { + + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + std::string status; + bool active; + uint64_t speed; + uint32_t threads_count; + std::string address; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(active) + KV_SERIALIZE(speed) + KV_SERIALIZE(threads_count) + KV_SERIALIZE(address) + END_KV_SERIALIZE_MAP() + }; + }; + //----------------------------------------------- + struct COMMAND_RPC_SAVE_BC + { + struct request + { + + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + // struct COMMAND_RPC_GETBLOCKCOUNT { diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h index 10785f8ab..273a8b8cb 100644 --- a/src/rpc/core_rpc_server_error_codes.h +++ b/src/rpc/core_rpc_server_error_codes.h @@ -13,7 +13,6 @@ #define CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB -6 #define CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED -7 #define CORE_RPC_ERROR_CODE_CORE_BUSY -9 - - +#define CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB_SIZE -10 diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h index 89d3c9885..b7763ffeb 100644 --- a/src/serialization/crypto.h +++ b/src/serialization/crypto.h @@ -2,6 +2,8 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <vector> + #include "serialization.h" #include "debug_archive.h" #include "crypto/chacha8.h" diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index 2d78baf3a..7024fdc03 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -10,6 +10,7 @@ #include <vector> #include <string> #include <boost/type_traits/is_integral.hpp> +#include <boost/type_traits/integral_constant.hpp> template <class T> struct is_blob_type { typedef boost::false_type type; }; @@ -79,8 +80,10 @@ inline bool do_serialize(Archive &ar, T &v) if (!r || !ar.stream().good()) return false; \ } while(0); #define FIELDS(f) \ + do { \ bool r = ::do_serialize(ar, f); \ - if (!r || !ar.stream().good()) return false; + if (!r || !ar.stream().good()) return false; \ + } while(0); #define FIELD(f) \ do { \ ar.tag(#f); \ @@ -93,6 +96,12 @@ inline bool do_serialize(Archive &ar, T &v) ar.serialize_varint(f); \ if (!ar.stream().good()) return false; \ } while(0); +#define VARINT_FIELD_N(t, f) \ + do { \ + ar.tag(t); \ + ar.serialize_varint(f); \ + if (!ar.stream().good()) return false; \ + } while(0); namespace serialization { namespace detail diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 919782ab6..6af0de9f9 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <thread> +#include <iostream> #include <boost/lexical_cast.hpp> #include <boost/program_options.hpp> #include <boost/algorithm/string.hpp> @@ -17,6 +18,8 @@ #include "rpc/core_rpc_server_commands_defs.h" #include "wallet/wallet_rpc_server.h" #include "version.h" +#include "crypto/crypto.h" // for crypto::secret_key definition +#include "crypto/electrum-words.h" #if defined(WIN32) #include <crtdbg.h> @@ -38,6 +41,9 @@ namespace const command_line::arg_descriptor<std::string> arg_daemon_address = {"daemon-address", "Use daemon instance at <host>:<port>", ""}; const command_line::arg_descriptor<std::string> arg_daemon_host = {"daemon-host", "Use daemon instance at host <arg> instead of localhost", ""}; const command_line::arg_descriptor<std::string> arg_password = {"password", "Wallet password", "", true}; + const command_line::arg_descriptor<std::string> arg_electrum_seed = {"electrum-seed", "Specify electrum seed for wallet recovery/creation", ""}; + const command_line::arg_descriptor<bool> arg_restore_deterministic_wallet = {"restore-deterministic-wallet", "Recover wallet using electrum-style mnemonic", false}; + const command_line::arg_descriptor<bool> arg_non_deterministic = {"non-deterministic", "creates non-deterministic view and spend keys", false}; const command_line::arg_descriptor<int> arg_daemon_port = {"daemon-port", "Use daemon instance at port <arg> instead of 8081", 0}; const command_line::arg_descriptor<uint32_t> arg_log_level = {"set_log", "", 0, true}; @@ -64,19 +70,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: @@ -181,6 +174,7 @@ simple_wallet::simple_wallet() { m_cmd_binder.set_handler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), "start_mining [<number_of_threads>] - Start mining in daemon"); m_cmd_binder.set_handler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, _1), "Stop mining in daemon"); + m_cmd_binder.set_handler("save_bc", boost::bind(&simple_wallet::save_bc, this, _1), "Save current blockchain data"); m_cmd_binder.set_handler("refresh", boost::bind(&simple_wallet::refresh, this, _1), "Resynchronize transactions and balance"); m_cmd_binder.set_handler("balance", boost::bind(&simple_wallet::show_balance, this, _1), "Show current wallet balance"); m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), "incoming_transfers [available|unavailable] - Show incoming transfers - all of them or filter them by availability"); @@ -201,7 +195,7 @@ bool simple_wallet::set_log(const std::vector<std::string> &args) return true; } uint16_t l = 0; - if(!string_tools::get_xtype_from_string(l, args[0])) + if(!epee::string_tools::get_xtype_from_string(l, args[0])) { fail_msg_writer() << "wrong number format, use: set_log <log_level_number_0-4>"; return true; @@ -220,16 +214,24 @@ bool simple_wallet::ask_wallet_create_if_needed() { std::string wallet_path; - std::cout << "Specify wallet file name (e.g., wallet.bin). If the wallet doesn't exist, it will be created.\n"; - std::cout << "Wallet file name: "; - - std::getline(std::cin, wallet_path); - - wallet_path = string_tools::trim(wallet_path); + wallet_path = command_line::input_line( + "Specify wallet file name (e.g., wallet.bin). If the wallet doesn't exist, it will be created.\n" + "Wallet file name: " + ); bool keys_file_exists; - bool wallet_file_exitst; - tools::wallet2::wallet_exists(wallet_path, keys_file_exists, wallet_file_exitst); + bool wallet_file_exists; + tools::wallet2::wallet_exists(wallet_path, keys_file_exists, wallet_file_exists); + + // add logic to error out if new wallet requested but named wallet file exists + if (keys_file_exists || wallet_file_exists) + { + if (!m_generate_new.empty() || m_restore_deterministic_wallet) + { + fail_msg_writer() << "Attempting to generate or restore wallet, but specified file(s) exist. Exiting to not risk overwriting."; + return false; + } + } bool r; if(keys_file_exists) @@ -238,7 +240,7 @@ bool simple_wallet::ask_wallet_create_if_needed() r = true; }else { - if(!wallet_file_exitst) + if(!wallet_file_exists) { std::cout << "The wallet doesn't exist, generating new one" << std::endl; m_generate_new = wallet_path; @@ -263,13 +265,14 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) return false; } - size_t c = 0; - if(!m_generate_new.empty()) ++c; - if(!m_wallet_file.empty()) ++c; - if (1 != c) + if(!m_generate_new.empty() && !m_wallet_file.empty()) { - if(!ask_wallet_create_if_needed()) - return false; + fail_msg_writer() << "Specifying both --generate-new-wallet=\"wallet_name\" and --wallet-file=\"wallet_name\" doesn't make sense!"; + return false; + } + else if (m_generate_new.empty() && m_wallet_file.empty()) + { + if(!ask_wallet_create_if_needed()) return false; } if (m_daemon_host.empty()) @@ -294,9 +297,36 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) } } - if (!m_generate_new.empty()) + if (!m_generate_new.empty() || m_restore_deterministic_wallet) { - bool r = new_wallet(m_generate_new, pwd_container.password()); + if (m_wallet_file.empty()) m_wallet_file = m_generate_new; // alias for simplicity later + + // check for recover flag. if present, require electrum word list (only recovery option for now). + if (m_restore_deterministic_wallet) + { + if (m_non_deterministic) + { + fail_msg_writer() << "Cannot specify both --restore-deterministic-wallet and --non-deterministic"; + return false; + } + + if (m_electrum_seed.empty()) + { + m_electrum_seed = command_line::input_line("Specify electrum seed: "); + if (m_electrum_seed.empty()) + { + fail_msg_writer() << "specify a recovery parameter with the --electrum-seed=\"words list here\""; + return false; + } + } + + if (!crypto::ElectrumWords::words_to_bytes(m_electrum_seed, m_recovery_key)) + { + fail_msg_writer() << "electrum-style word list failed verification"; + return false; + } + } + bool r = new_wallet(m_wallet_file, pwd_container.password(), m_recovery_key, m_restore_deterministic_wallet, m_non_deterministic); CHECK_AND_ASSERT_MES(r, false, "account creation failed"); } else @@ -318,11 +348,14 @@ bool simple_wallet::deinit() //---------------------------------------------------------------------------------------------------- void simple_wallet::handle_command_line(const boost::program_options::variables_map& vm) { - m_wallet_file = command_line::get_arg(vm, arg_wallet_file); - m_generate_new = command_line::get_arg(vm, arg_generate_new_wallet); - m_daemon_address = command_line::get_arg(vm, arg_daemon_address); - m_daemon_host = command_line::get_arg(vm, arg_daemon_host); - m_daemon_port = command_line::get_arg(vm, arg_daemon_port); + m_wallet_file = command_line::get_arg(vm, arg_wallet_file); + m_generate_new = command_line::get_arg(vm, arg_generate_new_wallet); + m_daemon_address = command_line::get_arg(vm, arg_daemon_address); + m_daemon_host = command_line::get_arg(vm, arg_daemon_host); + m_daemon_port = command_line::get_arg(vm, arg_daemon_port); + m_electrum_seed = command_line::get_arg(vm, arg_electrum_seed); + m_restore_deterministic_wallet = command_line::get_arg(vm, arg_restore_deterministic_wallet); + m_non_deterministic = command_line::get_arg(vm, arg_non_deterministic); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::try_connect_to_daemon() @@ -336,16 +369,19 @@ bool simple_wallet::try_connect_to_daemon() } return true; } + //---------------------------------------------------------------------------------------------------- -bool simple_wallet::new_wallet(const string &wallet_file, const std::string& password) +bool simple_wallet::new_wallet(const string &wallet_file, const std::string& password, const crypto::secret_key& recovery_key, bool recover, bool two_random) { m_wallet_file = wallet_file; m_wallet.reset(new tools::wallet2()); m_wallet->callback(this); + + crypto::secret_key recovery_val; try { - m_wallet->generate(wallet_file, password); + recovery_val = m_wallet->generate(wallet_file, password, recovery_key, recover, two_random); message_writer(epee::log_space::console_color_white, true) << "Generated new wallet: " << m_wallet->get_account().get_public_address_str() << std::endl << "view key: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key); } catch (const std::exception& e) @@ -356,6 +392,13 @@ bool simple_wallet::new_wallet(const string &wallet_file, const std::string& pas m_wallet->init(m_daemon_address); + // convert rng value to electrum-style word list + std::string electrum_words; + crypto::ElectrumWords::bytes_to_words(recovery_val, electrum_words); + + std::string print_electrum = ""; + + success_msg_writer() << "**********************************************************************\n" << "Your wallet has been generated.\n" << @@ -363,8 +406,16 @@ bool simple_wallet::new_wallet(const string &wallet_file, const std::string& pas "Use \"help\" command to see the list of available commands.\n" << "Always use \"exit\" command when closing simplewallet to save\n" << "current session's state. Otherwise, you will possibly need to synchronize \n" << - "your wallet again. Your wallet key is NOT under risk anyway.\n" << - "**********************************************************************"; + "your wallet again. Your wallet key is NOT under risk anyway.\n" + ; + + if (!two_random) + { + success_msg_writer(true) << "\nPLEASE NOTE: the following 24 words can be used to recover access to your wallet. Please write them down and store them somewhere safe and secure. Please do not store them in your email or on file storage services outside of your immediate control. You will not be able to view these words again, so it is imperative to make note of them now.\n"; + std::cout << electrum_words << std::endl; + } + success_msg_writer() << "**********************************************************************"; + return true; } //---------------------------------------------------------------------------------------------------- @@ -491,6 +542,22 @@ bool simple_wallet::stop_mining(const std::vector<std::string>& args) return true; } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::save_bc(const std::vector<std::string>& args) +{ + if (!try_connect_to_daemon()) + return true; + + COMMAND_RPC_SAVE_BC::request req; + COMMAND_RPC_SAVE_BC::response res; + bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/save_bc", req, res, m_http_client); + std::string err = interpret_rpc_response(r, res.status); + if (err.empty()) + success_msg_writer() << "Blockchain saved"; + else + fail_msg_writer() << "Blockchain can't be saved: " << err; + return true; +} +//---------------------------------------------------------------------------------------------------- void simple_wallet::on_new_block(uint64_t height, const cryptonote::block& block) { m_refresh_progress_reporter.update(height, false); @@ -663,7 +730,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); @@ -732,7 +799,7 @@ bool simple_wallet::transfer(const std::vector<std::string> &args_) } size_t fake_outs_count; - if(!string_tools::get_xtype_from_string(fake_outs_count, local_args[0])) + if(!epee::string_tools::get_xtype_from_string(fake_outs_count, local_args[0])) { fail_msg_writer() << "mixin_count should be non-negative integer, got " << local_args[0]; return true; @@ -746,7 +813,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; @@ -912,6 +979,9 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_params, arg_daemon_port); command_line::add_arg(desc_params, arg_command); command_line::add_arg(desc_params, arg_log_level); + command_line::add_arg(desc_params, arg_restore_deterministic_wallet ); + command_line::add_arg(desc_params, arg_non_deterministic ); + command_line::add_arg(desc_params, arg_electrum_seed ); tools::wallet_rpc_server::init_options(desc_params); po::positional_options_description positional_options; @@ -927,7 +997,7 @@ int main(int argc, char* argv[]) if (command_line::get_arg(vm, command_line::arg_help)) { - success_msg_writer() << "bytecoin wallet v" << PROJECT_VERSION_LONG; + success_msg_writer() << CRYPTONOTE_NAME << " wallet v" << PROJECT_VERSION_LONG; success_msg_writer() << "Usage: simplewallet [--wallet-file=<file>|--generate-new-wallet=<file>] [--daemon-address=<host>:<port>] [<COMMAND>]"; success_msg_writer() << desc_all << '\n' << w.get_commands_str(); return false; @@ -944,7 +1014,7 @@ int main(int argc, char* argv[]) return true; }); if (!r) - return 1; + return 0; //set up logging options log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2); @@ -1037,15 +1107,21 @@ int main(int argc, char* argv[]) std::vector<std::string> command = command_line::get_arg(vm, arg_command); if (!command.empty()) + { w.process_command(command); - - tools::signal_handler::install([&w] { w.stop(); - }); - w.run(); + w.deinit(); + } + else + { + tools::signal_handler::install([&w] { + w.stop(); + }); + w.run(); - w.deinit(); + w.deinit(); + } } - return 1; + return 0; //CATCH_ENTRY_L0("main", 1); } diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index a6c771804..006cdd08e 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -13,6 +13,7 @@ #include "wallet/wallet2.h" #include "console_handler.h" #include "password_container.h" +#include "crypto/crypto.h" // for definition of crypto::secret_key namespace cryptonote @@ -39,13 +40,14 @@ namespace cryptonote bool run_console_handler(); - bool new_wallet(const std::string &wallet_file, const std::string& password); + bool new_wallet(const std::string &wallet_file, const std::string& password, const crypto::secret_key& recovery_key = crypto::secret_key(), bool recover = false, bool two_random = false); bool open_wallet(const std::string &wallet_file, const std::string& password); bool close_wallet(); bool help(const std::vector<std::string> &args = std::vector<std::string>()); bool start_mining(const std::vector<std::string> &args); bool stop_mining(const std::vector<std::string> &args); + bool save_bc(const std::vector<std::string>& args); bool refresh(const std::vector<std::string> &args); bool show_balance(const std::vector<std::string> &args = std::vector<std::string>()); bool show_incoming_transfers(const std::vector<std::string> &args); @@ -124,6 +126,12 @@ namespace cryptonote std::string m_generate_new; std::string m_import_path; + std::string m_electrum_seed; // electrum-style seed parameter + + crypto::secret_key m_recovery_key; // recovery key (used as random for wallet gen) + bool m_restore_deterministic_wallet; // recover flag + bool m_non_deterministic; // old 2-random generation + std::string m_daemon_address; std::string m_daemon_host; int m_daemon_port; @@ -131,7 +139,7 @@ namespace cryptonote epee::console_handlers_binder m_cmd_binder; std::unique_ptr<tools::wallet2> m_wallet; - net_utils::http::http_simple_client m_http_client; + epee::net_utils::http::http_simple_client m_http_client; refresh_progress_reporter_t m_refresh_progress_reporter; }; } diff --git a/src/version.h.in b/src/version.h.in index 2578ea635..f171a8e6b 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "0.8.6" -#define PROJECT_VERSION_BUILD_NO "295" +#define PROJECT_VERSION "0.8.8" +#define PROJECT_VERSION_BUILD_NO "1" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e8d67eec2..a63eb4be7 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -19,6 +19,7 @@ using namespace epee; #include "profile_tools.h" #include "crypto/crypto.h" #include "serialization/binary_utils.h" +#include "cryptonote_protocol/blobdatatype.h" using namespace cryptonote; @@ -37,6 +38,7 @@ void do_prepare_file_names(const std::string& file_path, std::string& keys_file, keys_file += ".keys"; } } + } //namespace namespace tools @@ -434,7 +436,7 @@ void wallet2::load_keys(const std::string& keys_file_name, const std::string& pa THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password); } //---------------------------------------------------------------------------------------------------- -void wallet2::generate(const std::string& wallet_, const std::string& password) +crypto::secret_key wallet2::generate(const std::string& wallet_, const std::string& password, const crypto::secret_key& recovery_param, bool recover, bool two_random) { clear(); prepare_file_names(wallet_); @@ -443,7 +445,8 @@ void wallet2::generate(const std::string& wallet_, const std::string& password) THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file); THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file); - m_account.generate(); + crypto::secret_key retval = m_account.generate(recovery_param, recover, two_random); + m_account_public_address = m_account.get_keys().m_account_address; bool r = store_keys(m_keys_file, password); @@ -453,16 +456,30 @@ void wallet2::generate(const std::string& wallet_, const std::string& password) if(!r) LOG_PRINT_RED_L0("String with address text not saved"); store(); + return retval; } //---------------------------------------------------------------------------------------------------- -void wallet2::wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exitst) +void wallet2::wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exists) { std::string keys_file, wallet_file; do_prepare_file_names(file_path, keys_file, wallet_file); boost::system::error_code ignore; keys_file_exists = boost::filesystem::exists(keys_file, ignore); - wallet_file_exitst = boost::filesystem::exists(wallet_file, ignore); + 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) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index edbf31f2d..534a0517b 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -97,12 +97,17 @@ namespace tools END_SERIALIZE() }; - void generate(const std::string& wallet, const std::string& password); + crypto::secret_key generate(const std::string& wallet, const std::string& password, const crypto::secret_key& recovery_param = crypto::secret_key(), bool recover = false, bool two_random = false); void load(const std::string& wallet, const std::string& password); void store(); cryptonote::account_base& get_account(){return m_account;} - void init(const std::string& daemon_address = "http://localhost:8080", uint64_t upper_transaction_size_limit = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE*2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); + // upper_transaction_size_limit as defined below is set to + // approximately 125% of the fixed minimum allowable penalty + // free block size. TODO: fix this so that it actually takes + // into account the current median block size rather than + // the minimum block size. + void init(const std::string& daemon_address = "http://localhost:8080", uint64_t upper_transaction_size_limit = ((CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); bool deinit(); void stop() { m_run.store(false, std::memory_order_relaxed); } @@ -144,7 +149,9 @@ namespace tools a & m_payments; } - static void wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exitst); + 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); @@ -220,6 +227,7 @@ namespace boost namespace tools { + namespace detail { //---------------------------------------------------------------------------------------------------- @@ -326,7 +334,7 @@ namespace tools req.amounts.push_back(it->amount()); } - bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getrandom_outs.bin", req, daemon_resp, m_http_client, 200000); + bool r = epee::net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getrandom_outs.bin", req, daemon_resp, m_http_client, 200000); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getrandom_outs.bin"); THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getrandom_outs.bin"); THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status); @@ -421,7 +429,7 @@ namespace tools COMMAND_RPC_SEND_RAW_TX::request req; req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(tx)); COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp; - r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/sendrawtransaction", req, daemon_send_resp, m_http_client, 200000); + r = epee::net_utils::invoke_http_json_remote_command2(m_daemon_address + "/sendrawtransaction", req, daemon_send_resp, m_http_client, 200000); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "sendrawtransaction"); THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "sendrawtransaction"); THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != CORE_RPC_STATUS_OK, error::tx_rejected, tx, daemon_send_resp.status); diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 0d42dbaf4..71c946545 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -188,6 +188,17 @@ namespace tools std::string to_string() const { return wallet_logic_error::to_string(); } }; + + //---------------------------------------------------------------------------------------------------- + struct invalid_pregenerated_random : public wallet_logic_error + { + explicit invalid_pregenerated_random (std::string&& loc) + : wallet_logic_error(std::move(loc), "invalid pregenerated random for wallet creation/recovery") + { + } + + std::string to_string() const { return wallet_logic_error::to_string(); } + }; //---------------------------------------------------------------------------------------------------- struct refresh_error : public wallet_logic_error { diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index f1766c3b4..a0816946b 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -71,6 +71,21 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + try + { + res.address = m_wallet.get_account().get_public_address_str(); + } + catch (std::exception& e) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = e.what(); + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ 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) { @@ -87,10 +102,37 @@ namespace tools de.amount = it->amount; dsts.push_back(de); } + + std::vector<uint8_t> extra; + if (!req.payment_id.empty()) { + + /* Just to clarify */ + const std::string& payment_id_str = req.payment_id; + + crypto::hash payment_id; + /* Parse 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); + + /* Append Payment ID data into extra */ + if (!cryptonote::add_extra_nonce_to_tx_extra(extra, extra_nonce)) { + er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; + er.message = "Something went wront 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; } @@ -166,4 +208,55 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_incoming_transfers(const wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + if(req.transfer_type.compare("all") != 0 && req.transfer_type.compare("available") != 0 && req.transfer_type.compare("unavailable") != 0) + { + er.code = WALLET_RPC_ERROR_CODE_TRANSFER_TYPE; + er.message = "Transfer type must be one of: all, available, or unavailable"; + return false; + } + + bool filter = false; + bool available = false; + if (req.transfer_type.compare("available") == 0) + { + filter = true; + available = true; + } + else if (req.transfer_type.compare("unavailable") == 0) + { + filter = true; + available = false; + } + + wallet2::transfer_container transfers; + m_wallet.get_transfers(transfers); + + bool transfers_found = false; + for (const auto& td : transfers) + { + if (!filter || available != td.m_spent) + { + if (!transfers_found) + { + transfers_found = true; + } + wallet_rpc::transfer_details rpc_transfers; + rpc_transfers.amount = td.amount(); + rpc_transfers.spent = td.m_spent; + rpc_transfers.global_index = td.m_global_output_index; + rpc_transfers.tx_hash = boost::lexical_cast<std::string>(cryptonote::get_transaction_hash(td.m_tx)); + res.transfers.push_back(rpc_transfers); + } + } + + if (!transfers_found) + { + return false; + } + + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ } diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index db49df574..b8373d27d 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -35,18 +35,22 @@ namespace tools BEGIN_URI_MAP2() BEGIN_JSON_RPC_MAP("/json_rpc") - MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE) - MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER) - MAP_JON_RPC_WE("store", on_store, wallet_rpc::COMMAND_RPC_STORE) - MAP_JON_RPC_WE("get_payments", on_get_payments, wallet_rpc::COMMAND_RPC_GET_PAYMENTS) + MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE) + MAP_JON_RPC_WE("getaddress", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS) + MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER) + MAP_JON_RPC_WE("store", on_store, wallet_rpc::COMMAND_RPC_STORE) + MAP_JON_RPC_WE("get_payments", on_get_payments, wallet_rpc::COMMAND_RPC_GET_PAYMENTS) + MAP_JON_RPC_WE("incoming_transfers", on_incoming_transfers, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS) END_JSON_RPC_MAP() END_URI_MAP2() //json_rpc bool on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er, connection_context& cntx); bool 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); bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_incoming_transfers(const wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::response& res, epee::json_rpc::error& er, connection_context& cntx); bool handle_command_line(const boost::program_options::variables_map& vm); diff --git a/src/wallet/wallet_rpc_server_commans_defs.h b/src/wallet/wallet_rpc_server_commans_defs.h index b99d92ca2..7ffbcfc18 100644 --- a/src/wallet/wallet_rpc_server_commans_defs.h +++ b/src/wallet/wallet_rpc_server_commans_defs.h @@ -34,6 +34,24 @@ namespace wallet_rpc }; }; + struct COMMAND_RPC_GET_ADDRESS + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string address; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(address) + END_KV_SERIALIZE_MAP() + }; + }; + struct trnsfer_destination { uint64_t amount; @@ -52,12 +70,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() }; @@ -121,6 +141,42 @@ namespace wallet_rpc END_KV_SERIALIZE_MAP() }; }; + + struct transfer_details + { + uint64_t amount; + bool spent; + uint64_t global_index; + std::string tx_hash; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amount) + KV_SERIALIZE(spent) + KV_SERIALIZE(global_index) + KV_SERIALIZE(tx_hash) + END_KV_SERIALIZE_MAP() + }; + + struct COMMAND_RPC_INCOMING_TRANSFERS + { + struct request + { + std::string transfer_type; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(transfer_type) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::list<transfer_details> transfers; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(transfers) + END_KV_SERIALIZE_MAP() + }; + }; } } diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index 7fa536dac..a37bde795 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -10,3 +10,4 @@ #define WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY -3 #define WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR -4 #define WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID -5 +#define WALLET_RPC_ERROR_CODE_TRANSFER_TYPE -6 |