diff options
Diffstat (limited to 'src')
124 files changed, 21135 insertions, 440 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 000000000..600413f16 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,44 @@ +file(GLOB_RECURSE COMMON common/*) +file(GLOB_RECURSE CRYPTO crypto/*) +file(GLOB_RECURSE CRYPTONOTE_CORE cryptonote_core/*) +file(GLOB_RECURSE CRYPTONOTE_PROTOCOL cryptonote_protocol/*) +file(GLOB_RECURSE DAEMON daemon/*) +file(GLOB_RECURSE P2P p2p/*) +file(GLOB_RECURSE RPC rpc/*) +file(GLOB_RECURSE SIMPLEWALLET simplewallet/*) +file(GLOB_RECURSE CONN_TOOL connectivity_tool/*) +file(GLOB_RECURSE WALLET wallet/*) +file(GLOB_RECURSE MINER miner/*) + +source_group(common FILES ${COMMON}) +source_group(crypto FILES ${CRYPTO}) +source_group(cryptonote_core FILES ${CRYPTONOTE_CORE}) +source_group(cryptonote_protocol FILES ${CRYPTONOTE_PROTOCOL}) +source_group(daemon FILES ${DAEMON}) +source_group(p2p FILES ${P2P}) +source_group(rpc FILES ${RPC}) +source_group(simplewallet FILES ${SIMPLEWALLET}) +source_group(connectivity-tool FILES ${CONN_TOOL}) +source_group(wallet FILES ${WALLET}) +source_group(simpleminer FILES ${MINER}) + +add_library(common ${COMMON}) +add_library(crypto ${CRYPTO}) +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 ${Boost_LIBRARIES}) +target_link_libraries(connectivity_tool cryptonote_core crypto common ${Boost_LIBRARIES}) +target_link_libraries(simpleminer cryptonote_core crypto common ${Boost_LIBRARIES}) +add_dependencies(daemon version) +add_library(rpc ${RPC}) +add_library(wallet ${WALLET}) +add_executable(simplewallet ${SIMPLEWALLET} ) +target_link_libraries(simplewallet wallet rpc cryptonote_core crypto common ${Boost_LIBRARIES}) +add_dependencies(simplewallet version) +add_dependencies(daemon version) + +set_property(TARGET common crypto cryptonote_core rpc wallet PROPERTY FOLDER "libs") +set_property(TARGET daemon simplewallet connectivity_tool simpleminer PROPERTY FOLDER "prog") +set_property(TARGET daemon PROPERTY OUTPUT_NAME "bytecoind") diff --git a/src/common/base58.cpp b/src/common/base58.cpp new file mode 100644 index 000000000..30042eeba --- /dev/null +++ b/src/common/base58.cpp @@ -0,0 +1,246 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "base58.h" + +#include <assert.h> +#include <string> +#include <vector> + +#include "crypto/hash.h" +#include "int-util.h" +#include "util.h" +#include "varint.h" + +namespace tools +{ + namespace base58 + { + namespace + { + const char alphabet[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + const size_t alphabet_size = sizeof(alphabet) - 1; + const size_t encoded_block_sizes[] = {0, 2, 3, 5, 6, 7, 9, 10, 11}; + const size_t full_block_size = sizeof(encoded_block_sizes) / sizeof(encoded_block_sizes[0]) - 1; + const size_t full_encoded_block_size = encoded_block_sizes[full_block_size]; + const size_t addr_checksum_size = 4; + + struct reverse_alphabet + { + reverse_alphabet() + { + m_data.resize(alphabet[alphabet_size - 1] - alphabet[0] + 1, -1); + + for (size_t i = 0; i < alphabet_size; ++i) + { + size_t idx = static_cast<size_t>(alphabet[i] - alphabet[0]); + m_data[idx] = static_cast<int8_t>(i); + } + } + + int operator()(char letter) const + { + size_t idx = static_cast<size_t>(letter - alphabet[0]); + return idx < m_data.size() ? m_data[idx] : -1; + } + + static reverse_alphabet instance; + + private: + std::vector<int8_t> m_data; + }; + + reverse_alphabet reverse_alphabet::instance; + + struct decoded_block_sizes + { + decoded_block_sizes() + { + m_data.resize(encoded_block_sizes[full_block_size] + 1, -1); + for (size_t i = 0; i <= full_block_size; ++i) + { + m_data[encoded_block_sizes[i]] = static_cast<int>(i); + } + } + + int operator()(size_t encoded_block_size) const + { + assert(encoded_block_size <= full_encoded_block_size); + return m_data[encoded_block_size]; + } + + static decoded_block_sizes instance; + + private: + std::vector<int> m_data; + }; + + decoded_block_sizes decoded_block_sizes::instance; + + uint64_t uint_8be_to_64(const uint8_t* data, size_t size) + { + assert(1 <= size && size <= sizeof(uint64_t)); + + uint64_t res = 0; + switch (9 - size) + { + case 1: res |= *data++; + case 2: res <<= 8; res |= *data++; + case 3: res <<= 8; res |= *data++; + case 4: res <<= 8; res |= *data++; + case 5: res <<= 8; res |= *data++; + case 6: res <<= 8; res |= *data++; + case 7: res <<= 8; res |= *data++; + case 8: res <<= 8; res |= *data; break; + default: assert(false); + } + + return res; + } + + void uint_64_to_8be(uint64_t num, size_t size, uint8_t* data) + { + assert(1 <= size && size <= sizeof(uint64_t)); + + uint64_t num_be = SWAP64BE(num); + memcpy(data, reinterpret_cast<uint8_t*>(&num_be) + sizeof(uint64_t) - size, size); + } + + void encode_block(const char* block, size_t size, char* res) + { + assert(1 <= size && size <= sizeof(full_block_size)); + + uint64_t num = uint_8be_to_64(reinterpret_cast<const uint8_t*>(block), size); + int i = static_cast<int>(encoded_block_sizes[size]) - 1; + while (0 < num) + { + uint64_t remainder = num % alphabet_size; + num /= alphabet_size; + res[i] = alphabet[remainder]; + --i; + } + } + + bool decode_block(const char* block, size_t size, char* res) + { + assert(1 <= size && size <= full_encoded_block_size); + + int res_size = decoded_block_sizes::instance(size); + if (res_size <= 0) + return false; // Invalid block size + + uint64_t res_num = 0; + uint64_t order = 1; + for (size_t i = size - 1; i < size; --i) + { + int digit = reverse_alphabet::instance(block[i]); + if (digit < 0) + return false; // Invalid symbol + + uint64_t product_hi; + uint64_t tmp = res_num + mul128(order, digit, &product_hi); + if (tmp < res_num || 0 != product_hi) + return false; // Overflow + + res_num = tmp; + order *= alphabet_size; // Never overflows, 58^10 < 2^64 + } + + if (static_cast<size_t>(res_size) < full_block_size && (UINT64_C(1) << (8 * res_size)) <= res_num) + return false; // Overflow + + uint_64_to_8be(res_num, res_size, reinterpret_cast<uint8_t*>(res)); + + return true; + } + } + + std::string encode(const std::string& data) + { + if (data.empty()) + return std::string(); + + size_t full_block_count = data.size() / full_block_size; + size_t last_block_size = data.size() % full_block_size; + size_t res_size = full_block_count * full_encoded_block_size + encoded_block_sizes[last_block_size]; + + std::string res(res_size, alphabet[0]); + for (size_t i = 0; i < full_block_count; ++i) + { + encode_block(data.data() + i * full_block_size, full_block_size, &res[i * full_encoded_block_size]); + } + + if (0 < last_block_size) + { + encode_block(data.data() + full_block_count * full_block_size, last_block_size, &res[full_block_count * full_encoded_block_size]); + } + + return res; + } + + bool decode(const std::string& enc, std::string& data) + { + if (enc.empty()) + { + data.clear(); + return true; + } + + size_t full_block_count = enc.size() / full_encoded_block_size; + size_t last_block_size = enc.size() % full_encoded_block_size; + int last_block_decoded_size = decoded_block_sizes::instance(last_block_size); + if (last_block_decoded_size < 0) + return false; // Invalid enc length + size_t data_size = full_block_count * full_block_size + last_block_decoded_size; + + data.resize(data_size, 0); + for (size_t i = 0; i < full_block_count; ++i) + { + if (!decode_block(enc.data() + i * full_encoded_block_size, full_encoded_block_size, &data[i * full_block_size])) + return false; + } + + if (0 < last_block_size) + { + if (!decode_block(enc.data() + full_block_count * full_encoded_block_size, last_block_size, + &data[full_block_count * full_block_size])) + return false; + } + + return true; + } + + std::string encode_addr(uint64_t tag, const std::string& data) + { + std::string buf = get_varint_data(tag); + buf += data; + crypto::hash hash = crypto::cn_fast_hash(buf.data(), buf.size()); + const char* hash_data = reinterpret_cast<const char*>(&hash); + buf.append(hash_data, addr_checksum_size); + return encode(buf); + } + + bool decode_addr(std::string addr, uint64_t& tag, std::string& data) + { + std::string addr_data; + bool r = decode(addr, addr_data); + if (!r) return false; + + std::string checksum(addr_checksum_size, '\0'); + checksum = addr_data.substr(addr_data.size() - addr_checksum_size); + + addr_data.resize(addr_data.size() - addr_checksum_size); + crypto::hash hash = crypto::cn_fast_hash(addr_data.data(), addr_data.size()); + std::string expected_checksum(reinterpret_cast<const char*>(&hash), addr_checksum_size); + if (expected_checksum != checksum) return false; + + int read = tools::read_varint(addr_data.begin(), addr_data.end(), tag); + if (read <= 0) return false; + + data = addr_data.substr(read); + return true; + } + } +} diff --git a/src/common/base58.h b/src/common/base58.h new file mode 100644 index 000000000..4055f62ba --- /dev/null +++ b/src/common/base58.h @@ -0,0 +1,20 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <cstdint> +#include <string> + +namespace tools +{ + namespace base58 + { + std::string encode(const std::string& data); + bool decode(const std::string& enc, std::string& data); + + std::string encode_addr(uint64_t tag, const std::string& data); + bool decode_addr(std::string addr, uint64_t& tag, std::string& data); + } +} diff --git a/src/common/boost_serialization_helper.h b/src/common/boost_serialization_helper.h new file mode 100644 index 000000000..74016ae75 --- /dev/null +++ b/src/common/boost_serialization_helper.h @@ -0,0 +1,44 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <boost/archive/binary_oarchive.hpp> +#include <boost/archive/binary_iarchive.hpp> + + +namespace tools +{ + template<class t_object> + bool serialize_obj_to_file(t_object& obj, const std::string& file_path) + { + TRY_ENTRY(); + std::ofstream data_file; + data_file.open( file_path , std::ios_base::binary | std::ios_base::out| std::ios::trunc); + if(data_file.fail()) + return false; + + boost::archive::binary_oarchive a(data_file); + a << obj; + + return !data_file.fail(); + CATCH_ENTRY_L0("serialize_obj_to_file", false); + } + + template<class t_object> + bool unserialize_obj_from_file(t_object& obj, const std::string& file_path) + { + TRY_ENTRY(); + + std::ifstream data_file; + data_file.open( file_path, std::ios_base::binary | std::ios_base::in); + if(data_file.fail()) + return false; + boost::archive::binary_iarchive a(data_file); + + a >> obj; + return !data_file.fail(); + CATCH_ENTRY_L0("unserialize_obj_from_file", false); + } +} diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp new file mode 100644 index 000000000..0b90345d9 --- /dev/null +++ b/src/common/command_line.cpp @@ -0,0 +1,12 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "command_line.h" + +namespace command_line +{ + 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 new file mode 100644 index 000000000..860653772 --- /dev/null +++ b/src/common/command_line.h @@ -0,0 +1,177 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <iostream> +#include <type_traits> + +#include <boost/program_options/parsers.hpp> +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/variables_map.hpp> +#include "include_base_utils.h" + +namespace command_line +{ + template<typename T, bool required = false> + struct arg_descriptor; + + template<typename T> + struct arg_descriptor<T, false> + { + typedef T value_type; + + const char* name; + const char* description; + T default_value; + bool not_use_default; + }; + + template<typename T> + struct arg_descriptor<std::vector<T>, false> + { + typedef std::vector<T> value_type; + + const char* name; + const char* description; + }; + + template<typename T> + struct arg_descriptor<T, true> + { + static_assert(!std::is_same<T, bool>::value, "Boolean switch can't be required"); + + typedef T value_type; + + const char* name; + const char* description; + }; + + template<typename T> + boost::program_options::typed_value<T, char>* make_semantic(const arg_descriptor<T, true>& /*arg*/) + { + return boost::program_options::value<T>()->required(); + } + + template<typename T> + boost::program_options::typed_value<T, char>* make_semantic(const arg_descriptor<T, false>& arg) + { + auto semantic = boost::program_options::value<T>(); + if (!arg.not_use_default) + semantic->default_value(arg.default_value); + return semantic; + } + + template<typename T> + boost::program_options::typed_value<T, char>* make_semantic(const arg_descriptor<T, false>& arg, const T& def) + { + auto semantic = boost::program_options::value<T>(); + if (!arg.not_use_default) + semantic->default_value(def); + return semantic; + } + + template<typename T> + boost::program_options::typed_value<std::vector<T>, char>* make_semantic(const arg_descriptor<std::vector<T>, false>& /*arg*/) + { + auto semantic = boost::program_options::value< std::vector<T> >(); + semantic->default_value(std::vector<T>(), ""); + return semantic; + } + + template<typename T, bool required> + void add_arg(boost::program_options::options_description& description, const arg_descriptor<T, required>& arg, bool unique = true) + { + if (0 != description.find_nothrow(arg.name, false)) + { + CHECK_AND_ASSERT_MES(!unique, void(), "Argument already exists: " << arg.name); + return; + } + + description.add_options()(arg.name, make_semantic(arg), arg.description); + } + + template<typename T> + void add_arg(boost::program_options::options_description& description, const arg_descriptor<T, false>& arg, const T& def, bool unique = true) + { + if (0 != description.find_nothrow(arg.name, false)) + { + CHECK_AND_ASSERT_MES(!unique, void(), "Argument already exists: " << arg.name); + return; + } + + description.add_options()(arg.name, make_semantic(arg, def), arg.description); + } + + template<> + inline void add_arg(boost::program_options::options_description& description, const arg_descriptor<bool, false>& arg, bool unique) + { + if (0 != description.find_nothrow(arg.name, false)) + { + CHECK_AND_ASSERT_MES(!unique, void(), "Argument already exists: " << arg.name); + return; + } + + description.add_options()(arg.name, boost::program_options::bool_switch(), arg.description); + } + + template<typename charT> + boost::program_options::basic_parsed_options<charT> parse_command_line(int argc, const charT* const argv[], + const boost::program_options::options_description& desc, bool allow_unregistered = false) + { + auto parser = boost::program_options::command_line_parser(argc, argv); + parser.options(desc); + if (allow_unregistered) + { + parser.allow_unregistered(); + } + return parser.run(); + } + + template<typename F> + bool handle_error_helper(const boost::program_options::options_description& desc, F parser) + { + try + { + return parser(); + } + catch (std::exception& e) + { + std::cerr << "Failed to parse arguments: " << e.what() << std::endl; + std::cerr << desc << std::endl; + return false; + } + catch (...) + { + std::cerr << "Failed to parse arguments: unknown exception" << std::endl; + std::cerr << desc << std::endl; + return false; + } + } + + template<typename T, bool required> + bool has_arg(const boost::program_options::variables_map& vm, const arg_descriptor<T, required>& arg) + { + auto value = vm[arg.name]; + return !value.empty(); + } + + + template<typename T, bool required> + T get_arg(const boost::program_options::variables_map& vm, const arg_descriptor<T, required>& arg) + { + return vm[arg.name].template as<T>(); + } + + template<> + inline bool has_arg<bool, false>(const boost::program_options::variables_map& vm, const arg_descriptor<bool, false>& arg) + { + return get_arg<bool, false>(vm, arg); + } + + + extern const arg_descriptor<bool> arg_help; + extern const arg_descriptor<bool> arg_version; + extern const arg_descriptor<std::string> arg_data_dir; +} diff --git a/src/common/int-util.h b/src/common/int-util.h new file mode 100644 index 000000000..ad0ef60e0 --- /dev/null +++ b/src/common/int-util.h @@ -0,0 +1,205 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <assert.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include <sys/param.h> + +#if defined(_MSC_VER) +#include <stdlib.h> + +static inline uint32_t rol32(uint32_t x, int r) { + static_assert(sizeof(uint32_t) == sizeof(unsigned int), "this code assumes 32-bit integers"); + return _rotl(x, r); +} + +static inline uint64_t rol64(uint64_t x, int r) { + return _rotl64(x, r); +} + +#else + +static inline uint32_t rol32(uint32_t x, int r) { + return (x << (r & 31)) | (x >> (-r & 31)); +} + +static inline uint64_t rol64(uint64_t x, int r) { + return (x << (r & 63)) | (x >> (-r & 63)); +} + +#endif + +inline uint64_t hi_dword(uint64_t val) { + return val >> 32; +} + +inline uint64_t lo_dword(uint64_t val) { + return val & 0xFFFFFFFF; +} + +inline uint64_t mul128(uint64_t multiplier, uint64_t multiplicand, uint64_t* product_hi) { + // multiplier = ab = a * 2^32 + b + // multiplicand = cd = c * 2^32 + d + // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d + uint64_t a = hi_dword(multiplier); + uint64_t b = lo_dword(multiplier); + uint64_t c = hi_dword(multiplicand); + uint64_t d = lo_dword(multiplicand); + + uint64_t ac = a * c; + uint64_t ad = a * d; + uint64_t bc = b * c; + uint64_t bd = b * d; + + uint64_t adbc = ad + bc; + uint64_t adbc_carry = adbc < ad ? 1 : 0; + + // multiplier * multiplicand = product_hi * 2^64 + product_lo + uint64_t product_lo = bd + (adbc << 32); + uint64_t product_lo_carry = product_lo < bd ? 1 : 0; + *product_hi = ac + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry; + assert(ac <= *product_hi); + + return product_lo; +} + +inline uint64_t div_with_reminder(uint64_t dividend, uint32_t divisor, uint32_t* remainder) { + dividend |= ((uint64_t)*remainder) << 32; + *remainder = dividend % divisor; + return dividend / divisor; +} + +// Long division with 2^32 base +inline uint32_t div128_32(uint64_t dividend_hi, uint64_t dividend_lo, uint32_t divisor, uint64_t* quotient_hi, uint64_t* quotient_lo) { + uint64_t dividend_dwords[4]; + uint32_t remainder = 0; + + dividend_dwords[3] = hi_dword(dividend_hi); + dividend_dwords[2] = lo_dword(dividend_hi); + dividend_dwords[1] = hi_dword(dividend_lo); + dividend_dwords[0] = lo_dword(dividend_lo); + + *quotient_hi = div_with_reminder(dividend_dwords[3], divisor, &remainder) << 32; + *quotient_hi |= div_with_reminder(dividend_dwords[2], divisor, &remainder); + *quotient_lo = div_with_reminder(dividend_dwords[1], divisor, &remainder) << 32; + *quotient_lo |= div_with_reminder(dividend_dwords[0], divisor, &remainder); + + return remainder; +} + +#define IDENT32(x) ((uint32_t) (x)) +#define IDENT64(x) ((uint64_t) (x)) + +#define SWAP32(x) ((((uint32_t) (x) & 0x000000ff) << 24) | \ + (((uint32_t) (x) & 0x0000ff00) << 8) | \ + (((uint32_t) (x) & 0x00ff0000) >> 8) | \ + (((uint32_t) (x) & 0xff000000) >> 24)) +#define SWAP64(x) ((((uint64_t) (x) & 0x00000000000000ff) << 56) | \ + (((uint64_t) (x) & 0x000000000000ff00) << 40) | \ + (((uint64_t) (x) & 0x0000000000ff0000) << 24) | \ + (((uint64_t) (x) & 0x00000000ff000000) << 8) | \ + (((uint64_t) (x) & 0x000000ff00000000) >> 8) | \ + (((uint64_t) (x) & 0x0000ff0000000000) >> 24) | \ + (((uint64_t) (x) & 0x00ff000000000000) >> 40) | \ + (((uint64_t) (x) & 0xff00000000000000) >> 56)) + +static inline uint32_t ident32(uint32_t x) { return x; } +static inline uint64_t ident64(uint64_t x) { return x; } + +static inline uint32_t swap32(uint32_t x) { + x = ((x & 0x00ff00ff) << 8) | ((x & 0xff00ff00) >> 8); + return (x << 16) | (x >> 16); +} +static inline uint64_t swap64(uint64_t x) { + x = ((x & 0x00ff00ff00ff00ff) << 8) | ((x & 0xff00ff00ff00ff00) >> 8); + x = ((x & 0x0000ffff0000ffff) << 16) | ((x & 0xffff0000ffff0000) >> 16); + return (x << 32) | (x >> 32); +} + +#if defined(__GNUC__) +#define UNUSED __attribute__((unused)) +#else +#define UNUSED +#endif +static inline void mem_inplace_ident(void *mem UNUSED, size_t n UNUSED) { } +#undef UNUSED + +static inline void mem_inplace_swap32(void *mem, size_t n) { + size_t i; + for (i = 0; i < n; i++) { + ((uint32_t *) mem)[i] = swap32(((const uint32_t *) mem)[i]); + } +} +static inline void mem_inplace_swap64(void *mem, size_t n) { + size_t i; + for (i = 0; i < n; i++) { + ((uint64_t *) mem)[i] = swap64(((const uint64_t *) mem)[i]); + } +} + +static inline void memcpy_ident32(void *dst, const void *src, size_t n) { + memcpy(dst, src, 4 * n); +} +static inline void memcpy_ident64(void *dst, const void *src, size_t n) { + memcpy(dst, src, 8 * n); +} + +static inline void memcpy_swap32(void *dst, const void *src, size_t n) { + size_t i; + for (i = 0; i < n; i++) { + ((uint32_t *) dst)[i] = swap32(((const uint32_t *) src)[i]); + } +} +static inline void memcpy_swap64(void *dst, const void *src, size_t n) { + size_t i; + for (i = 0; i < n; i++) { + ((uint64_t *) dst)[i] = swap64(((const uint64_t *) src)[i]); + } +} + +#if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN) || !defined(BIG_ENDIAN) +static_assert(false, "BYTE_ORDER is undefined. Perhaps, GNU extensions are not enabled"); +#endif + +#if BYTE_ORDER == LITTLE_ENDIAN +#define SWAP32LE IDENT32 +#define SWAP32BE SWAP32 +#define swap32le ident32 +#define swap32be swap32 +#define mem_inplace_swap32le mem_inplace_ident +#define mem_inplace_swap32be mem_inplace_swap32 +#define memcpy_swap32le memcpy_ident32 +#define memcpy_swap32be memcpy_swap32 +#define SWAP64LE IDENT64 +#define SWAP64BE SWAP64 +#define swap64le ident64 +#define swap64be swap64 +#define mem_inplace_swap64le mem_inplace_ident +#define mem_inplace_swap64be mem_inplace_swap64 +#define memcpy_swap64le memcpy_ident64 +#define memcpy_swap64be memcpy_swap64 +#endif + +#if BYTE_ORDER == BIG_ENDIAN +#define SWAP32BE IDENT32 +#define SWAP32LE SWAP32 +#define swap32be ident32 +#define swap32le swap32 +#define mem_inplace_swap32be mem_inplace_ident +#define mem_inplace_swap32le mem_inplace_swap32 +#define memcpy_swap32be memcpy_ident32 +#define memcpy_swap32le memcpy_swap32 +#define SWAP64BE IDENT64 +#define SWAP64LE SWAP64 +#define swap64be ident64 +#define swap64le swap64 +#define mem_inplace_swap64be mem_inplace_ident +#define mem_inplace_swap64le mem_inplace_swap64 +#define memcpy_swap64be memcpy_ident64 +#define memcpy_swap64le memcpy_swap64 +#endif diff --git a/src/common/pod-class.h b/src/common/pod-class.h new file mode 100644 index 000000000..c07edb208 --- /dev/null +++ b/src/common/pod-class.h @@ -0,0 +1,11 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#if defined(_MSC_VER) +#define POD_CLASS struct +#else +#define POD_CLASS class +#endif diff --git a/src/common/unordered_containers_boost_serialization.h b/src/common/unordered_containers_boost_serialization.h new file mode 100644 index 000000000..0804660ca --- /dev/null +++ b/src/common/unordered_containers_boost_serialization.h @@ -0,0 +1,80 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <boost/serialization/split_free.hpp> +#include <unordered_map> +#include <unordered_set> + +namespace boost +{ + namespace serialization + { + template <class Archive, class h_key, class hval> + inline void save(Archive &a, const std::unordered_map<h_key, hval> &x, const boost::serialization::version_type ver) + { + size_t s = x.size(); + a << s; + BOOST_FOREACH(auto& v, x) + { + a << v.first; + a << v.second; + } + } + + template <class Archive, class h_key, class hval> + inline void load(Archive &a, std::unordered_map<h_key, hval> &x, const boost::serialization::version_type ver) + { + size_t s = 0; + a >> s; + for(size_t i = 0; i != s; i++) + { + h_key k; + hval v; + a >> k; + a >> v; + x.insert(std::pair<h_key, hval>(k, v)); + } + } + + + template <class Archive, class hval> + inline void save(Archive &a, const std::unordered_set<hval> &x, const boost::serialization::version_type ver) + { + size_t s = x.size(); + a << s; + BOOST_FOREACH(auto& v, x) + { + a << v; + } + } + + template <class Archive, class hval> + inline void load(Archive &a, std::unordered_set<hval> &x, const boost::serialization::version_type ver) + { + size_t s = 0; + a >> s; + for(size_t i = 0; i != s; i++) + { + hval v; + a >> v; + x.insert(v); + } + } + + + template <class Archive, class h_key, class hval> + inline void serialize(Archive &a, std::unordered_map<h_key, hval> &x, const boost::serialization::version_type ver) + { + split_free(a, x, ver); + } + + template <class Archive, class hval> + inline void serialize(Archive &a, std::unordered_set<hval> &x, const boost::serialization::version_type ver) + { + split_free(a, x, ver); + } + } +} diff --git a/src/common/util.cpp b/src/common/util.cpp new file mode 100644 index 000000000..b24016cc3 --- /dev/null +++ b/src/common/util.cpp @@ -0,0 +1,363 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <cstdio> + +#include "include_base_utils.h" +using namespace epee; + +#include "util.h" +#include "cryptonote_config.h" + +#ifdef WIN32 +#include <windows.h> +#include <shlobj.h> +#include <strsafe.h> +#else +#include <sys/utsname.h> +#endif + + +namespace tools +{ + +#ifdef WIN32 + std::string get_windows_version_display_string() + { + typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO); + typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD); +#define BUFSIZE 10000 + + char pszOS[BUFSIZE] = {0}; + OSVERSIONINFOEX osvi; + SYSTEM_INFO si; + PGNSI pGNSI; + PGPI pGPI; + BOOL bOsVersionInfoEx; + DWORD dwType; + + ZeroMemory(&si, sizeof(SYSTEM_INFO)); + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); + + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO*) &osvi); + + if(!bOsVersionInfoEx) return pszOS; + + // Call GetNativeSystemInfo if supported or GetSystemInfo otherwise. + + pGNSI = (PGNSI) GetProcAddress( + GetModuleHandle(TEXT("kernel32.dll")), + "GetNativeSystemInfo"); + if(NULL != pGNSI) + pGNSI(&si); + else GetSystemInfo(&si); + + if ( VER_PLATFORM_WIN32_NT==osvi.dwPlatformId && + osvi.dwMajorVersion > 4 ) + { + StringCchCopy(pszOS, BUFSIZE, TEXT("Microsoft ")); + + // Test for the specific product. + + if ( osvi.dwMajorVersion == 6 ) + { + if( osvi.dwMinorVersion == 0 ) + { + if( osvi.wProductType == VER_NT_WORKSTATION ) + StringCchCat(pszOS, BUFSIZE, TEXT("Windows Vista ")); + else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2008 " )); + } + + if ( osvi.dwMinorVersion == 1 ) + { + if( osvi.wProductType == VER_NT_WORKSTATION ) + StringCchCat(pszOS, BUFSIZE, TEXT("Windows 7 ")); + else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2008 R2 " )); + } + + pGPI = (PGPI) GetProcAddress( + GetModuleHandle(TEXT("kernel32.dll")), + "GetProductInfo"); + + pGPI( osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType); + + switch( dwType ) + { + case PRODUCT_ULTIMATE: + StringCchCat(pszOS, BUFSIZE, TEXT("Ultimate Edition" )); + break; + case PRODUCT_PROFESSIONAL: + StringCchCat(pszOS, BUFSIZE, TEXT("Professional" )); + break; + case PRODUCT_HOME_PREMIUM: + StringCchCat(pszOS, BUFSIZE, TEXT("Home Premium Edition" )); + break; + case PRODUCT_HOME_BASIC: + StringCchCat(pszOS, BUFSIZE, TEXT("Home Basic Edition" )); + break; + case PRODUCT_ENTERPRISE: + StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition" )); + break; + case PRODUCT_BUSINESS: + StringCchCat(pszOS, BUFSIZE, TEXT("Business Edition" )); + break; + case PRODUCT_STARTER: + StringCchCat(pszOS, BUFSIZE, TEXT("Starter Edition" )); + break; + case PRODUCT_CLUSTER_SERVER: + StringCchCat(pszOS, BUFSIZE, TEXT("Cluster Server Edition" )); + break; + case PRODUCT_DATACENTER_SERVER: + StringCchCat(pszOS, BUFSIZE, TEXT("Datacenter Edition" )); + break; + case PRODUCT_DATACENTER_SERVER_CORE: + StringCchCat(pszOS, BUFSIZE, TEXT("Datacenter Edition (core installation)" )); + break; + case PRODUCT_ENTERPRISE_SERVER: + StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition" )); + break; + case PRODUCT_ENTERPRISE_SERVER_CORE: + StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition (core installation)" )); + break; + case PRODUCT_ENTERPRISE_SERVER_IA64: + StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition for Itanium-based Systems" )); + break; + case PRODUCT_SMALLBUSINESS_SERVER: + StringCchCat(pszOS, BUFSIZE, TEXT("Small Business Server" )); + break; + case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM: + StringCchCat(pszOS, BUFSIZE, TEXT("Small Business Server Premium Edition" )); + break; + case PRODUCT_STANDARD_SERVER: + StringCchCat(pszOS, BUFSIZE, TEXT("Standard Edition" )); + break; + case PRODUCT_STANDARD_SERVER_CORE: + StringCchCat(pszOS, BUFSIZE, TEXT("Standard Edition (core installation)" )); + break; + case PRODUCT_WEB_SERVER: + StringCchCat(pszOS, BUFSIZE, TEXT("Web Server Edition" )); + break; + } + } + + if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2 ) + { + if( GetSystemMetrics(SM_SERVERR2) ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Server 2003 R2, ")); + else if ( osvi.wSuiteMask & VER_SUITE_STORAGE_SERVER ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Storage Server 2003")); + else if ( osvi.wSuiteMask & VER_SUITE_WH_SERVER ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Home Server")); + else if( osvi.wProductType == VER_NT_WORKSTATION && + si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64) + { + StringCchCat(pszOS, BUFSIZE, TEXT( "Windows XP Professional x64 Edition")); + } + else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2003, ")); + + // Test for the server type. + if ( osvi.wProductType != VER_NT_WORKSTATION ) + { + if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_IA64 ) + { + if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Edition for Itanium-based Systems" )); + else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise Edition for Itanium-based Systems" )); + } + + else if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 ) + { + if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter x64 Edition" )); + else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise x64 Edition" )); + else StringCchCat(pszOS, BUFSIZE, TEXT( "Standard x64 Edition" )); + } + + else + { + if ( osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Compute Cluster Edition" )); + else if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Edition" )); + else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise Edition" )); + else if ( osvi.wSuiteMask & VER_SUITE_BLADE ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Web Edition" )); + else StringCchCat(pszOS, BUFSIZE, TEXT( "Standard Edition" )); + } + } + } + + if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 ) + { + StringCchCat(pszOS, BUFSIZE, TEXT("Windows XP ")); + if( osvi.wSuiteMask & VER_SUITE_PERSONAL ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Home Edition" )); + else StringCchCat(pszOS, BUFSIZE, TEXT( "Professional" )); + } + + if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 ) + { + StringCchCat(pszOS, BUFSIZE, TEXT("Windows 2000 ")); + + if ( osvi.wProductType == VER_NT_WORKSTATION ) + { + StringCchCat(pszOS, BUFSIZE, TEXT( "Professional" )); + } + else + { + if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Server" )); + else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Advanced Server" )); + else StringCchCat(pszOS, BUFSIZE, TEXT( "Server" )); + } + } + + // Include service pack (if any) and build number. + + if( strlen(osvi.szCSDVersion) > 0 ) + { + StringCchCat(pszOS, BUFSIZE, TEXT(" ") ); + StringCchCat(pszOS, BUFSIZE, osvi.szCSDVersion); + } + + TCHAR buf[80]; + + StringCchPrintf( buf, 80, TEXT(" (build %d)"), osvi.dwBuildNumber); + StringCchCat(pszOS, BUFSIZE, buf); + + if ( osvi.dwMajorVersion >= 6 ) + { + if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 ) + StringCchCat(pszOS, BUFSIZE, TEXT( ", 64-bit" )); + else if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_INTEL ) + StringCchCat(pszOS, BUFSIZE, TEXT(", 32-bit")); + } + + return pszOS; + } + else + { + printf( "This sample does not support this version of Windows.\n"); + return pszOS; + } + } +#else +std::string get_nix_version_display_string() +{ + utsname un; + + if(uname(&un) < 0) + return std::string("*nix: failed to get os version"); + return std::string() + un.sysname + " " + un.version + " " + un.release; +} +#endif + + + + std::string get_os_version_string() + { +#ifdef WIN32 + return get_windows_version_display_string(); +#else + return get_nix_version_display_string(); +#endif + } + + + +#ifdef WIN32 + std::string get_special_folder_path(int nfolder, bool iscreate) + { + namespace fs = boost::filesystem; + char psz_path[MAX_PATH] = ""; + + if(SHGetSpecialFolderPathA(NULL, psz_path, nfolder, iscreate)) + { + return psz_path; + } + + LOG_ERROR("SHGetSpecialFolderPathA() failed, could not obtain requested path."); + return ""; + } +#endif + + std::string get_default_data_dir() + { + //namespace fs = boost::filesystem; + // Windows < Vista: C:\Documents and Settings\Username\Application Data\CRYPTONOTE_NAME + // Windows >= Vista: C:\Users\Username\AppData\Roaming\CRYPTONOTE_NAME + // Mac: ~/Library/Application Support/CRYPTONOTE_NAME + // Unix: ~/.CRYPTONOTE_NAME + std::string config_folder; +#ifdef WIN32 + // Windows + config_folder = get_special_folder_path(CSIDL_APPDATA, true) + "/" + CRYPTONOTE_NAME; +#else + std::string pathRet; + char* pszHome = getenv("HOME"); + if (pszHome == NULL || strlen(pszHome) == 0) + pathRet = "/"; + else + pathRet = pszHome; +#ifdef MAC_OSX + // Mac + pathRet /= "Library/Application Support"; + config_folder = (pathRet + "/" + CRYPTONOTE_NAME); +#else + // Unix + config_folder = (pathRet + "/." + CRYPTONOTE_NAME); +#endif +#endif + + return config_folder; + } + + bool create_directories_if_necessary(const std::string& path) + { + namespace fs = boost::filesystem; + boost::system::error_code ec; + fs::path fs_path(path); + if (fs::is_directory(fs_path, ec)) + { + return true; + } + + bool res = fs::create_directories(fs_path, ec); + if (res) + { + LOG_PRINT_L2("Created directory: " << path); + } + else + { + LOG_PRINT_L2("Can't create directory: " << path << ", err: "<< ec.message()); + } + + return res; + } + + std::error_code replace_file(const std::string& replacement_name, const std::string& replaced_name) + { + int code; +#if defined(WIN32) + // Maximizing chances for success + DWORD attributes = ::GetFileAttributes(replaced_name.c_str()); + if (INVALID_FILE_ATTRIBUTES != attributes) + { + ::SetFileAttributes(replaced_name.c_str(), attributes & (~FILE_ATTRIBUTE_READONLY)); + } + + bool ok = 0 != ::MoveFileEx(replacement_name.c_str(), replaced_name.c_str(), MOVEFILE_REPLACE_EXISTING); + code = ok ? 0 : static_cast<int>(::GetLastError()); +#else + bool ok = 0 == std::rename(replacement_name.c_str(), replaced_name.c_str()); + code = ok ? 0 : errno; +#endif + return std::error_code(code, std::system_category()); + } +} diff --git a/src/common/util.h b/src/common/util.h new file mode 100644 index 000000000..a29a30fff --- /dev/null +++ b/src/common/util.h @@ -0,0 +1,29 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <system_error> +#include <boost/filesystem.hpp> + +#include "crypto/crypto.h" +#include "crypto/hash.h" +#include "misc_language.h" +#include "p2p/p2p_protocol_defs.h" + +namespace tools +{ + std::string get_default_data_dir(); + std::string get_os_version_string(); + bool create_directories_if_necessary(const std::string& path); + std::error_code replace_file(const std::string& replacement_name, const std::string& replaced_name); + + inline crypto::hash get_proof_of_trust_hash(const nodetool::proof_of_trust& pot) + { + std::string s; + s.append(reinterpret_cast<const char*>(&pot.peer_id), sizeof(pot.peer_id)); + s.append(reinterpret_cast<const char*>(&pot.time), sizeof(pot.time)); + return crypto::cn_fast_hash(s.data(), s.size()); + } +} diff --git a/src/common/varint.h b/src/common/varint.h new file mode 100644 index 000000000..e62470fdf --- /dev/null +++ b/src/common/varint.h @@ -0,0 +1,62 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <limits> +#include <type_traits> +#include <utility> +#include <sstream> +#include <string> + +namespace tools { + + template<typename OutputIt, typename T> + typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, void>::type + write_varint(OutputIt &&dest, T i) { + while (i >= 0x80) { + *dest++ = (static_cast<char>(i) & 0x7f) | 0x80; + i >>= 7; + } + *dest++ = static_cast<char>(i); + } + + template<typename t_type> + std::string get_varint_data(const t_type& v) + { + std::stringstream ss; + write_varint(std::ostreambuf_iterator<char>(ss), v); + return ss.str(); + } + + template<int bits, typename InputIt, typename T> + typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value && 0 <= bits && bits <= std::numeric_limits<T>::digits, int>::type + read_varint(InputIt &&first, InputIt &&last, T &i) { + int read = 0; + i = 0; + for (int shift = 0;; shift += 7) { + if (first == last) { + return read; // End of input. + } + unsigned char byte = *first++; + ++read; + if (shift + 7 >= bits && byte >= 1 << (bits - shift)) { + return -1; // Overflow. + } + if (byte == 0 && shift != 0) { + return -2; // Non-canonical representation. + } + i |= static_cast<T>(byte & 0x7f) << shift; + if ((byte & 0x80) == 0) { + break; + } + } + return read; + } + + template<typename InputIt, typename T> + int read_varint(InputIt &&first, InputIt &&last, T &i) { + return read_varint<std::numeric_limits<T>::digits, InputIt, T>(std::move(first), std::move(last), i); + } +} diff --git a/src/connectivity_tool/conn_tool.cpp b/src/connectivity_tool/conn_tool.cpp new file mode 100644 index 000000000..4b83b4f49 --- /dev/null +++ b/src/connectivity_tool/conn_tool.cpp @@ -0,0 +1,353 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + + +#include "include_base_utils.h" +#include "version.h" + +using namespace epee; +#include <boost/program_options.hpp> +#include "p2p/p2p_protocol_defs.h" +#include "common/command_line.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "net/levin_client.h" +#include "storages/levin_abstract_invoke2.h" +#include "cryptonote_core/cryptonote_core.h" +#include "storages/portable_storage_template_helper.h" +#include "crypto/crypto.h" +#include "storages/http_abstract_invoke.h" +#include "net/http_client.h" + +namespace po = boost::program_options; +using namespace cryptonote; +using namespace nodetool; + +namespace +{ + const command_line::arg_descriptor<std::string, true> arg_ip = {"ip", "set ip"}; + const command_line::arg_descriptor<size_t> arg_port = {"port", "set port"}; + const command_line::arg_descriptor<size_t> arg_rpc_port = {"rpc_port", "set rpc port"}; + const command_line::arg_descriptor<uint32_t, true> arg_timeout = {"timeout", "set timeout"}; + const command_line::arg_descriptor<std::string> arg_priv_key = {"private_key", "private key to subscribe debug command", "", true}; + const command_line::arg_descriptor<boost::uint64_t> arg_peer_id = {"peer_id", "peer_id if known(if not - will be requested)", 0}; + const command_line::arg_descriptor<bool> arg_generate_keys = {"generate_keys_pair", "generate private and public keys pair"}; + const command_line::arg_descriptor<bool> arg_request_stat_info = {"request_stat_info", "request statistics information"}; + const command_line::arg_descriptor<bool> arg_request_net_state = {"request_net_state", "request network state information (peer list, connections count)"}; + const command_line::arg_descriptor<bool> arg_get_daemon_info = {"rpc_get_daemon_info", "request daemon state info vie rpc (--rpc_port option should be set ).", "", true}; +} + +typedef COMMAND_REQUEST_STAT_INFO_T<t_cryptonote_protocol_handler<core>::stat_info> COMMAND_REQUEST_STAT_INFO; + +struct response_schema +{ + std::string status; + std::string COMMAND_REQUEST_STAT_INFO_status; + std::string COMMAND_REQUEST_NETWORK_STATE_status; + enableable<COMMAND_REQUEST_STAT_INFO::response> si_rsp; + enableable<COMMAND_REQUEST_NETWORK_STATE::response> ns_rsp; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(COMMAND_REQUEST_STAT_INFO_status) + KV_SERIALIZE(COMMAND_REQUEST_NETWORK_STATE_status) + KV_SERIALIZE(si_rsp) + KV_SERIALIZE(ns_rsp) + END_KV_SERIALIZE_MAP() +}; + + std::string get_response_schema_as_json(response_schema& rs) + { + std::stringstream ss; + ss << "{" << ENDL + << " \"status\": \"" << rs.status << "\"," << ENDL + << " \"COMMAND_REQUEST_NETWORK_STATE_status\": \"" << rs.COMMAND_REQUEST_NETWORK_STATE_status << "\"," << ENDL + << " \"COMMAND_REQUEST_STAT_INFO_status\": \"" << rs.COMMAND_REQUEST_STAT_INFO_status << "\""; + if(rs.si_rsp.enabled) + { + ss << "," << ENDL << " \"si_rsp\": " << epee::serialization::store_t_to_json(rs.si_rsp.v, 1); + } + if(rs.ns_rsp.enabled) + { + ss << "," << ENDL << " \"ns_rsp\": {" << ENDL + << " \"local_time\": " << rs.ns_rsp.v.local_time << "," << ENDL + << " \"my_id\": \"" << rs.ns_rsp.v.my_id << "\"," << ENDL + << " \"connections_list\": [" << ENDL; + + size_t i = 0; + BOOST_FOREACH(const connection_entry& ce, rs.ns_rsp.v.connections_list) + { + ss << " {\"peer_id\": \"" << ce.id << "\", \"ip\": \"" << string_tools::get_ip_string_from_int32(ce.adr.ip) << "\", \"port\": " << ce.adr.port << ", \"is_income\": "<< ce.is_income << "}"; + if(rs.ns_rsp.v.connections_list.size()-1 != i) + ss << ","; + ss << ENDL; + i++; + } + ss << " ]," << ENDL; + ss << " \"local_peerlist_white\": [" << ENDL; + i = 0; + BOOST_FOREACH(const peerlist_entry& pe, rs.ns_rsp.v.local_peerlist_white) + { + ss << " {\"peer_id\": \"" << pe.id << "\", \"ip\": \"" << string_tools::get_ip_string_from_int32(pe.adr.ip) << "\", \"port\": " << pe.adr.port << ", \"last_seen\": "<< rs.ns_rsp.v.local_time - pe.last_seen << "}"; + if(rs.ns_rsp.v.local_peerlist_white.size()-1 != i) + ss << ","; + ss << ENDL; + i++; + } + ss << " ]," << ENDL; + + ss << " \"local_peerlist_gray\": [" << ENDL; + i = 0; + BOOST_FOREACH(const peerlist_entry& pe, rs.ns_rsp.v.local_peerlist_gray) + { + ss << " {\"peer_id\": \"" << pe.id << "\", \"ip\": \"" << string_tools::get_ip_string_from_int32(pe.adr.ip) << "\", \"port\": " << pe.adr.port << ", \"last_seen\": "<< rs.ns_rsp.v.local_time - pe.last_seen << "}"; + if(rs.ns_rsp.v.local_peerlist_gray.size()-1 != i) + ss << ","; + ss << ENDL; + i++; + } + ss << " ]" << ENDL << " }" << ENDL; + } + ss << "}"; + return std::move(ss.str()); + } +//--------------------------------------------------------------------------------------------------------------- +bool print_COMMAND_REQUEST_STAT_INFO(const COMMAND_REQUEST_STAT_INFO::response& si) +{ + std::cout << " ------ COMMAND_REQUEST_STAT_INFO ------ " << ENDL; + std::cout << "Version: " << si.version << ENDL; + std::cout << "OS Version: " << si.os_version << ENDL; + std::cout << "Connections: " << si.connections_count << ENDL; + std::cout << "INC Connections: " << si.incoming_connections_count << ENDL; + + + std::cout << "Tx pool size: " << si.payload_info.tx_pool_size << ENDL; + std::cout << "BC height: " << si.payload_info.blockchain_height << ENDL; + std::cout << "Mining speed: " << si.payload_info.mining_speed << ENDL; + std::cout << "Alternative blocks: " << si.payload_info.alternative_blocks << ENDL; + std::cout << "Top block id: " << si.payload_info.top_block_id_str << ENDL; + return true; +} +//--------------------------------------------------------------------------------------------------------------- +bool print_COMMAND_REQUEST_NETWORK_STATE(const COMMAND_REQUEST_NETWORK_STATE::response& ns) +{ + std::cout << " ------ COMMAND_REQUEST_NETWORK_STATE ------ " << ENDL; + std::cout << "Peer id: " << ns.my_id << ENDL; + std::cout << "Active connections:" << ENDL; + BOOST_FOREACH(const connection_entry& ce, ns.connections_list) + { + std::cout << ce.id << "\t" << string_tools::get_ip_string_from_int32(ce.adr.ip) << ":" << ce.adr.port << (ce.is_income ? "(INC)":"(OUT)") << ENDL; + } + + std::cout << "Peer list white:" << ns.my_id << ENDL; + BOOST_FOREACH(const peerlist_entry& pe, ns.local_peerlist_white) + { + std::cout << pe.id << "\t" << string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << pe.adr.port << "\t" << misc_utils::get_time_interval_string(ns.local_time - pe.last_seen) << ENDL; + } + + std::cout << "Peer list gray:" << ns.my_id << ENDL; + BOOST_FOREACH(const peerlist_entry& pe, ns.local_peerlist_gray) + { + std::cout << pe.id << "\t" << string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << pe.adr.port << "\t" << misc_utils::get_time_interval_string(ns.local_time - pe.last_seen) << ENDL; + } + + + return true; +} +//--------------------------------------------------------------------------------------------------------------- +bool handle_get_daemon_info(po::variables_map& vm) +{ + if(!command_line::has_arg(vm, arg_rpc_port)) + { + std::cout << "ERROR: rpc port not set" << ENDL; + return false; + } + + epee::net_utils::http::http_simple_client http_client; + + cryptonote::COMMAND_RPC_GET_INFO::request req = AUTO_VAL_INIT(req); + cryptonote::COMMAND_RPC_GET_INFO::response res = AUTO_VAL_INIT(res); + std::string daemon_addr = command_line::get_arg(vm, arg_ip) + ":" + std::to_string(command_line::get_arg(vm, arg_rpc_port)); + bool r = net_utils::invoke_http_json_remote_command2(daemon_addr + "/getinfo", req, res, http_client, command_line::get_arg(vm, arg_timeout)); + if(!r) + { + std::cout << "ERROR: failed to invoke request" << ENDL; + return false; + } + std::cout << "OK" << ENDL + << "height: " << res.height << ENDL + << "difficulty: " << res.difficulty << ENDL + << "tx_count: " << res.tx_count << ENDL + << "tx_pool_size: " << res.tx_pool_size << ENDL + << "alt_blocks_count: " << res.alt_blocks_count << ENDL + << "outgoing_connections_count: " << res.outgoing_connections_count << ENDL + << "incoming_connections_count: " << res.incoming_connections_count << ENDL + << "white_peerlist_size: " << res.white_peerlist_size << ENDL + << "grey_peerlist_size: " << res.grey_peerlist_size << ENDL; + + return true; +} +//--------------------------------------------------------------------------------------------------------------- +bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) +{ + + if(!command_line::has_arg(vm, arg_priv_key)) + { + std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "secret key not set \"" << ENDL << "}"; + return false; + } + crypto::secret_key prvk = AUTO_VAL_INIT(prvk); + if(!string_tools::hex_to_pod(command_line::get_arg(vm, arg_priv_key) , prvk)) + { + std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "wrong secret key set \"" << ENDL << "}"; + return false; + } + + + response_schema rs = AUTO_VAL_INIT(rs); + + levin::levin_client_impl2 transport; + if(!transport.connect(command_line::get_arg(vm, arg_ip), static_cast<int>(command_line::get_arg(vm, arg_port)), static_cast<int>(command_line::get_arg(vm, arg_timeout)))) + { + std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "Failed to connect to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port) << "\"" << ENDL << "}"; + return false; + }else + rs.status = "OK"; + + if(!peer_id) + { + COMMAND_REQUEST_PEER_ID::request req = AUTO_VAL_INIT(req); + COMMAND_REQUEST_PEER_ID::response rsp = AUTO_VAL_INIT(rsp); + if(!net_utils::invoke_remote_command2(COMMAND_REQUEST_PEER_ID::ID, req, rsp, transport)) + { + std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "Failed to connect to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port) << "\"" << ENDL << "}"; + return false; + }else + { + peer_id = rsp.my_id; + } + } + + + nodetool::proof_of_trust pot = AUTO_VAL_INIT(pot); + pot.peer_id = peer_id; + pot.time = time(NULL); + crypto::public_key pubk = AUTO_VAL_INIT(pubk); + string_tools::hex_to_pod(P2P_STAT_TRUSTED_PUB_KEY, pubk); + crypto::hash h = tools::get_proof_of_trust_hash(pot); + crypto::generate_signature(h, pubk, prvk, pot.sign); + + if(command_line::get_arg(vm, arg_request_stat_info)) + { + COMMAND_REQUEST_STAT_INFO::request req = AUTO_VAL_INIT(req); + req.tr = pot; + if(!net_utils::invoke_remote_command2(COMMAND_REQUEST_STAT_INFO::ID, req, rs.si_rsp.v, transport)) + { + std::stringstream ss; + ss << "ERROR: " << "Failed to invoke remote command COMMAND_REQUEST_STAT_INFO to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port); + rs.COMMAND_REQUEST_STAT_INFO_status = ss.str(); + }else + { + rs.si_rsp.enabled = true; + rs.COMMAND_REQUEST_STAT_INFO_status = "OK"; + } + } + + + if(command_line::get_arg(vm, arg_request_net_state)) + { + ++pot.time; + h = tools::get_proof_of_trust_hash(pot); + crypto::generate_signature(h, pubk, prvk, pot.sign); + COMMAND_REQUEST_NETWORK_STATE::request req = AUTO_VAL_INIT(req); + req.tr = pot; + if(!net_utils::invoke_remote_command2(COMMAND_REQUEST_NETWORK_STATE::ID, req, rs.ns_rsp.v, transport)) + { + std::stringstream ss; + ss << "ERROR: " << "Failed to invoke remote command COMMAND_REQUEST_NETWORK_STATE to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port); + rs.COMMAND_REQUEST_NETWORK_STATE_status = ss.str(); + }else + { + rs.ns_rsp.enabled = true; + rs.COMMAND_REQUEST_NETWORK_STATE_status = "OK"; + } + } + std::cout << get_response_schema_as_json(rs); + return true; +} +//--------------------------------------------------------------------------------------------------------------- +bool generate_and_print_keys() +{ + crypto::public_key pk = AUTO_VAL_INIT(pk); + crypto::secret_key sk = AUTO_VAL_INIT(sk); + generate_keys(pk, sk); + std::cout << "PUBLIC KEY: " << epee::string_tools::pod_to_hex(pk) << ENDL + << "PRIVATE KEY: " << epee::string_tools::pod_to_hex(sk); + return true; +} +int main(int argc, char* argv[]) +{ + string_tools::set_module_name_and_folder(argv[0]); + log_space::get_set_log_detalisation_level(true, LOG_LEVEL_0); + + // Declare the supported options. + po::options_description desc_general("General options"); + command_line::add_arg(desc_general, command_line::arg_help); + + po::options_description desc_params("Connectivity options"); + command_line::add_arg(desc_params, arg_ip); + command_line::add_arg(desc_params, arg_port); + command_line::add_arg(desc_params, arg_rpc_port); + command_line::add_arg(desc_params, arg_timeout); + command_line::add_arg(desc_params, arg_request_stat_info); + command_line::add_arg(desc_params, arg_request_net_state); + command_line::add_arg(desc_params, arg_generate_keys); + command_line::add_arg(desc_params, arg_peer_id); + command_line::add_arg(desc_params, arg_priv_key); + command_line::add_arg(desc_params, arg_get_daemon_info); + + + po::options_description desc_all; + desc_all.add(desc_general).add(desc_params); + + po::variables_map vm; + bool r = command_line::handle_error_helper(desc_all, [&]() + { + po::store(command_line::parse_command_line(argc, argv, desc_general, true), vm); + if (command_line::get_arg(vm, command_line::arg_help)) + { + std::cout << desc_all << ENDL; + return false; + } + + po::store(command_line::parse_command_line(argc, argv, desc_params, false), vm); + po::notify(vm); + + return true; + }); + if (!r) + return 1; + + if(command_line::has_arg(vm, arg_request_stat_info) || command_line::has_arg(vm, arg_request_net_state)) + { + return handle_request_stat(vm, command_line::get_arg(vm, arg_peer_id)) ? 0:1; + } + if(command_line::has_arg(vm, arg_get_daemon_info)) + { + return handle_get_daemon_info(vm) ? 0:1; + } + else if(command_line::has_arg(vm, arg_generate_keys)) + { + return generate_and_print_keys() ? 0:1; + } + else + { + std::cerr << "Not enough arguments." << ENDL; + std::cerr << desc_all << ENDL; + } + + return 1; +} + diff --git a/src/crypto/blake256.c b/src/crypto/blake256.c new file mode 100644 index 000000000..36b42c3d4 --- /dev/null +++ b/src/crypto/blake256.c @@ -0,0 +1,326 @@ +/* + * The blake256_* and blake224_* functions are largely copied from + * blake256_light.c and blake224_light.c from the BLAKE website: + * + * http://131002.net/blake/ + * + * The hmac_* functions implement HMAC-BLAKE-256 and HMAC-BLAKE-224. + * HMAC is specified by RFC 2104. + */ + +#include <string.h> +#include <stdio.h> +#include <stdint.h> +#include "blake256.h" + +#define U8TO32(p) \ + (((uint32_t)((p)[0]) << 24) | ((uint32_t)((p)[1]) << 16) | \ + ((uint32_t)((p)[2]) << 8) | ((uint32_t)((p)[3]) )) +#define U32TO8(p, v) \ + (p)[0] = (uint8_t)((v) >> 24); (p)[1] = (uint8_t)((v) >> 16); \ + (p)[2] = (uint8_t)((v) >> 8); (p)[3] = (uint8_t)((v) ); + +const uint8_t sigma[][16] = { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15}, + {14,10, 4, 8, 9,15,13, 6, 1,12, 0, 2,11, 7, 5, 3}, + {11, 8,12, 0, 5, 2,15,13,10,14, 3, 6, 7, 1, 9, 4}, + { 7, 9, 3, 1,13,12,11,14, 2, 6, 5,10, 4, 0,15, 8}, + { 9, 0, 5, 7, 2, 4,10,15,14, 1,11,12, 6, 8, 3,13}, + { 2,12, 6,10, 0,11, 8, 3, 4,13, 7, 5,15,14, 1, 9}, + {12, 5, 1,15,14,13, 4,10, 0, 7, 6, 3, 9, 2, 8,11}, + {13,11, 7,14,12, 1, 3, 9, 5, 0,15, 4, 8, 6, 2,10}, + { 6,15,14, 9,11, 3, 0, 8,12, 2,13, 7, 1, 4,10, 5}, + {10, 2, 8, 4, 7, 6, 1, 5,15,11, 9,14, 3,12,13, 0}, + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15}, + {14,10, 4, 8, 9,15,13, 6, 1,12, 0, 2,11, 7, 5, 3}, + {11, 8,12, 0, 5, 2,15,13,10,14, 3, 6, 7, 1, 9, 4}, + { 7, 9, 3, 1,13,12,11,14, 2, 6, 5,10, 4, 0,15, 8} +}; + +const uint32_t cst[16] = { + 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, + 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89, + 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, + 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917 +}; + +static const uint8_t padding[] = { + 0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + + +void blake256_compress(state *S, const uint8_t *block) { + uint32_t v[16], m[16], i; + +#define ROT(x,n) (((x)<<(32-n))|((x)>>(n))) +#define G(a,b,c,d,e) \ + v[a] += (m[sigma[i][e]] ^ cst[sigma[i][e+1]]) + v[b]; \ + v[d] = ROT(v[d] ^ v[a],16); \ + v[c] += v[d]; \ + v[b] = ROT(v[b] ^ v[c],12); \ + v[a] += (m[sigma[i][e+1]] ^ cst[sigma[i][e]])+v[b]; \ + v[d] = ROT(v[d] ^ v[a], 8); \ + v[c] += v[d]; \ + v[b] = ROT(v[b] ^ v[c], 7); + + for (i = 0; i < 16; ++i) m[i] = U8TO32(block + i * 4); + for (i = 0; i < 8; ++i) v[i] = S->h[i]; + v[ 8] = S->s[0] ^ 0x243F6A88; + v[ 9] = S->s[1] ^ 0x85A308D3; + v[10] = S->s[2] ^ 0x13198A2E; + v[11] = S->s[3] ^ 0x03707344; + v[12] = 0xA4093822; + v[13] = 0x299F31D0; + v[14] = 0x082EFA98; + v[15] = 0xEC4E6C89; + + if (S->nullt == 0) { + v[12] ^= S->t[0]; + v[13] ^= S->t[0]; + v[14] ^= S->t[1]; + v[15] ^= S->t[1]; + } + + for (i = 0; i < 14; ++i) { + G(0, 4, 8, 12, 0); + G(1, 5, 9, 13, 2); + G(2, 6, 10, 14, 4); + G(3, 7, 11, 15, 6); + G(3, 4, 9, 14, 14); + G(2, 7, 8, 13, 12); + G(0, 5, 10, 15, 8); + G(1, 6, 11, 12, 10); + } + + for (i = 0; i < 16; ++i) S->h[i % 8] ^= v[i]; + for (i = 0; i < 8; ++i) S->h[i] ^= S->s[i % 4]; +} + +void blake256_init(state *S) { + S->h[0] = 0x6A09E667; + S->h[1] = 0xBB67AE85; + S->h[2] = 0x3C6EF372; + S->h[3] = 0xA54FF53A; + S->h[4] = 0x510E527F; + S->h[5] = 0x9B05688C; + S->h[6] = 0x1F83D9AB; + S->h[7] = 0x5BE0CD19; + S->t[0] = S->t[1] = S->buflen = S->nullt = 0; + S->s[0] = S->s[1] = S->s[2] = S->s[3] = 0; +} + +void blake224_init(state *S) { + S->h[0] = 0xC1059ED8; + S->h[1] = 0x367CD507; + S->h[2] = 0x3070DD17; + S->h[3] = 0xF70E5939; + S->h[4] = 0xFFC00B31; + S->h[5] = 0x68581511; + S->h[6] = 0x64F98FA7; + S->h[7] = 0xBEFA4FA4; + S->t[0] = S->t[1] = S->buflen = S->nullt = 0; + S->s[0] = S->s[1] = S->s[2] = S->s[3] = 0; +} + +// datalen = number of bits +void blake256_update(state *S, const uint8_t *data, uint64_t datalen) { + int left = S->buflen >> 3; + int fill = 64 - left; + + if (left && (((datalen >> 3) & 0x3F) >= (unsigned) fill)) { + memcpy((void *) (S->buf + left), (void *) data, fill); + S->t[0] += 512; + if (S->t[0] == 0) S->t[1]++; + blake256_compress(S, S->buf); + data += fill; + datalen -= (fill << 3); + left = 0; + } + + while (datalen >= 512) { + S->t[0] += 512; + if (S->t[0] == 0) S->t[1]++; + blake256_compress(S, data); + data += 64; + datalen -= 512; + } + + if (datalen > 0) { + memcpy((void *) (S->buf + left), (void *) data, datalen >> 3); + S->buflen = (left << 3) + datalen; + } else { + S->buflen = 0; + } +} + +// datalen = number of bits +void blake224_update(state *S, const uint8_t *data, uint64_t datalen) { + blake256_update(S, data, datalen); +} + +void blake256_final_h(state *S, uint8_t *digest, uint8_t pa, uint8_t pb) { + uint8_t msglen[8]; + uint32_t lo = S->t[0] + S->buflen, hi = S->t[1]; + if (lo < (unsigned) S->buflen) hi++; + U32TO8(msglen + 0, hi); + U32TO8(msglen + 4, lo); + + if (S->buflen == 440) { /* one padding byte */ + S->t[0] -= 8; + blake256_update(S, &pa, 8); + } else { + if (S->buflen < 440) { /* enough space to fill the block */ + if (S->buflen == 0) S->nullt = 1; + S->t[0] -= 440 - S->buflen; + blake256_update(S, padding, 440 - S->buflen); + } else { /* need 2 compressions */ + S->t[0] -= 512 - S->buflen; + blake256_update(S, padding, 512 - S->buflen); + S->t[0] -= 440; + blake256_update(S, padding + 1, 440); + S->nullt = 1; + } + blake256_update(S, &pb, 8); + S->t[0] -= 8; + } + S->t[0] -= 64; + blake256_update(S, msglen, 64); + + U32TO8(digest + 0, S->h[0]); + U32TO8(digest + 4, S->h[1]); + U32TO8(digest + 8, S->h[2]); + U32TO8(digest + 12, S->h[3]); + U32TO8(digest + 16, S->h[4]); + U32TO8(digest + 20, S->h[5]); + U32TO8(digest + 24, S->h[6]); + U32TO8(digest + 28, S->h[7]); +} + +void blake256_final(state *S, uint8_t *digest) { + blake256_final_h(S, digest, 0x81, 0x01); +} + +void blake224_final(state *S, uint8_t *digest) { + blake256_final_h(S, digest, 0x80, 0x00); +} + +// inlen = number of bytes +void blake256_hash(uint8_t *out, const uint8_t *in, uint64_t inlen) { + state S; + blake256_init(&S); + blake256_update(&S, in, inlen * 8); + blake256_final(&S, out); +} + +// inlen = number of bytes +void blake224_hash(uint8_t *out, const uint8_t *in, uint64_t inlen) { + state S; + blake224_init(&S); + blake224_update(&S, in, inlen * 8); + blake224_final(&S, out); +} + +// keylen = number of bytes +void hmac_blake256_init(hmac_state *S, const uint8_t *_key, uint64_t keylen) { + const uint8_t *key = _key; + uint8_t keyhash[32]; + uint8_t pad[64]; + uint64_t i; + + if (keylen > 64) { + blake256_hash(keyhash, key, keylen); + key = keyhash; + keylen = 32; + } + + blake256_init(&S->inner); + memset(pad, 0x36, 64); + for (i = 0; i < keylen; ++i) { + pad[i] ^= key[i]; + } + blake256_update(&S->inner, pad, 512); + + blake256_init(&S->outer); + memset(pad, 0x5c, 64); + for (i = 0; i < keylen; ++i) { + pad[i] ^= key[i]; + } + blake256_update(&S->outer, pad, 512); + + memset(keyhash, 0, 32); +} + +// keylen = number of bytes +void hmac_blake224_init(hmac_state *S, const uint8_t *_key, uint64_t keylen) { + const uint8_t *key = _key; + uint8_t keyhash[32]; + uint8_t pad[64]; + uint64_t i; + + if (keylen > 64) { + blake256_hash(keyhash, key, keylen); + key = keyhash; + keylen = 28; + } + + blake224_init(&S->inner); + memset(pad, 0x36, 64); + for (i = 0; i < keylen; ++i) { + pad[i] ^= key[i]; + } + blake224_update(&S->inner, pad, 512); + + blake224_init(&S->outer); + memset(pad, 0x5c, 64); + for (i = 0; i < keylen; ++i) { + pad[i] ^= key[i]; + } + blake224_update(&S->outer, pad, 512); + + memset(keyhash, 0, 32); +} + +// datalen = number of bits +void hmac_blake256_update(hmac_state *S, const uint8_t *data, uint64_t datalen) { + // update the inner state + blake256_update(&S->inner, data, datalen); +} + +// datalen = number of bits +void hmac_blake224_update(hmac_state *S, const uint8_t *data, uint64_t datalen) { + // update the inner state + blake224_update(&S->inner, data, datalen); +} + +void hmac_blake256_final(hmac_state *S, uint8_t *digest) { + uint8_t ihash[32]; + blake256_final(&S->inner, ihash); + blake256_update(&S->outer, ihash, 256); + blake256_final(&S->outer, digest); + memset(ihash, 0, 32); +} + +void hmac_blake224_final(hmac_state *S, uint8_t *digest) { + uint8_t ihash[32]; + blake224_final(&S->inner, ihash); + blake224_update(&S->outer, ihash, 224); + blake224_final(&S->outer, digest); + memset(ihash, 0, 32); +} + +// keylen = number of bytes; inlen = number of bytes +void hmac_blake256_hash(uint8_t *out, const uint8_t *key, uint64_t keylen, const uint8_t *in, uint64_t inlen) { + hmac_state S; + hmac_blake256_init(&S, key, keylen); + hmac_blake256_update(&S, in, inlen * 8); + hmac_blake256_final(&S, out); +} + +// keylen = number of bytes; inlen = number of bytes +void hmac_blake224_hash(uint8_t *out, const uint8_t *key, uint64_t keylen, const uint8_t *in, uint64_t inlen) { + hmac_state S; + hmac_blake224_init(&S, key, keylen); + hmac_blake224_update(&S, in, inlen * 8); + hmac_blake224_final(&S, out); +} diff --git a/src/crypto/blake256.h b/src/crypto/blake256.h new file mode 100644 index 000000000..b9c2aad0d --- /dev/null +++ b/src/crypto/blake256.h @@ -0,0 +1,43 @@ +#ifndef _BLAKE256_H_ +#define _BLAKE256_H_ + +#include <stdint.h> + +typedef struct { + uint32_t h[8], s[4], t[2]; + int buflen, nullt; + uint8_t buf[64]; +} state; + +typedef struct { + state inner; + state outer; +} hmac_state; + +void blake256_init(state *); +void blake224_init(state *); + +void blake256_update(state *, const uint8_t *, uint64_t); +void blake224_update(state *, const uint8_t *, uint64_t); + +void blake256_final(state *, uint8_t *); +void blake224_final(state *, uint8_t *); + +void blake256_hash(uint8_t *, const uint8_t *, uint64_t); +void blake224_hash(uint8_t *, const uint8_t *, uint64_t); + +/* HMAC functions: */ + +void hmac_blake256_init(hmac_state *, const uint8_t *, uint64_t); +void hmac_blake224_init(hmac_state *, const uint8_t *, uint64_t); + +void hmac_blake256_update(hmac_state *, const uint8_t *, uint64_t); +void hmac_blake224_update(hmac_state *, const uint8_t *, uint64_t); + +void hmac_blake256_final(hmac_state *, uint8_t *); +void hmac_blake224_final(hmac_state *, uint8_t *); + +void hmac_blake256_hash(uint8_t *, const uint8_t *, uint64_t, const uint8_t *, uint64_t); +void hmac_blake224_hash(uint8_t *, const uint8_t *, uint64_t, const uint8_t *, uint64_t); + +#endif /* _BLAKE256_H_ */ diff --git a/src/crypto/chacha8.c b/src/crypto/chacha8.c new file mode 100644 index 000000000..df135af59 --- /dev/null +++ b/src/crypto/chacha8.c @@ -0,0 +1,170 @@ +/* +chacha-merged.c version 20080118 +D. J. Bernstein +Public domain. +*/ + +#include <memory.h> +#include <stdio.h> +#include <sys/param.h> + +#include "chacha8.h" +#include "common/int-util.h" +#include "warnings.h" + +/* + * The following macros are used to obtain exact-width results. + */ +#define U8V(v) ((uint8_t)(v) & UINT8_C(0xFF)) +#define U32V(v) ((uint32_t)(v) & UINT32_C(0xFFFFFFFF)) + +/* + * The following macros load words from an array of bytes with + * different types of endianness, and vice versa. + */ +#define U8TO32_LITTLE(p) SWAP32LE(((uint32_t*)(p))[0]) +#define U32TO8_LITTLE(p, v) (((uint32_t*)(p))[0] = SWAP32LE(v)) + +#define ROTATE(v,c) (rol32(v,c)) +#define XOR(v,w) ((v) ^ (w)) +#define PLUS(v,w) (U32V((v) + (w))) +#define PLUSONE(v) (PLUS((v),1)) + +#define QUARTERROUND(a,b,c,d) \ + a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \ + a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c), 7); + +static const char sigma[] = "expand 32-byte k"; + +DISABLE_GCC_AND_CLANG_WARNING(strict-aliasing) + +void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher) { + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + char* ctarget = 0; + char tmp[64]; + int i; + + if (!length) return; + + j0 = U8TO32_LITTLE(sigma + 0); + j1 = U8TO32_LITTLE(sigma + 4); + j2 = U8TO32_LITTLE(sigma + 8); + j3 = U8TO32_LITTLE(sigma + 12); + j4 = U8TO32_LITTLE(key + 0); + j5 = U8TO32_LITTLE(key + 4); + j6 = U8TO32_LITTLE(key + 8); + j7 = U8TO32_LITTLE(key + 12); + j8 = U8TO32_LITTLE(key + 16); + j9 = U8TO32_LITTLE(key + 20); + j10 = U8TO32_LITTLE(key + 24); + j11 = U8TO32_LITTLE(key + 28); + j12 = 0; + j13 = 0; + j14 = U8TO32_LITTLE(iv + 0); + j15 = U8TO32_LITTLE(iv + 4); + + for (;;) { + if (length < 64) { + memcpy(tmp, data, length); + data = tmp; + ctarget = cipher; + cipher = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for (i = 8;i > 0;i -= 2) { + QUARTERROUND( x0, x4, x8,x12) + QUARTERROUND( x1, x5, x9,x13) + QUARTERROUND( x2, x6,x10,x14) + QUARTERROUND( x3, x7,x11,x15) + QUARTERROUND( x0, x5,x10,x15) + QUARTERROUND( x1, x6,x11,x12) + QUARTERROUND( x2, x7, x8,x13) + QUARTERROUND( x3, x4, x9,x14) + } + x0 = PLUS( x0, j0); + x1 = PLUS( x1, j1); + x2 = PLUS( x2, j2); + x3 = PLUS( x3, j3); + x4 = PLUS( x4, j4); + x5 = PLUS( x5, j5); + x6 = PLUS( x6, j6); + x7 = PLUS( x7, j7); + x8 = PLUS( x8, j8); + x9 = PLUS( x9, j9); + x10 = PLUS(x10,j10); + x11 = PLUS(x11,j11); + x12 = PLUS(x12,j12); + x13 = PLUS(x13,j13); + x14 = PLUS(x14,j14); + x15 = PLUS(x15,j15); + + x0 = XOR( x0,U8TO32_LITTLE((uint8_t*)data + 0)); + x1 = XOR( x1,U8TO32_LITTLE((uint8_t*)data + 4)); + x2 = XOR( x2,U8TO32_LITTLE((uint8_t*)data + 8)); + x3 = XOR( x3,U8TO32_LITTLE((uint8_t*)data + 12)); + x4 = XOR( x4,U8TO32_LITTLE((uint8_t*)data + 16)); + x5 = XOR( x5,U8TO32_LITTLE((uint8_t*)data + 20)); + x6 = XOR( x6,U8TO32_LITTLE((uint8_t*)data + 24)); + x7 = XOR( x7,U8TO32_LITTLE((uint8_t*)data + 28)); + x8 = XOR( x8,U8TO32_LITTLE((uint8_t*)data + 32)); + x9 = XOR( x9,U8TO32_LITTLE((uint8_t*)data + 36)); + x10 = XOR(x10,U8TO32_LITTLE((uint8_t*)data + 40)); + x11 = XOR(x11,U8TO32_LITTLE((uint8_t*)data + 44)); + x12 = XOR(x12,U8TO32_LITTLE((uint8_t*)data + 48)); + x13 = XOR(x13,U8TO32_LITTLE((uint8_t*)data + 52)); + x14 = XOR(x14,U8TO32_LITTLE((uint8_t*)data + 56)); + x15 = XOR(x15,U8TO32_LITTLE((uint8_t*)data + 60)); + + j12 = PLUSONE(j12); + if (!j12) + { + j13 = PLUSONE(j13); + /* stopping at 2^70 bytes per iv is user's responsibility */ + } + + U32TO8_LITTLE(cipher + 0,x0); + U32TO8_LITTLE(cipher + 4,x1); + U32TO8_LITTLE(cipher + 8,x2); + U32TO8_LITTLE(cipher + 12,x3); + U32TO8_LITTLE(cipher + 16,x4); + U32TO8_LITTLE(cipher + 20,x5); + U32TO8_LITTLE(cipher + 24,x6); + U32TO8_LITTLE(cipher + 28,x7); + U32TO8_LITTLE(cipher + 32,x8); + U32TO8_LITTLE(cipher + 36,x9); + U32TO8_LITTLE(cipher + 40,x10); + U32TO8_LITTLE(cipher + 44,x11); + U32TO8_LITTLE(cipher + 48,x12); + U32TO8_LITTLE(cipher + 52,x13); + U32TO8_LITTLE(cipher + 56,x14); + U32TO8_LITTLE(cipher + 60,x15); + + if (length <= 64) { + if (length < 64) { + memcpy(ctarget, cipher, length); + } + return; + } + length -= 64; + cipher += 64; + data = (uint8_t*)data + 64; + } +} diff --git a/src/crypto/chacha8.h b/src/crypto/chacha8.h new file mode 100644 index 000000000..e4fe46799 --- /dev/null +++ b/src/crypto/chacha8.h @@ -0,0 +1,56 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <stdint.h> +#include <stddef.h> + +#define CHACHA8_KEY_SIZE 32 +#define CHACHA8_IV_SIZE 8 + +#if defined(__cplusplus) +#include <memory.h> + +#include "hash.h" + +namespace crypto { + extern "C" { +#endif + void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher); +#if defined(__cplusplus) + } + +#pragma pack(push, 1) + struct chacha8_key { + uint8_t data[CHACHA8_KEY_SIZE]; + + ~chacha8_key() + { + memset(data, 0, sizeof(data)); + } + }; + + // MS VC 2012 doesn't interpret `class chacha8_iv` as POD in spite of [9.0.10], so it is a struct + struct chacha8_iv { + uint8_t data[CHACHA8_IV_SIZE]; + }; +#pragma pack(pop) + + static_assert(sizeof(chacha8_key) == CHACHA8_KEY_SIZE && sizeof(chacha8_iv) == CHACHA8_IV_SIZE, "Invalid structure size"); + + inline void chacha8(const void* data, std::size_t length, const chacha8_key& key, const chacha8_iv& iv, char* cipher) { + chacha8(data, length, reinterpret_cast<const uint8_t*>(&key), reinterpret_cast<const uint8_t*>(&iv), cipher); + } + + inline void generate_chacha8_key(std::string password, chacha8_key& key) { + static_assert(sizeof(chacha8_key) <= sizeof(hash), "Size of hash must be at least that of chacha8_key"); + char pwd_hash[HASH_SIZE]; + crypto::cn_slow_hash(password.data(), password.size(), pwd_hash); + memcpy(&key, pwd_hash, sizeof(key)); + memset(pwd_hash, 0, sizeof(pwd_hash)); + } +} + +#endif diff --git a/src/crypto/crypto-ops-data.c b/src/crypto/crypto-ops-data.c index 8eb2fbb0b..48bfe21a2 100644 --- a/src/crypto/crypto-ops-data.c +++ b/src/crypto/crypto-ops-data.c @@ -1,3 +1,7 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include <stdint.h> #include "crypto-ops.h" @@ -839,4 +843,4 @@ const fe fe_ma = {-486662, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* -A */ const fe fe_fffb1 = {-31702527, -2466483, -26106795, -12203692, -12169197, -321052, 14850977, -10296299, -16929438, -407568}; /* sqrt(-2 * A * (A + 2)) */ const fe fe_fffb2 = {8166131, -6741800, -17040804, 3154616, 21461005, 1466302, -30876704, -6368709, 10503587, -13363080}; /* sqrt(2 * A * (A + 2)) */ const fe fe_fffb3 = {-13620103, 14639558, 4532995, 7679154, 16815101, -15883539, -22863840, -14813421, 13716513, -6477756}; /* sqrt(-sqrt(-1) * A * (A + 2)) */ -const fe fe_fffb4 = {-21786234, -12173074, 21573800, 4524538, -4645904, 16204591, 8012863, -8444712, 3212926, 6885324}; /* sqrt(sqrt(-1) * A * (A + 2)) */
\ No newline at end of file +const fe fe_fffb4 = {-21786234, -12173074, 21573800, 4524538, -4645904, 16204591, 8012863, -8444712, 3212926, 6885324}; /* sqrt(sqrt(-1) * A * (A + 2)) */ diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c index dc2c7092c..97e7df50e 100644 --- a/src/crypto/crypto-ops.c +++ b/src/crypto/crypto-ops.c @@ -1,8 +1,15 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include <assert.h> #include <stdint.h> +#include "warnings.h" #include "crypto-ops.h" +DISABLE_VS_WARNINGS(4146 4244) + /* Predeclarations */ static void fe_mul(fe, const fe, const fe); @@ -232,28 +239,53 @@ static void fe_invert(fe out, const fe z) { fe t3; int i; - fe_sq(t0,z); for (i = 1;i < 1;++i) fe_sq(t0,t0); - fe_sq(t1,t0); for (i = 1;i < 2;++i) fe_sq(t1,t1); - fe_mul(t1,z,t1); - fe_mul(t0,t0,t1); - fe_sq(t2,t0); for (i = 1;i < 1;++i) fe_sq(t2,t2); - fe_mul(t1,t1,t2); - fe_sq(t2,t1); for (i = 1;i < 5;++i) fe_sq(t2,t2); - fe_mul(t1,t2,t1); - fe_sq(t2,t1); for (i = 1;i < 10;++i) fe_sq(t2,t2); - fe_mul(t2,t2,t1); - fe_sq(t3,t2); for (i = 1;i < 20;++i) fe_sq(t3,t3); - fe_mul(t2,t3,t2); - fe_sq(t2,t2); for (i = 1;i < 10;++i) fe_sq(t2,t2); - fe_mul(t1,t2,t1); - fe_sq(t2,t1); for (i = 1;i < 50;++i) fe_sq(t2,t2); - fe_mul(t2,t2,t1); - fe_sq(t3,t2); for (i = 1;i < 100;++i) fe_sq(t3,t3); - fe_mul(t2,t3,t2); - fe_sq(t2,t2); for (i = 1;i < 50;++i) fe_sq(t2,t2); - fe_mul(t1,t2,t1); - fe_sq(t1,t1); for (i = 1;i < 5;++i) fe_sq(t1,t1); - fe_mul(out,t1,t0); + fe_sq(t0, z); + fe_sq(t1, t0); + fe_sq(t1, t1); + fe_mul(t1, z, t1); + fe_mul(t0, t0, t1); + fe_sq(t2, t0); + fe_mul(t1, t1, t2); + fe_sq(t2, t1); + for (i = 0; i < 4; ++i) { + fe_sq(t2, t2); + } + fe_mul(t1, t2, t1); + fe_sq(t2, t1); + for (i = 0; i < 9; ++i) { + fe_sq(t2, t2); + } + fe_mul(t2, t2, t1); + fe_sq(t3, t2); + for (i = 0; i < 19; ++i) { + fe_sq(t3, t3); + } + fe_mul(t2, t3, t2); + fe_sq(t2, t2); + for (i = 0; i < 9; ++i) { + fe_sq(t2, t2); + } + fe_mul(t1, t2, t1); + fe_sq(t2, t1); + for (i = 0; i < 49; ++i) { + fe_sq(t2, t2); + } + fe_mul(t2, t2, t1); + fe_sq(t3, t2); + for (i = 0; i < 99; ++i) { + fe_sq(t3, t3); + } + fe_mul(t2, t3, t2); + fe_sq(t2, t2); + for (i = 0; i < 49; ++i) { + fe_sq(t2, t2); + } + fe_mul(t1, t2, t1); + fe_sq(t1, t1); + for (i = 0; i < 4; ++i) { + fe_sq(t1, t1); + } + fe_mul(out, t1, t0); return; } @@ -1089,8 +1121,9 @@ static void slide(signed char *r, const unsigned char *a) { int b; int k; - for (i = 0; i < 256; ++i) + for (i = 0; i < 256; ++i) { r[i] = 1 & (a[i >> 3] >> (i & 7)); + } for (i = 0; i < 256; ++i) { if (r[i]) { @@ -1417,8 +1450,8 @@ void ge_p3_tobytes(unsigned char *s, const ge_p3 *h) { fe y; fe_invert(recip, h->Z); - fe_mul(x,h->X, recip); - fe_mul(y,h->Y, recip); + fe_mul(x, h->X, recip); + fe_mul(y, h->Y, recip); fe_tobytes(s, y); s[31] ^= fe_isnegative(x) << 7; } @@ -1492,7 +1525,7 @@ void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) { ge_precomp t; int i; - for (i = 0;i < 32;++i) { + for (i = 0; i < 32; ++i) { e[2 * i + 0] = (a[i] >> 0) & 15; e[2 * i + 1] = (a[i] >> 4) & 15; } @@ -1500,7 +1533,7 @@ void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) { /* e[63] is between 0 and 7 */ carry = 0; - for (i = 0;i < 63;++i) { + for (i = 0; i < 63; ++i) { e[i] += carry; carry = e[i] + 8; carry >>= 4; @@ -1510,7 +1543,7 @@ void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) { /* each e[i] is between -8 and 8 */ ge_p3_0(h); - for (i = 1;i < 64;i += 2) { + for (i = 1; i < 64; i += 2) { select(&t, i / 2, e[i]); ge_madd(&r, h, &t); ge_p1p1_to_p3(h, &r); } @@ -1520,7 +1553,7 @@ void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) { ge_p2_dbl(&r, &s); ge_p1p1_to_p2(&s, &r); ge_p2_dbl(&r, &s); ge_p1p1_to_p3(h, &r); - for (i = 0;i < 64;i += 2) { + for (i = 0; i < 64; i += 2) { select(&t, i / 2, e[i]); ge_madd(&r, h, &t); ge_p1p1_to_p3(h, &r); } @@ -1622,7 +1655,6 @@ void sc_reduce(unsigned char *s) { s14 -= s23 * 997805; s15 += s23 * 136657; s16 -= s23 * 683901; - s23 = 0; s10 += s22 * 666643; s11 += s22 * 470296; @@ -1630,7 +1662,6 @@ void sc_reduce(unsigned char *s) { s13 -= s22 * 997805; s14 += s22 * 136657; s15 -= s22 * 683901; - s22 = 0; s9 += s21 * 666643; s10 += s21 * 470296; @@ -1638,7 +1669,6 @@ void sc_reduce(unsigned char *s) { s12 -= s21 * 997805; s13 += s21 * 136657; s14 -= s21 * 683901; - s21 = 0; s8 += s20 * 666643; s9 += s20 * 470296; @@ -1646,7 +1676,6 @@ void sc_reduce(unsigned char *s) { s11 -= s20 * 997805; s12 += s20 * 136657; s13 -= s20 * 683901; - s20 = 0; s7 += s19 * 666643; s8 += s19 * 470296; @@ -1654,7 +1683,6 @@ void sc_reduce(unsigned char *s) { s10 -= s19 * 997805; s11 += s19 * 136657; s12 -= s19 * 683901; - s19 = 0; s6 += s18 * 666643; s7 += s18 * 470296; @@ -1662,7 +1690,6 @@ void sc_reduce(unsigned char *s) { s9 -= s18 * 997805; s10 += s18 * 136657; s11 -= s18 * 683901; - s18 = 0; carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; @@ -1683,7 +1710,6 @@ void sc_reduce(unsigned char *s) { s8 -= s17 * 997805; s9 += s17 * 136657; s10 -= s17 * 683901; - s17 = 0; s4 += s16 * 666643; s5 += s16 * 470296; @@ -1691,7 +1717,6 @@ void sc_reduce(unsigned char *s) { s7 -= s16 * 997805; s8 += s16 * 136657; s9 -= s16 * 683901; - s16 = 0; s3 += s15 * 666643; s4 += s15 * 470296; @@ -1699,7 +1724,6 @@ void sc_reduce(unsigned char *s) { s6 -= s15 * 997805; s7 += s15 * 136657; s8 -= s15 * 683901; - s15 = 0; s2 += s14 * 666643; s3 += s14 * 470296; @@ -1707,7 +1731,6 @@ void sc_reduce(unsigned char *s) { s5 -= s14 * 997805; s6 += s14 * 136657; s7 -= s14 * 683901; - s14 = 0; s1 += s13 * 666643; s2 += s13 * 470296; @@ -1715,7 +1738,6 @@ void sc_reduce(unsigned char *s) { s4 -= s13 * 997805; s5 += s13 * 136657; s6 -= s13 * 683901; - s13 = 0; s0 += s12 * 666643; s1 += s12 * 470296; @@ -1766,7 +1788,6 @@ void sc_reduce(unsigned char *s) { s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; - s12 = 0; carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; @@ -2196,7 +2217,6 @@ void sc_reduce32(unsigned char *s) { s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; - s12 = 0; carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; @@ -2336,7 +2356,6 @@ void sc_add(unsigned char *s, const unsigned char *a, const unsigned char *b) { s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; - s12 = 0; carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; @@ -2476,7 +2495,6 @@ void sc_sub(unsigned char *s, const unsigned char *a, const unsigned char *b) { s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; - s12 = 0; carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; @@ -2676,7 +2694,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s14 -= s23 * 997805; s15 += s23 * 136657; s16 -= s23 * 683901; - s23 = 0; s10 += s22 * 666643; s11 += s22 * 470296; @@ -2684,7 +2701,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s13 -= s22 * 997805; s14 += s22 * 136657; s15 -= s22 * 683901; - s22 = 0; s9 += s21 * 666643; s10 += s21 * 470296; @@ -2692,7 +2708,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s12 -= s21 * 997805; s13 += s21 * 136657; s14 -= s21 * 683901; - s21 = 0; s8 += s20 * 666643; s9 += s20 * 470296; @@ -2700,7 +2715,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s11 -= s20 * 997805; s12 += s20 * 136657; s13 -= s20 * 683901; - s20 = 0; s7 += s19 * 666643; s8 += s19 * 470296; @@ -2708,7 +2722,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s10 -= s19 * 997805; s11 += s19 * 136657; s12 -= s19 * 683901; - s19 = 0; s6 += s18 * 666643; s7 += s18 * 470296; @@ -2716,7 +2729,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s9 -= s18 * 997805; s10 += s18 * 136657; s11 -= s18 * 683901; - s18 = 0; carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; @@ -2737,7 +2749,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s8 -= s17 * 997805; s9 += s17 * 136657; s10 -= s17 * 683901; - s17 = 0; s4 += s16 * 666643; s5 += s16 * 470296; @@ -2745,7 +2756,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s7 -= s16 * 997805; s8 += s16 * 136657; s9 -= s16 * 683901; - s16 = 0; s3 += s15 * 666643; s4 += s15 * 470296; @@ -2753,7 +2763,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s6 -= s15 * 997805; s7 += s15 * 136657; s8 -= s15 * 683901; - s15 = 0; s2 += s14 * 666643; s3 += s14 * 470296; @@ -2761,7 +2770,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s5 -= s14 * 997805; s6 += s14 * 136657; s7 -= s14 * 683901; - s14 = 0; s1 += s13 * 666643; s2 += s13 * 470296; @@ -2769,7 +2777,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s4 -= s13 * 997805; s5 += s13 * 136657; s6 -= s13 * 683901; - s13 = 0; s0 += s12 * 666643; s1 += s12 * 470296; @@ -2820,7 +2827,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; - s12 = 0; carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; @@ -2890,4 +2896,4 @@ int sc_isnonzero(const unsigned char *s) { s[9] | s[10] | s[11] | s[12] | s[13] | s[14] | s[15] | s[16] | s[17] | s[18] | s[19] | s[20] | s[21] | s[22] | s[23] | s[24] | s[25] | s[26] | s[27] | s[28] | s[29] | s[30] | s[31]) - 1) >> 8) + 1; -}
\ No newline at end of file +} diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h index 3c253bb5e..9d07fc8b0 100644 --- a/src/crypto/crypto-ops.h +++ b/src/crypto/crypto-ops.h @@ -1,3 +1,7 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #pragma once /* From fe.h */ @@ -112,4 +116,4 @@ void sc_add(unsigned char *, const unsigned char *, const unsigned char *); void sc_sub(unsigned char *, const unsigned char *, const unsigned char *); void sc_mulsub(unsigned char *, const unsigned char *, const unsigned char *, const unsigned char *); int sc_check(const unsigned char *); -int sc_isnonzero(const unsigned char *); /* Doesn't normalize */
\ No newline at end of file +int sc_isnonzero(const unsigned char *); /* Doesn't normalize */ diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index 020e413e9..f5f525700 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -1,3 +1,7 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include <alloca.h> #include <cassert> #include <cstddef> @@ -5,23 +9,31 @@ #include <cstdlib> #include <cstring> #include <memory> +#include <mutex> +#include "common/varint.h" +#include "warnings.h" #include "crypto.h" -#include "random.h" +#include "hash.h" namespace crypto { using std::abort; using std::int32_t; using std::int64_t; + using std::lock_guard; + using std::mutex; using std::size_t; using std::uint32_t; using std::uint64_t; extern "C" { #include "crypto-ops.h" +#include "random.h" } + mutex random_lock; + static inline unsigned char *operator &(ec_point &point) { return &reinterpret_cast<unsigned char &>(point); } @@ -46,11 +58,12 @@ namespace crypto { } static inline void hash_to_scalar(const void *data, size_t length, ec_scalar &res) { - keccak(data, length, reinterpret_cast<hash &>(res)); + cn_fast_hash(data, length, reinterpret_cast<hash &>(res)); sc_reduce32(&res); } void crypto_ops::generate_keys(public_key &pub, secret_key &sec) { + lock_guard<mutex> lock(random_lock); ge_p3 point; random_scalar(sec); ge_scalarmult_base(&point, &sec); @@ -62,13 +75,79 @@ namespace crypto { return ge_frombytes_vartime(&point, &key) == 0; } + bool crypto_ops::secret_key_to_public_key(const secret_key &sec, public_key &pub) { + ge_p3 point; + if (sc_check(&sec) != 0) { + return false; + } + ge_scalarmult_base(&point, &sec); + ge_p3_tobytes(&pub, &point); + return true; + } + + bool crypto_ops::generate_key_derivation(const public_key &key1, const secret_key &key2, key_derivation &derivation) { + ge_p3 point; + ge_p2 point2; + ge_p1p1 point3; + assert(sc_check(&key2) == 0); + if (ge_frombytes_vartime(&point, &key1) != 0) { + return false; + } + ge_scalarmult(&point2, &key2, &point); + ge_mul8(&point3, &point2); + ge_p1p1_to_p2(&point2, &point3); + ge_tobytes(&derivation, &point2); + return true; + } + + static void derivation_to_scalar(const key_derivation &derivation, size_t output_index, ec_scalar &res) { + struct { + key_derivation derivation; + char output_index[(sizeof(size_t) * 8 + 6) / 7]; + } buf; + char *end = buf.output_index; + buf.derivation = derivation; + tools::write_varint(end, output_index); + assert(end <= buf.output_index + sizeof buf.output_index); + hash_to_scalar(&buf, end - reinterpret_cast<char *>(&buf), res); + } + + bool crypto_ops::derive_public_key(const key_derivation &derivation, size_t output_index, + const public_key &base, public_key &derived_key) { + ec_scalar scalar; + ge_p3 point1; + ge_p3 point2; + ge_cached point3; + ge_p1p1 point4; + ge_p2 point5; + if (ge_frombytes_vartime(&point1, &base) != 0) { + return false; + } + derivation_to_scalar(derivation, output_index, scalar); + ge_scalarmult_base(&point2, &scalar); + ge_p3_to_cached(&point3, &point2); + ge_add(&point4, &point1, &point3); + ge_p1p1_to_p2(&point5, &point4); + ge_tobytes(&derived_key, &point5); + return true; + } + + void crypto_ops::derive_secret_key(const key_derivation &derivation, size_t output_index, + const secret_key &base, secret_key &derived_key) { + ec_scalar scalar; + assert(sc_check(&base) == 0); + derivation_to_scalar(derivation, output_index, scalar); + sc_add(&derived_key, &base, &scalar); + } + struct s_comm { hash h; ec_point key; ec_point comm; }; - void crypto_ops::generate_signature(const hash &message_hash, const public_key &pub, const secret_key &sec, signature &sig) { + void crypto_ops::generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig) { + lock_guard<mutex> lock(random_lock); ge_p3 tmp3; ec_scalar k; s_comm buf; @@ -82,7 +161,7 @@ namespace crypto { assert(pub == t2); } #endif - buf.h = message_hash; + buf.h = prefix_hash; buf.key = pub; random_scalar(k); ge_scalarmult_base(&tmp3, &k); @@ -91,13 +170,13 @@ namespace crypto { sc_mulsub(&sig.r, &sig.c, &sec, &k); } - bool crypto_ops::check_signature(const hash &message_hash, const public_key &pub, const signature &sig) { + bool crypto_ops::check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig) { ge_p2 tmp2; ge_p3 tmp3; ec_scalar c; s_comm buf; assert(check_key(pub)); - buf.h = message_hash; + buf.h = prefix_hash; buf.key = pub; if (ge_frombytes_vartime(&tmp3, &pub) != 0) { abort(); @@ -116,7 +195,7 @@ namespace crypto { hash h; ge_p2 point; ge_p1p1 point2; - keccak(std::addressof(key), sizeof(public_key), h); + cn_fast_hash(std::addressof(key), sizeof(public_key), h); ge_fromfe_frombytes_vartime(&point, reinterpret_cast<const unsigned char *>(&h)); ge_mul8(&point2, &point); ge_p1p1_to_p3(&res, &point2); @@ -131,21 +210,25 @@ namespace crypto { ge_tobytes(&image, &point2); } +PUSH_WARNINGS +DISABLE_VS_WARNINGS(4200) struct rs_comm { hash h; struct { ec_point a, b; } ab[]; }; +POP_WARNINGS static inline size_t rs_comm_size(size_t pubs_count) { return sizeof(rs_comm) + pubs_count * sizeof(rs_comm().ab[0]); } - void crypto_ops::generate_ring_signature(const hash &message_hash, const key_image &image, + void crypto_ops::generate_ring_signature(const hash &prefix_hash, const key_image &image, const public_key *const *pubs, size_t pubs_count, const secret_key &sec, size_t sec_index, signature *sig) { + lock_guard<mutex> lock(random_lock); size_t i; ge_p3 image_unp; ge_dsmp image_pre; @@ -173,7 +256,7 @@ namespace crypto { } ge_dsm_precomp(image_pre, &image_unp); sc_0(&sum); - buf->h = message_hash; + buf->h = prefix_hash; for (i = 0; i < pubs_count; i++) { ge_p2 tmp2; ge_p3 tmp3; @@ -203,7 +286,7 @@ namespace crypto { sc_mulsub(&sig[sec_index].r, &sig[sec_index].c, &sec, &k); } - bool crypto_ops::check_ring_signature(const hash &message_hash, const key_image &image, + bool crypto_ops::check_ring_signature(const hash &prefix_hash, const key_image &image, const public_key *const *pubs, size_t pubs_count, const signature *sig) { size_t i; @@ -221,7 +304,7 @@ namespace crypto { } ge_dsm_precomp(image_pre, &image_unp); sc_0(&sum); - buf->h = message_hash; + buf->h = prefix_hash; for (i = 0; i < pubs_count; i++) { ge_p2 tmp2; ge_p3 tmp3; @@ -242,4 +325,4 @@ namespace crypto { sc_sub(&h, &h, &sum); return sc_isnonzero(&h) == 0; } -}
\ No newline at end of file +} diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 2dd2085ce..61641fbcf 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -1,66 +1,61 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #pragma once #include <cstddef> -#include <cstring> +#include <mutex> +#include <vector> + +#include "common/pod-class.h" +#include "generic-ops.h" +#include "hash.h" namespace crypto { + extern "C" { +#include "random.h" + } + + extern std::mutex random_lock; + #pragma pack(push, 1) - class hash { + POD_CLASS ec_point { char data[32]; }; - class ec_point { + POD_CLASS ec_scalar { char data[32]; }; - class ec_scalar { - char data[32]; + POD_CLASS public_key: ec_point { + friend class crypto_ops; }; - class public_key: ec_point { + POD_CLASS secret_key: ec_scalar { friend class crypto_ops; }; - class secret_key: ec_scalar { + POD_CLASS key_derivation: ec_point { friend class crypto_ops; }; - class key_image: ec_point { + POD_CLASS key_image: ec_point { friend class crypto_ops; }; - class signature { + POD_CLASS signature { ec_scalar c, r; friend class crypto_ops; }; #pragma pack(pop) - static_assert(sizeof(hash) == 32 && sizeof(ec_point) == 32 && - sizeof(ec_scalar) == 32 && sizeof(public_key) == 32 && - sizeof(secret_key) == 32 && sizeof(key_image) == 32 && + static_assert(sizeof(ec_point) == 32 && sizeof(ec_scalar) == 32 && + sizeof(public_key) == 32 && sizeof(secret_key) == 32 && + sizeof(key_derivation) == 32 && sizeof(key_image) == 32 && sizeof(signature) == 64, "Invalid structure size"); - extern "C" { - void keccak(const void *data, std::size_t length, char *hash); - } - - inline void keccak(const void *data, std::size_t length, hash &hash) { - keccak(data, length, reinterpret_cast<char *>(&hash)); - } - - inline bool operator==(const hash &a, const hash &b) { - return std::memcmp(&a, &b, sizeof(struct hash)) == 0; - } - - inline bool operator==(const public_key &a, const public_key &b) { - return std::memcmp(&a, &b, sizeof(struct public_key)) == 0; - } - - inline bool operator==(const key_image &a, const key_image &b) { - return std::memcmp(&a, &b, sizeof(struct key_image)) == 0; - } - class crypto_ops { crypto_ops(); crypto_ops(const crypto_ops &); @@ -71,6 +66,14 @@ namespace crypto { friend void generate_keys(public_key &, secret_key &); 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 &); + friend bool secret_key_to_public_key(const secret_key &, public_key &); + static bool generate_key_derivation(const public_key &, const secret_key &, key_derivation &); + friend bool generate_key_derivation(const public_key &, const secret_key &, key_derivation &); + static bool derive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &); + friend bool derive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &); + static void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &); + friend void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &); static void generate_signature(const hash &, const public_key &, const secret_key &, signature &); friend void generate_signature(const hash &, const public_key &, const secret_key &, signature &); static bool check_signature(const hash &, const public_key &, const signature &); @@ -87,80 +90,97 @@ namespace crypto { const public_key *const *, std::size_t, const signature *); }; - /* Generate a new key pair. - * pub: a newly generated public key. - * sec: a newly generated secret key. + /* Generate a value filled with random bytes. + */ + template<typename T> + typename std::enable_if<std::is_pod<T>::value, T>::type rand() { + typename std::remove_cv<T>::type res; + std::lock_guard<std::mutex> lock(random_lock); + generate_random_bytes(sizeof(T), &res); + return res; + } + + /* Generate a new key pair */ inline void generate_keys(public_key &pub, secret_key &sec) { crypto_ops::generate_keys(pub, sec); } - /* Check a public key. - * key: a key to check. - * returns: true if the key is valid, false otherwise. + /* Check a public key. Returns true if it is valid, false otherwise. */ inline bool check_key(const public_key &key) { return crypto_ops::check_key(key); } - /* Sign a message. - * message_hash: hash of a message. - * pub: public key used for signing. Assumed to be valid. - * sec: secret key used for signing. Assumed to correspond to pub. - * sig: the resulting signature. + /* Checks a private key and computes the corresponding public key. */ - inline void generate_signature(const hash &message_hash, const public_key &pub, const secret_key &sec, signature &sig) { - crypto_ops::generate_signature(message_hash, pub, sec, sig); + inline bool secret_key_to_public_key(const secret_key &sec, public_key &pub) { + return crypto_ops::secret_key_to_public_key(sec, pub); } - /* Verify a signature. - * message_hash: hash of a message. - * pub: public key used for signing. Assumed to be valid, use check_key to check it first. - * sig: a signature. - * returns: true if the signature is valid, false otherwise. + /* To generate an ephemeral key used to send money to: + * * The sender generates a new key pair, which becomes the transaction key. The public transaction key is included in "extra" field. + * * Both the sender and the receiver generate key derivation from the transaction key, the receivers' "view" key and the output index. + * * The sender uses key derivation and the receivers' "spend" key to derive an ephemeral public key. + * * The receiver can either derive the public key (to check that the transaction is addressed to him) or the private key (to spend the money). */ - inline bool check_signature(const hash &message_hash, const public_key &pub, const signature &sig) { - return crypto_ops::check_signature(message_hash, pub, sig); + inline bool generate_key_derivation(const public_key &key1, const secret_key &key2, key_derivation &derivation) { + return crypto_ops::generate_key_derivation(key1, key2, derivation); + } + inline bool derive_public_key(const key_derivation &derivation, std::size_t output_index, + const public_key &base, public_key &derived_key) { + return crypto_ops::derive_public_key(derivation, output_index, base, derived_key); + } + inline void derive_secret_key(const key_derivation &derivation, std::size_t output_index, + const secret_key &base, secret_key &derived_key) { + crypto_ops::derive_secret_key(derivation, output_index, base, derived_key); } - /* Generate the image of a key. - * pub: public key used for signing. Assumed to be valid. - * sec: secret key used for signing. Assumed to correspond to pub. - * image: the resulting key image. + /* Generation and checking of a standard signature. */ - inline void generate_key_image(const public_key &pub, const secret_key &sec, key_image &image) { - crypto_ops::generate_key_image(pub, sec, image); + inline void generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig) { + crypto_ops::generate_signature(prefix_hash, pub, sec, sig); + } + inline bool check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig) { + return crypto_ops::check_signature(prefix_hash, pub, sig); } - /* Sign a message using linkable ring signature. - * message_hash: hash of a message. - * image: image of the key used for signing. Use generate_key_image to create it. Assumed to correspond to the key used for signing. - * pubs: pointer to an array of pointers to public keys of a ring. All keys are assumed to be valid, use check_key to check them first. - * pubs_count: number of keys in a ring. - * sec: secret key used for signing. - * sec_index: index of the key used for signing in pubs. It is assumed that 0 <= sec_index < pubs_count and that sec corresponds to *pubs[sec_index]. - * sig: the resulting signature (occupies pubs_count elements). To verify it, image of the key is also necessary. + /* To send money to a key: + * * The sender generates an ephemeral key and includes it in transaction output. + * * To spend the money, the receiver generates a key image from it. + * * Then he selects a bunch of outputs, including the one he spends, and uses them to generate a ring signature. + * To check the signature, it is necessary to collect all the keys that were used to generate it. To detect double spends, it is necessary to check that each key image is used at most once. */ - inline void generate_ring_signature(const hash &message_hash, const key_image &image, + inline void generate_key_image(const public_key &pub, const secret_key &sec, key_image &image) { + crypto_ops::generate_key_image(pub, sec, image); + } + inline void generate_ring_signature(const hash &prefix_hash, const key_image &image, const public_key *const *pubs, std::size_t pubs_count, const secret_key &sec, std::size_t sec_index, signature *sig) { - crypto_ops::generate_ring_signature(message_hash, image, pubs, pubs_count, sec, sec_index, sig); + crypto_ops::generate_ring_signature(prefix_hash, image, pubs, pubs_count, sec, sec_index, sig); + } + inline bool check_ring_signature(const hash &prefix_hash, const key_image &image, + const public_key *const *pubs, std::size_t pubs_count, + const signature *sig) { + return crypto_ops::check_ring_signature(prefix_hash, image, pubs, pubs_count, sig); } - /* Verify a linkable ring signature. - * message_hash: hash of a message. - * image: image of the key used for signing. - * pubs: pointer to an array of pointers to public keys of a ring. All keys are assumed to be valid, use check_key to check them first. - * pubs_count: number of keys in a ring. - * sig: a signature (occupies pubs_count elements). - * returns: true if the signature is valid, false otherwise. + /* Variants with vector<const public_key *> parameters. */ - inline bool check_ring_signature(const hash &message_hash, const key_image &image, - const public_key *const *pubs, std::size_t pubs_count, + inline void generate_ring_signature(const hash &prefix_hash, const key_image &image, + const std::vector<const public_key *> &pubs, + const secret_key &sec, std::size_t sec_index, + signature *sig) { + generate_ring_signature(prefix_hash, image, pubs.data(), pubs.size(), sec, sec_index, sig); + } + inline bool check_ring_signature(const hash &prefix_hash, const key_image &image, + const std::vector<const public_key *> &pubs, const signature *sig) { - return crypto_ops::check_ring_signature(message_hash, image, pubs, pubs_count, sig); + return check_ring_signature(prefix_hash, image, pubs.data(), pubs.size(), sig); } +} - /* To check whether two signatures are linked, compare their key images. */ -}
\ No newline at end of file +CRYPTO_MAKE_COMPARABLE(public_key) +CRYPTO_MAKE_HASHABLE(key_image) +CRYPTO_MAKE_COMPARABLE(signature) diff --git a/src/crypto/example.cpp b/src/crypto/example.cpp deleted file mode 100644 index a8b6b7462..000000000 --- a/src/crypto/example.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include <assert.h> - -#include "crypto.h" -#include "random.h" - -using namespace crypto; - -int main(int argc, char *argv[]) { - char message[10]; - hash mh; - public_key pk1, pk2; - secret_key sk1, sk2; - key_image ki; - public_key *ppk[2]; - signature sig[2]; - bool res; - /* Call this before using functions that depend on randomness. */ - init_random(); - /* Generate a random message. */ - generate_random_bytes(sizeof message, message); - /* Find its hash */ - keccak(message, sizeof message, mh); - /* Generate some keys */ - generate_keys(pk1, sk1); - generate_keys(pk2, sk2); - /* Sign the message */ - generate_signature(mh, pk1, sk1, sig[0]); - /* Check the signature */ - res = check_signature(mh, pk1, sig[0]); - assert(res); - /* Sign the message using ring signature */ - /* First, generate a key image */ - generate_key_image(pk2, sk2, ki); - /* Then, generate the signature */ - ppk[0] = &pk1; - ppk[1] = &pk2; - generate_ring_signature(mh, ki, ppk, 2, sk2, 1, sig); - /* Check it */ - res = check_ring_signature(mh, ki, ppk, 2, sig); - assert(res); - return 0; -}
\ No newline at end of file diff --git a/src/crypto/generic-ops.h b/src/crypto/generic-ops.h new file mode 100644 index 000000000..8cade72a8 --- /dev/null +++ b/src/crypto/generic-ops.h @@ -0,0 +1,36 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <cstddef> +#include <cstring> +#include <functional> + +#define CRYPTO_MAKE_COMPARABLE(type) \ +namespace crypto { \ + inline bool operator==(const type &_v1, const type &_v2) { \ + return std::memcmp(&_v1, &_v2, sizeof(type)) == 0; \ + } \ + inline bool operator!=(const type &_v1, const type &_v2) { \ + return std::memcmp(&_v1, &_v2, sizeof(type)) != 0; \ + } \ +} + +#define CRYPTO_MAKE_HASHABLE(type) \ +CRYPTO_MAKE_COMPARABLE(type) \ +namespace crypto { \ + static_assert(sizeof(std::size_t) <= sizeof(type), "Size of " #type " must be at least that of size_t"); \ + inline std::size_t hash_value(const type &_v) { \ + return reinterpret_cast<const std::size_t &>(_v); \ + } \ +} \ +namespace std { \ + template<> \ + struct hash<crypto::type> { \ + std::size_t operator()(const crypto::type &_v) const { \ + return reinterpret_cast<const std::size_t &>(_v); \ + } \ + }; \ +} diff --git a/src/crypto/groestl.c b/src/crypto/groestl.c new file mode 100644 index 000000000..00bf987c9 --- /dev/null +++ b/src/crypto/groestl.c @@ -0,0 +1,360 @@ +/* hash.c April 2012 + * Groestl ANSI C code optimised for 32-bit machines + * Author: Thomas Krinninger + * + * This work is based on the implementation of + * Soeren S. Thomsen and Krystian Matusiewicz + * + * + */ + +#include "groestl.h" +#include "groestl_tables.h" + +#define P_TYPE 0 +#define Q_TYPE 1 + +const uint8_t shift_Values[2][8] = {{0,1,2,3,4,5,6,7},{1,3,5,7,0,2,4,6}}; + +const uint8_t indices_cyclic[15] = {0,1,2,3,4,5,6,7,0,1,2,3,4,5,6}; + + +#define ROTATE_COLUMN_DOWN(v1, v2, amount_bytes, temp_var) {temp_var = (v1<<(8*amount_bytes))|(v2>>(8*(4-amount_bytes))); \ + v2 = (v2<<(8*amount_bytes))|(v1>>(8*(4-amount_bytes))); \ + v1 = temp_var;} + + +#define COLUMN(x,y,i,c0,c1,c2,c3,c4,c5,c6,c7,tv1,tv2,tu,tl,t) \ + tu = T[2*(uint32_t)x[4*c0+0]]; \ + tl = T[2*(uint32_t)x[4*c0+0]+1]; \ + tv1 = T[2*(uint32_t)x[4*c1+1]]; \ + tv2 = T[2*(uint32_t)x[4*c1+1]+1]; \ + ROTATE_COLUMN_DOWN(tv1,tv2,1,t) \ + tu ^= tv1; \ + tl ^= tv2; \ + tv1 = T[2*(uint32_t)x[4*c2+2]]; \ + tv2 = T[2*(uint32_t)x[4*c2+2]+1]; \ + ROTATE_COLUMN_DOWN(tv1,tv2,2,t) \ + tu ^= tv1; \ + tl ^= tv2; \ + tv1 = T[2*(uint32_t)x[4*c3+3]]; \ + tv2 = T[2*(uint32_t)x[4*c3+3]+1]; \ + ROTATE_COLUMN_DOWN(tv1,tv2,3,t) \ + tu ^= tv1; \ + tl ^= tv2; \ + tl ^= T[2*(uint32_t)x[4*c4+0]]; \ + tu ^= T[2*(uint32_t)x[4*c4+0]+1]; \ + tv1 = T[2*(uint32_t)x[4*c5+1]]; \ + tv2 = T[2*(uint32_t)x[4*c5+1]+1]; \ + ROTATE_COLUMN_DOWN(tv1,tv2,1,t) \ + tl ^= tv1; \ + tu ^= tv2; \ + tv1 = T[2*(uint32_t)x[4*c6+2]]; \ + tv2 = T[2*(uint32_t)x[4*c6+2]+1]; \ + ROTATE_COLUMN_DOWN(tv1,tv2,2,t) \ + tl ^= tv1; \ + tu ^= tv2; \ + tv1 = T[2*(uint32_t)x[4*c7+3]]; \ + tv2 = T[2*(uint32_t)x[4*c7+3]+1]; \ + ROTATE_COLUMN_DOWN(tv1,tv2,3,t) \ + tl ^= tv1; \ + tu ^= tv2; \ + y[i] = tu; \ + y[i+1] = tl; + + +/* compute one round of P (short variants) */ +static void RND512P(uint8_t *x, uint32_t *y, uint32_t r) { + uint32_t temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp; + uint32_t* x32 = (uint32_t*)x; + x32[ 0] ^= 0x00000000^r; + x32[ 2] ^= 0x00000010^r; + x32[ 4] ^= 0x00000020^r; + x32[ 6] ^= 0x00000030^r; + x32[ 8] ^= 0x00000040^r; + x32[10] ^= 0x00000050^r; + x32[12] ^= 0x00000060^r; + x32[14] ^= 0x00000070^r; + COLUMN(x,y, 0, 0, 2, 4, 6, 9, 11, 13, 15, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y, 2, 2, 4, 6, 8, 11, 13, 15, 1, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y, 4, 4, 6, 8, 10, 13, 15, 1, 3, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y, 6, 6, 8, 10, 12, 15, 1, 3, 5, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y, 8, 8, 10, 12, 14, 1, 3, 5, 7, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y,10, 10, 12, 14, 0, 3, 5, 7, 9, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y,12, 12, 14, 0, 2, 5, 7, 9, 11, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y,14, 14, 0, 2, 4, 7, 9, 11, 13, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); +} + +/* compute one round of Q (short variants) */ +static void RND512Q(uint8_t *x, uint32_t *y, uint32_t r) { + uint32_t temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp; + uint32_t* x32 = (uint32_t*)x; + x32[ 0] = ~x32[ 0]; + x32[ 1] ^= 0xffffffff^r; + x32[ 2] = ~x32[ 2]; + x32[ 3] ^= 0xefffffff^r; + x32[ 4] = ~x32[ 4]; + x32[ 5] ^= 0xdfffffff^r; + x32[ 6] = ~x32[ 6]; + x32[ 7] ^= 0xcfffffff^r; + x32[ 8] = ~x32[ 8]; + x32[ 9] ^= 0xbfffffff^r; + x32[10] = ~x32[10]; + x32[11] ^= 0xafffffff^r; + x32[12] = ~x32[12]; + x32[13] ^= 0x9fffffff^r; + x32[14] = ~x32[14]; + x32[15] ^= 0x8fffffff^r; + COLUMN(x,y, 0, 2, 6, 10, 14, 1, 5, 9, 13, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y, 2, 4, 8, 12, 0, 3, 7, 11, 15, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y, 4, 6, 10, 14, 2, 5, 9, 13, 1, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y, 6, 8, 12, 0, 4, 7, 11, 15, 3, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y, 8, 10, 14, 2, 6, 9, 13, 1, 5, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y,10, 12, 0, 4, 8, 11, 15, 3, 7, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y,12, 14, 2, 6, 10, 13, 1, 5, 9, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y,14, 0, 4, 8, 12, 15, 3, 7, 11, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); +} + +/* compute compression function (short variants) */ +static void F512(uint32_t *h, const uint32_t *m) { + int i; + uint32_t Ptmp[2*COLS512]; + uint32_t Qtmp[2*COLS512]; + uint32_t y[2*COLS512]; + uint32_t z[2*COLS512]; + + for (i = 0; i < 2*COLS512; i++) { + z[i] = m[i]; + Ptmp[i] = h[i]^m[i]; + } + + /* compute Q(m) */ + RND512Q((uint8_t*)z, y, 0x00000000); + RND512Q((uint8_t*)y, z, 0x01000000); + RND512Q((uint8_t*)z, y, 0x02000000); + RND512Q((uint8_t*)y, z, 0x03000000); + RND512Q((uint8_t*)z, y, 0x04000000); + RND512Q((uint8_t*)y, z, 0x05000000); + RND512Q((uint8_t*)z, y, 0x06000000); + RND512Q((uint8_t*)y, z, 0x07000000); + RND512Q((uint8_t*)z, y, 0x08000000); + RND512Q((uint8_t*)y, Qtmp, 0x09000000); + + /* compute P(h+m) */ + RND512P((uint8_t*)Ptmp, y, 0x00000000); + RND512P((uint8_t*)y, z, 0x00000001); + RND512P((uint8_t*)z, y, 0x00000002); + RND512P((uint8_t*)y, z, 0x00000003); + RND512P((uint8_t*)z, y, 0x00000004); + RND512P((uint8_t*)y, z, 0x00000005); + RND512P((uint8_t*)z, y, 0x00000006); + RND512P((uint8_t*)y, z, 0x00000007); + RND512P((uint8_t*)z, y, 0x00000008); + RND512P((uint8_t*)y, Ptmp, 0x00000009); + + /* compute P(h+m) + Q(m) + h */ + for (i = 0; i < 2*COLS512; i++) { + h[i] ^= Ptmp[i]^Qtmp[i]; + } +} + + +/* digest up to msglen bytes of input (full blocks only) */ +static void Transform(hashState *ctx, + const uint8_t *input, + int msglen) { + + /* digest message, one block at a time */ + for (; msglen >= SIZE512; + msglen -= SIZE512, input += SIZE512) { + F512(ctx->chaining,(uint32_t*)input); + + /* increment block counter */ + ctx->block_counter1++; + if (ctx->block_counter1 == 0) ctx->block_counter2++; + } +} + +/* given state h, do h <- P(h)+h */ +static void OutputTransformation(hashState *ctx) { + int j; + uint32_t temp[2*COLS512]; + uint32_t y[2*COLS512]; + uint32_t z[2*COLS512]; + + + + for (j = 0; j < 2*COLS512; j++) { + temp[j] = ctx->chaining[j]; + } + RND512P((uint8_t*)temp, y, 0x00000000); + RND512P((uint8_t*)y, z, 0x00000001); + RND512P((uint8_t*)z, y, 0x00000002); + RND512P((uint8_t*)y, z, 0x00000003); + RND512P((uint8_t*)z, y, 0x00000004); + RND512P((uint8_t*)y, z, 0x00000005); + RND512P((uint8_t*)z, y, 0x00000006); + RND512P((uint8_t*)y, z, 0x00000007); + RND512P((uint8_t*)z, y, 0x00000008); + RND512P((uint8_t*)y, temp, 0x00000009); + for (j = 0; j < 2*COLS512; j++) { + ctx->chaining[j] ^= temp[j]; + } +} + +/* initialise context */ +static void Init(hashState* ctx) { + int i = 0; + /* allocate memory for state and data buffer */ + + for(;i<(SIZE512/sizeof(uint32_t));i++) + { + ctx->chaining[i] = 0; + } + + /* set initial value */ + ctx->chaining[2*COLS512-1] = u32BIG((uint32_t)HASH_BIT_LEN); + + /* set other variables */ + ctx->buf_ptr = 0; + ctx->block_counter1 = 0; + ctx->block_counter2 = 0; + ctx->bits_in_last_byte = 0; +} + +/* update state with databitlen bits of input */ +static void Update(hashState* ctx, + const BitSequence* input, + DataLength databitlen) { + int index = 0; + int msglen = (int)(databitlen/8); + int rem = (int)(databitlen%8); + + /* if the buffer contains data that has not yet been digested, first + add data to buffer until full */ + if (ctx->buf_ptr) { + while (ctx->buf_ptr < SIZE512 && index < msglen) { + ctx->buffer[(int)ctx->buf_ptr++] = input[index++]; + } + if (ctx->buf_ptr < SIZE512) { + /* buffer still not full, return */ + if (rem) { + ctx->bits_in_last_byte = rem; + ctx->buffer[(int)ctx->buf_ptr++] = input[index]; + } + return; + } + + /* digest buffer */ + ctx->buf_ptr = 0; + Transform(ctx, ctx->buffer, SIZE512); + } + + /* digest bulk of message */ + Transform(ctx, input+index, msglen-index); + index += ((msglen-index)/SIZE512)*SIZE512; + + /* store remaining data in buffer */ + while (index < msglen) { + ctx->buffer[(int)ctx->buf_ptr++] = input[index++]; + } + + /* if non-integral number of bytes have been supplied, store + remaining bits in last byte, together with information about + number of bits */ + if (rem) { + ctx->bits_in_last_byte = rem; + ctx->buffer[(int)ctx->buf_ptr++] = input[index]; + } +} + +#define BILB ctx->bits_in_last_byte + +/* finalise: process remaining data (including padding), perform + output transformation, and write hash result to 'output' */ +static void Final(hashState* ctx, + BitSequence* output) { + int i, j = 0, hashbytelen = HASH_BIT_LEN/8; + uint8_t *s = (BitSequence*)ctx->chaining; + + /* pad with '1'-bit and first few '0'-bits */ + if (BILB) { + ctx->buffer[(int)ctx->buf_ptr-1] &= ((1<<BILB)-1)<<(8-BILB); + ctx->buffer[(int)ctx->buf_ptr-1] ^= 0x1<<(7-BILB); + BILB = 0; + } + else ctx->buffer[(int)ctx->buf_ptr++] = 0x80; + + /* pad with '0'-bits */ + if (ctx->buf_ptr > SIZE512-LENGTHFIELDLEN) { + /* padding requires two blocks */ + while (ctx->buf_ptr < SIZE512) { + ctx->buffer[(int)ctx->buf_ptr++] = 0; + } + /* digest first padding block */ + Transform(ctx, ctx->buffer, SIZE512); + ctx->buf_ptr = 0; + } + while (ctx->buf_ptr < SIZE512-LENGTHFIELDLEN) { + ctx->buffer[(int)ctx->buf_ptr++] = 0; + } + + /* length padding */ + ctx->block_counter1++; + if (ctx->block_counter1 == 0) ctx->block_counter2++; + ctx->buf_ptr = SIZE512; + + while (ctx->buf_ptr > SIZE512-(int)sizeof(uint32_t)) { + ctx->buffer[(int)--ctx->buf_ptr] = (uint8_t)ctx->block_counter1; + ctx->block_counter1 >>= 8; + } + while (ctx->buf_ptr > SIZE512-LENGTHFIELDLEN) { + ctx->buffer[(int)--ctx->buf_ptr] = (uint8_t)ctx->block_counter2; + ctx->block_counter2 >>= 8; + } + /* digest final padding block */ + Transform(ctx, ctx->buffer, SIZE512); + /* perform output transformation */ + OutputTransformation(ctx); + + /* store hash result in output */ + for (i = SIZE512-hashbytelen; i < SIZE512; i++,j++) { + output[j] = s[i]; + } + + /* zeroise relevant variables and deallocate memory */ + for (i = 0; i < COLS512; i++) { + ctx->chaining[i] = 0; + } + for (i = 0; i < SIZE512; i++) { + ctx->buffer[i] = 0; + } +} + +/* hash bit sequence */ +void groestl(const BitSequence* data, + DataLength databitlen, + BitSequence* hashval) { + + hashState context; + + /* initialise */ + Init(&context); + + + /* process message */ + Update(&context, data, databitlen); + + /* finalise */ + Final(&context, hashval); +} +/* +static int crypto_hash(unsigned char *out, + const unsigned char *in, + unsigned long long len) +{ + groestl(in, 8*len, out); + return 0; +} + +*/
\ No newline at end of file diff --git a/src/crypto/groestl.h b/src/crypto/groestl.h new file mode 100644 index 000000000..078515cee --- /dev/null +++ b/src/crypto/groestl.h @@ -0,0 +1,60 @@ +#ifndef __hash_h +#define __hash_h +/* +#include "crypto_uint8.h" +#include "crypto_uint32.h" +#include "crypto_uint64.h" +#include "crypto_hash.h" + +typedef crypto_uint8 uint8_t; +typedef crypto_uint32 uint32_t; +typedef crypto_uint64 uint64_t; +*/ +#include <stdint.h> + +/* some sizes (number of bytes) */ +#define ROWS 8 +#define LENGTHFIELDLEN ROWS +#define COLS512 8 + +#define SIZE512 (ROWS*COLS512) + +#define ROUNDS512 10 +#define HASH_BIT_LEN 256 + +#define ROTL32(v, n) ((((v)<<(n))|((v)>>(32-(n))))&li_32(ffffffff)) + + +#define li_32(h) 0x##h##u +#define EXT_BYTE(var,n) ((uint8_t)((uint32_t)(var) >> (8*n))) +#define u32BIG(a) \ + ((ROTL32(a,8) & li_32(00FF00FF)) | \ + (ROTL32(a,24) & li_32(FF00FF00))) + + +/* NIST API begin */ +typedef unsigned char BitSequence; +typedef unsigned long long DataLength; +typedef struct { + uint32_t chaining[SIZE512/sizeof(uint32_t)]; /* actual state */ + uint32_t block_counter1, + block_counter2; /* message block counter(s) */ + BitSequence buffer[SIZE512]; /* data buffer */ + int buf_ptr; /* data buffer pointer */ + int bits_in_last_byte; /* no. of message bits in last byte of + data buffer */ +} hashState; + +/*void Init(hashState*); +void Update(hashState*, const BitSequence*, DataLength); +void Final(hashState*, BitSequence*); */ +void groestl(const BitSequence*, DataLength, BitSequence*); +/* NIST API end */ + +/* +int crypto_hash(unsigned char *out, + const unsigned char *in, + unsigned long long len); +*/ + +#endif /* __hash_h */ diff --git a/src/crypto/groestl_tables.h b/src/crypto/groestl_tables.h new file mode 100644 index 000000000..a23295c35 --- /dev/null +++ b/src/crypto/groestl_tables.h @@ -0,0 +1,38 @@ +#ifndef __tables_h +#define __tables_h + + +const uint32_t T[512] = {0xa5f432c6, 0xc6a597f4, 0x84976ff8, 0xf884eb97, 0x99b05eee, 0xee99c7b0, 0x8d8c7af6, 0xf68df78c, 0xd17e8ff, 0xff0de517, 0xbddc0ad6, 0xd6bdb7dc, 0xb1c816de, 0xdeb1a7c8, 0x54fc6d91, 0x915439fc +, 0x50f09060, 0x6050c0f0, 0x3050702, 0x2030405, 0xa9e02ece, 0xcea987e0, 0x7d87d156, 0x567dac87, 0x192bcce7, 0xe719d52b, 0x62a613b5, 0xb56271a6, 0xe6317c4d, 0x4de69a31, 0x9ab559ec, 0xec9ac3b5 +, 0x45cf408f, 0x8f4505cf, 0x9dbca31f, 0x1f9d3ebc, 0x40c04989, 0x894009c0, 0x879268fa, 0xfa87ef92, 0x153fd0ef, 0xef15c53f, 0xeb2694b2, 0xb2eb7f26, 0xc940ce8e, 0x8ec90740, 0xb1de6fb, 0xfb0bed1d +, 0xec2f6e41, 0x41ec822f, 0x67a91ab3, 0xb3677da9, 0xfd1c435f, 0x5ffdbe1c, 0xea256045, 0x45ea8a25, 0xbfdaf923, 0x23bf46da, 0xf7025153, 0x53f7a602, 0x96a145e4, 0xe496d3a1, 0x5bed769b, 0x9b5b2ded +, 0xc25d2875, 0x75c2ea5d, 0x1c24c5e1, 0xe11cd924, 0xaee9d43d, 0x3dae7ae9, 0x6abef24c, 0x4c6a98be, 0x5aee826c, 0x6c5ad8ee, 0x41c3bd7e, 0x7e41fcc3, 0x206f3f5, 0xf502f106, 0x4fd15283, 0x834f1dd1 +, 0x5ce48c68, 0x685cd0e4, 0xf4075651, 0x51f4a207, 0x345c8dd1, 0xd134b95c, 0x818e1f9, 0xf908e918, 0x93ae4ce2, 0xe293dfae, 0x73953eab, 0xab734d95, 0x53f59762, 0x6253c4f5, 0x3f416b2a, 0x2a3f5441 +, 0xc141c08, 0x80c1014, 0x52f66395, 0x955231f6, 0x65afe946, 0x46658caf, 0x5ee27f9d, 0x9d5e21e2, 0x28784830, 0x30286078, 0xa1f8cf37, 0x37a16ef8, 0xf111b0a, 0xa0f1411, 0xb5c4eb2f, 0x2fb55ec4 +, 0x91b150e, 0xe091c1b, 0x365a7e24, 0x2436485a, 0x9bb6ad1b, 0x1b9b36b6, 0x3d4798df, 0xdf3da547, 0x266aa7cd, 0xcd26816a, 0x69bbf54e, 0x4e699cbb, 0xcd4c337f, 0x7fcdfe4c, 0x9fba50ea, 0xea9fcfba +, 0x1b2d3f12, 0x121b242d, 0x9eb9a41d, 0x1d9e3ab9, 0x749cc458, 0x5874b09c, 0x2e724634, 0x342e6872, 0x2d774136, 0x362d6c77, 0xb2cd11dc, 0xdcb2a3cd, 0xee299db4, 0xb4ee7329, 0xfb164d5b, 0x5bfbb616 +, 0xf601a5a4, 0xa4f65301, 0x4dd7a176, 0x764decd7, 0x61a314b7, 0xb76175a3, 0xce49347d, 0x7dcefa49, 0x7b8ddf52, 0x527ba48d, 0x3e429fdd, 0xdd3ea142, 0x7193cd5e, 0x5e71bc93, 0x97a2b113, 0x139726a2 +, 0xf504a2a6, 0xa6f55704, 0x68b801b9, 0xb96869b8, 0x0, 0x0, 0x2c74b5c1, 0xc12c9974, 0x60a0e040, 0x406080a0, 0x1f21c2e3, 0xe31fdd21, 0xc8433a79, 0x79c8f243, 0xed2c9ab6, 0xb6ed772c +, 0xbed90dd4, 0xd4beb3d9, 0x46ca478d, 0x8d4601ca, 0xd9701767, 0x67d9ce70, 0x4bddaf72, 0x724be4dd, 0xde79ed94, 0x94de3379, 0xd467ff98, 0x98d42b67, 0xe82393b0, 0xb0e87b23, 0x4ade5b85, 0x854a11de +, 0x6bbd06bb, 0xbb6b6dbd, 0x2a7ebbc5, 0xc52a917e, 0xe5347b4f, 0x4fe59e34, 0x163ad7ed, 0xed16c13a, 0xc554d286, 0x86c51754, 0xd762f89a, 0x9ad72f62, 0x55ff9966, 0x6655ccff, 0x94a7b611, 0x119422a7 +, 0xcf4ac08a, 0x8acf0f4a, 0x1030d9e9, 0xe910c930, 0x60a0e04, 0x406080a, 0x819866fe, 0xfe81e798, 0xf00baba0, 0xa0f05b0b, 0x44ccb478, 0x7844f0cc, 0xbad5f025, 0x25ba4ad5, 0xe33e754b, 0x4be3963e +, 0xf30eaca2, 0xa2f35f0e, 0xfe19445d, 0x5dfeba19, 0xc05bdb80, 0x80c01b5b, 0x8a858005, 0x58a0a85, 0xadecd33f, 0x3fad7eec, 0xbcdffe21, 0x21bc42df, 0x48d8a870, 0x7048e0d8, 0x40cfdf1, 0xf104f90c +, 0xdf7a1963, 0x63dfc67a, 0xc1582f77, 0x77c1ee58, 0x759f30af, 0xaf75459f, 0x63a5e742, 0x426384a5, 0x30507020, 0x20304050, 0x1a2ecbe5, 0xe51ad12e, 0xe12effd, 0xfd0ee112, 0x6db708bf, 0xbf6d65b7 +, 0x4cd45581, 0x814c19d4, 0x143c2418, 0x1814303c, 0x355f7926, 0x26354c5f, 0x2f71b2c3, 0xc32f9d71, 0xe13886be, 0xbee16738, 0xa2fdc835, 0x35a26afd, 0xcc4fc788, 0x88cc0b4f, 0x394b652e, 0x2e395c4b +, 0x57f96a93, 0x93573df9, 0xf20d5855, 0x55f2aa0d, 0x829d61fc, 0xfc82e39d, 0x47c9b37a, 0x7a47f4c9, 0xacef27c8, 0xc8ac8bef, 0xe73288ba, 0xbae76f32, 0x2b7d4f32, 0x322b647d, 0x95a442e6, 0xe695d7a4 +, 0xa0fb3bc0, 0xc0a09bfb, 0x98b3aa19, 0x199832b3, 0xd168f69e, 0x9ed12768, 0x7f8122a3, 0xa37f5d81, 0x66aaee44, 0x446688aa, 0x7e82d654, 0x547ea882, 0xabe6dd3b, 0x3bab76e6, 0x839e950b, 0xb83169e +, 0xca45c98c, 0x8cca0345, 0x297bbcc7, 0xc729957b, 0xd36e056b, 0x6bd3d66e, 0x3c446c28, 0x283c5044, 0x798b2ca7, 0xa779558b, 0xe23d81bc, 0xbce2633d, 0x1d273116, 0x161d2c27, 0x769a37ad, 0xad76419a +, 0x3b4d96db, 0xdb3bad4d, 0x56fa9e64, 0x6456c8fa, 0x4ed2a674, 0x744ee8d2, 0x1e223614, 0x141e2822, 0xdb76e492, 0x92db3f76, 0xa1e120c, 0xc0a181e, 0x6cb4fc48, 0x486c90b4, 0xe4378fb8, 0xb8e46b37 +, 0x5de7789f, 0x9f5d25e7, 0x6eb20fbd, 0xbd6e61b2, 0xef2a6943, 0x43ef862a, 0xa6f135c4, 0xc4a693f1, 0xa8e3da39, 0x39a872e3, 0xa4f7c631, 0x31a462f7, 0x37598ad3, 0xd337bd59, 0x8b8674f2, 0xf28bff86 +, 0x325683d5, 0xd532b156, 0x43c54e8b, 0x8b430dc5, 0x59eb856e, 0x6e59dceb, 0xb7c218da, 0xdab7afc2, 0x8c8f8e01, 0x18c028f, 0x64ac1db1, 0xb16479ac, 0xd26df19c, 0x9cd2236d, 0xe03b7249, 0x49e0923b +, 0xb4c71fd8, 0xd8b4abc7, 0xfa15b9ac, 0xacfa4315, 0x709faf3, 0xf307fd09, 0x256fa0cf, 0xcf25856f, 0xafea20ca, 0xcaaf8fea, 0x8e897df4, 0xf48ef389, 0xe9206747, 0x47e98e20, 0x18283810, 0x10182028 +, 0xd5640b6f, 0x6fd5de64, 0x888373f0, 0xf088fb83, 0x6fb1fb4a, 0x4a6f94b1, 0x7296ca5c, 0x5c72b896, 0x246c5438, 0x3824706c, 0xf1085f57, 0x57f1ae08, 0xc7522173, 0x73c7e652, 0x51f36497, 0x975135f3 +, 0x2365aecb, 0xcb238d65, 0x7c8425a1, 0xa17c5984, 0x9cbf57e8, 0xe89ccbbf, 0x21635d3e, 0x3e217c63, 0xdd7cea96, 0x96dd377c, 0xdc7f1e61, 0x61dcc27f, 0x86919c0d, 0xd861a91, 0x85949b0f, 0xf851e94 +, 0x90ab4be0, 0xe090dbab, 0x42c6ba7c, 0x7c42f8c6, 0xc4572671, 0x71c4e257, 0xaae529cc, 0xccaa83e5, 0xd873e390, 0x90d83b73, 0x50f0906, 0x6050c0f, 0x103f4f7, 0xf701f503, 0x12362a1c, 0x1c123836 +, 0xa3fe3cc2, 0xc2a39ffe, 0x5fe18b6a, 0x6a5fd4e1, 0xf910beae, 0xaef94710, 0xd06b0269, 0x69d0d26b, 0x91a8bf17, 0x17912ea8, 0x58e87199, 0x995829e8, 0x2769533a, 0x3a277469, 0xb9d0f727, 0x27b94ed0 +, 0x384891d9, 0xd938a948, 0x1335deeb, 0xeb13cd35, 0xb3cee52b, 0x2bb356ce, 0x33557722, 0x22334455, 0xbbd604d2, 0xd2bbbfd6, 0x709039a9, 0xa9704990, 0x89808707, 0x7890e80, 0xa7f2c133, 0x33a766f2 +, 0xb6c1ec2d, 0x2db65ac1, 0x22665a3c, 0x3c227866, 0x92adb815, 0x15922aad, 0x2060a9c9, 0xc9208960, 0x49db5c87, 0x874915db, 0xff1ab0aa, 0xaaff4f1a, 0x7888d850, 0x5078a088, 0x7a8e2ba5, 0xa57a518e +, 0x8f8a8903, 0x38f068a, 0xf8134a59, 0x59f8b213, 0x809b9209, 0x980129b, 0x1739231a, 0x1a173439, 0xda751065, 0x65daca75, 0x315384d7, 0xd731b553, 0xc651d584, 0x84c61351, 0xb8d303d0, 0xd0b8bbd3 +, 0xc35edc82, 0x82c31f5e, 0xb0cbe229, 0x29b052cb, 0x7799c35a, 0x5a77b499, 0x11332d1e, 0x1e113c33, 0xcb463d7b, 0x7bcbf646, 0xfc1fb7a8, 0xa8fc4b1f, 0xd6610c6d, 0x6dd6da61, 0x3a4e622c, 0x2c3a584e}; + +#endif /* __tables_h */ diff --git a/src/crypto/hash-extra-blake.c b/src/crypto/hash-extra-blake.c new file mode 100644 index 000000000..2eeb52020 --- /dev/null +++ b/src/crypto/hash-extra-blake.c @@ -0,0 +1,12 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <stddef.h> +#include <stdint.h> + +#include "blake256.h" + +void hash_extra_blake(const void *data, size_t length, char *hash) { + blake256_hash((uint8_t*)hash, data, length); +} diff --git a/src/crypto/hash-extra-groestl.c b/src/crypto/hash-extra-groestl.c new file mode 100644 index 000000000..7918cfc53 --- /dev/null +++ b/src/crypto/hash-extra-groestl.c @@ -0,0 +1,12 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <stddef.h> +#include <stdint.h> + +#include "groestl.h" + +void hash_extra_groestl(const void *data, size_t length, char *hash) { + groestl(data, length * 8, (uint8_t*)hash); +} diff --git a/src/crypto/hash-extra-jh.c b/src/crypto/hash-extra-jh.c new file mode 100644 index 000000000..15c271b2a --- /dev/null +++ b/src/crypto/hash-extra-jh.c @@ -0,0 +1,16 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <assert.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#include "jh.h" +#include "hash-ops.h" + +void hash_extra_jh(const void *data, size_t length, char *hash) { + int r = jh_hash(HASH_SIZE * 8, data, 8 * length, (uint8_t*)hash); + assert(SUCCESS == r); +} diff --git a/src/crypto/hash-extra-skein.c b/src/crypto/hash-extra-skein.c new file mode 100644 index 000000000..92361e6db --- /dev/null +++ b/src/crypto/hash-extra-skein.c @@ -0,0 +1,14 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <stddef.h> +#include <stdint.h> + +#include "hash-ops.h" +#include "skein.h" + +void hash_extra_skein(const void *data, size_t length, char *hash) { + int r = skein_hash(8 * HASH_SIZE, data, 8 * length, (uint8_t*)hash); + assert(SKEIN_SUCCESS == r); +} diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h new file mode 100644 index 000000000..9e6c821ef --- /dev/null +++ b/src/crypto/hash-ops.h @@ -0,0 +1,63 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#if !defined(__cplusplus) + +#include <assert.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include "common/int-util.h" +#include "warnings.h" + +static inline void *padd(void *p, size_t i) { + return (char *) p + i; +} + +static inline const void *cpadd(const void *p, size_t i) { + return (const char *) p + i; +} + +PUSH_WARNINGS +DISABLE_VS_WARNINGS(4267) +static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, "size_t must be 4 or 8 bytes long"); +static inline void place_length(uint8_t *buffer, size_t bufsize, size_t length) { + if (sizeof(size_t) == 4) { + *(uint32_t *) padd(buffer, bufsize - 4) = swap32be(length); + } else { + *(uint64_t *) padd(buffer, bufsize - 8) = swap64be(length); + } +} +POP_WARNINGS + +#pragma pack(push, 1) +union hash_state { + uint8_t b[200]; + uint64_t w[25]; +}; +#pragma pack(pop) +static_assert(sizeof(union hash_state) == 200, "Invalid structure size"); + +void hash_permutation(union hash_state *state); +void hash_process(union hash_state *state, const uint8_t *buf, size_t count); + +#endif + +enum { + HASH_SIZE = 32, + HASH_DATA_AREA = 136 +}; + +void cn_fast_hash(const void *data, size_t length, char *hash); +void cn_slow_hash(const void *data, size_t length, char *hash); + +void hash_extra_blake(const void *data, size_t length, char *hash); +void hash_extra_groestl(const void *data, size_t length, char *hash); +void hash_extra_jh(const void *data, size_t length, char *hash); +void hash_extra_skein(const void *data, size_t length, char *hash); + +void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash); diff --git a/src/crypto/hash.c b/src/crypto/hash.c index f07ffd661..a3989d88c 100644 --- a/src/crypto/hash.c +++ b/src/crypto/hash.c @@ -1,82 +1,24 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include <stddef.h> #include <stdint.h> #include <string.h> -#include "int-util.h" -#include "hash.h" - -static const uint64_t round_constants[] = { - 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000, - 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009, - 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, - 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003, - 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a, - 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008 -}; +#include "hash-ops.h" +#include "keccak.h" -static const uint8_t rho_offsets[] = { - 0, 1, 62, 28, 27, - 36, 44, 6, 55, 20, - 3, 10, 43, 25, 39, - 41, 45, 15, 21, 8, - 18, 2, 61, 56, 14 -}; - -static void keccak_round(uint64_t *state, unsigned int i) { - unsigned int j, k; - uint64_t tmp[25]; - for(j = 0; j < 5; j++) { - tmp[j] = state[j] ^ state[j + 5] ^ state[j + 10] ^ state[j + 15] ^ state[j + 20]; - } - for(j = 0; j < 5; j++) { - uint64_t t = rol64(tmp[(j + 1) % 5], 1) ^ tmp[(j + 4) % 5]; - state[j] ^= t; - state[j + 5] ^= t; - state[j + 10] ^= t; - state[j + 15] ^= t; - state[j + 20] ^= t; - } - for(j = 0; j < 25; j++) { - state[j] = rol64(state[j], rho_offsets[j]); - } -#define index(x, y) (((x) % 5) + 5 * ((y) % 5)) - for(j = 0; j < 5; j++) { - for(k = 0; k < 5; k++) { - tmp[index(0 * k + 1 * j, 2 * k + 3 * j)] = state[index(k, j)]; - } - } - for(j = 0; j < 5; j++) { - for(k = 0; k < 5; k++) { - state[index(k, j)] = tmp[index(k, j)] ^ ((~tmp[index(k + 1, j)]) & tmp[index(k + 2, j)]); - } - } -#undef index - state[0] ^= round_constants[i]; +void hash_permutation(union hash_state *state) { + keccakf((uint64_t*)state, 24); } -void keccak_permutation(struct keccak_state *state) { - unsigned int i; - mem_inplace_swap64le(&state, 25); - for (i = 0; i < 24; i++) { - keccak_round(state->w, i); - } - mem_inplace_swap64le(&state, 25); +void hash_process(union hash_state *state, const uint8_t *buf, size_t count) { + keccak1600(buf, count, (uint8_t*)state); } -void keccak(const void *data, size_t length, char *hash) { - struct keccak_state state; - const void *limit = cpadd(data, length); - size_t i; - memset(&state, 0, sizeof(struct keccak_state)); - for (i = 0; data != limit; ++data) { - state.b[i] ^= *(char *) data; - if (++i == 136) { - keccak_permutation(&state); - i = 0; - } - } - state.b[i] ^= 1; - state.b[135] ^= 0x80; - keccak_permutation(&state); - memcpy(hash, &state, 32); -}
\ No newline at end of file +void cn_fast_hash(const void *data, size_t length, char *hash) { + union hash_state state; + hash_process(&state, data, length); + memcpy(hash, &state, HASH_SIZE); +} diff --git a/src/crypto/hash.h b/src/crypto/hash.h index 32e8f4701..fb65494b6 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -1,40 +1,50 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #pragma once -#include <assert.h> -#include <stdbool.h> #include <stddef.h> -#include <stdint.h> - -#include "int-util.h" -static inline void *padd(void *p, size_t i) { - return (char *) p + i; -} +#include "common/pod-class.h" +#include "generic-ops.h" -static inline const void *cpadd(const void *p, size_t i) { - return (const char *) p + i; -} +namespace crypto { -static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, "size_t must be 4 or 8 bytes long"); -static inline void place_length(uint8_t *buffer, size_t bufsize, size_t length) { - if (sizeof(size_t) == 4) { - *(uint32_t *) padd(buffer, bufsize - 4) = swap32be(length); - } else { - *(uint64_t *) padd(buffer, bufsize - 8) = swap64be(length); + extern "C" { +#include "hash-ops.h" } -} -struct keccak_state { - union { - uint8_t b[200]; - uint64_t w[25]; +#pragma pack(push, 1) + POD_CLASS hash { + char data[HASH_SIZE]; }; -}; +#pragma pack(pop) + + static_assert(sizeof(hash) == HASH_SIZE, "Invalid structure size"); + + /* + Cryptonight hash functions + */ + + inline void cn_fast_hash(const void *data, std::size_t length, hash &hash) { + cn_fast_hash(data, length, reinterpret_cast<char *>(&hash)); + } + + inline hash cn_fast_hash(const void *data, std::size_t length) { + hash h; + cn_fast_hash(data, length, reinterpret_cast<char *>(&h)); + return h; + } + + inline void cn_slow_hash(const void *data, std::size_t length, hash &hash) { + cn_slow_hash(data, length, reinterpret_cast<char *>(&hash)); + } + + inline void tree_hash(const hash *hashes, std::size_t count, hash &root_hash) { + tree_hash(reinterpret_cast<const char (*)[HASH_SIZE]>(hashes), count, reinterpret_cast<char *>(&root_hash)); + } -enum { - HASH_SIZE = 32, - HASH_DATA_AREA = 136 -}; +} -void keccak(const void *data, size_t length, char *hash); -void keccak_permutation(struct keccak_state *state);
\ No newline at end of file +CRYPTO_MAKE_HASHABLE(hash) diff --git a/src/crypto/initializer.h b/src/crypto/initializer.h new file mode 100644 index 000000000..8c84621bf --- /dev/null +++ b/src/crypto/initializer.h @@ -0,0 +1,32 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#if defined(__GNUC__) +#define INITIALIZER(name) __attribute__((constructor(101))) static void name(void) +#define FINALIZER(name) __attribute__((destructor(101))) static void name(void) +#define REGISTER_FINALIZER(name) ((void) 0) + +#elif defined(_MSC_VER) +#include <assert.h> +#include <stdlib.h> +// http://stackoverflow.com/questions/1113409/attribute-constructor-equivalent-in-vc +// http://msdn.microsoft.com/en-us/library/bb918180.aspx +#pragma section(".CRT$XCT", read) +#define INITIALIZER(name) \ + static void __cdecl name(void); \ + __declspec(allocate(".CRT$XCT")) void (__cdecl *const _##name)(void) = &name; \ + static void __cdecl name(void) +#define FINALIZER(name) \ + static void __cdecl name(void) +#define REGISTER_FINALIZER(name) \ + do { \ + int _res = atexit(name); \ + assert(_res == 0); \ + } while (0); + +#else +#error Unsupported compiler +#endif diff --git a/src/crypto/int-util.h b/src/crypto/int-util.h deleted file mode 100644 index 933f2e784..000000000 --- a/src/crypto/int-util.h +++ /dev/null @@ -1,121 +0,0 @@ -#pragma once - -#include <stdint.h> -#include <string.h> -#include <sys/param.h> - -static inline uint32_t rol32(uint32_t x, int r) { - return (x << (r & 31)) | (x >> (-r & 31)); -} - -static inline uint64_t rol64(uint64_t x, int r) { - return (x << (r & 63)) | (x >> (-r & 63)); -} - -#define IDENT32(x) ((uint32_t) (x)) -#define IDENT64(x) ((uint64_t) (x)) - -#define SWAP32(x) ((((uint32_t) (x) & 0x000000ff) << 24) | \ - (((uint32_t) (x) & 0x0000ff00) << 8) | \ - (((uint32_t) (x) & 0x00ff0000) >> 8) | \ - (((uint32_t) (x) & 0xff000000) >> 24)) -#define SWAP64(x) ((((uint64_t) (x) & 0x00000000000000ff) << 56) | \ - (((uint64_t) (x) & 0x000000000000ff00) << 40) | \ - (((uint64_t) (x) & 0x0000000000ff0000) << 24) | \ - (((uint64_t) (x) & 0x00000000ff000000) << 8) | \ - (((uint64_t) (x) & 0x000000ff00000000) >> 8) | \ - (((uint64_t) (x) & 0x0000ff0000000000) >> 24) | \ - (((uint64_t) (x) & 0x00ff000000000000) >> 40) | \ - (((uint64_t) (x) & 0xff00000000000000) >> 56)) - -static inline uint32_t ident32(uint32_t x) { return x; } -static inline uint64_t ident64(uint64_t x) { return x; } - -static inline uint32_t swap32(uint32_t x) { - x = ((x & 0x00ff00ff) << 8) | ((x & 0xff00ff00) >> 8); - return (x << 16) | (x >> 16); -} -static inline uint64_t swap64(uint64_t x) { - x = ((x & 0x00ff00ff00ff00ff) << 8) | ((x & 0xff00ff00ff00ff00) >> 8); - x = ((x & 0x0000ffff0000ffff) << 16) | ((x & 0xffff0000ffff0000) >> 16); - return (x << 32) | (x >> 32); -} - -#if defined(__GNUC__) -#define UNUSED __attribute__((unused)) -#else -#define UNUSED -#endif -static inline void mem_inplace_ident(void *mem UNUSED, size_t n UNUSED) { } -#undef UNUSED - -static inline void mem_inplace_swap32(void *mem, size_t n) { - size_t i; - for (i = 0; i < n; i++) { - ((uint32_t *) mem)[i] = swap32(((const uint32_t *) mem)[i]); - } -} -static inline void mem_inplace_swap64(void *mem, size_t n) { - size_t i; - for (i = 0; i < n; i++) { - ((uint64_t *) mem)[i] = swap64(((const uint64_t *) mem)[i]); - } -} - -static inline void memcpy_ident32(void *dst, const void *src, size_t n) { - memcpy(dst, src, 4 * n); -} -static inline void memcpy_ident64(void *dst, const void *src, size_t n) { - memcpy(dst, src, 8 * n); -} - -static inline void memcpy_swap32(void *dst, const void *src, size_t n) { - size_t i; - for (i = 0; i < n; i++) { - ((uint32_t *) dst)[i] = swap32(((const uint32_t *) src)[i]); - } -} -static inline void memcpy_swap64(void *dst, const void *src, size_t n) { - size_t i; - for (i = 0; i < n; i++) { - ((uint64_t *) dst)[i] = swap64(((const uint64_t *) src)[i]); - } -} - -#if BYTE_ORDER == LITTLE_ENDIAN -#define SWAP32LE IDENT32 -#define SWAP32BE SWAP32 -#define swap32le ident32 -#define swap32be swap32 -#define mem_inplace_swap32le mem_inplace_ident -#define mem_inplace_swap32be mem_inplace_swap32 -#define memcpy_swap32le memcpy_ident32 -#define memcpy_swap32be memcpy_swap32 -#define SWAP64LE IDENT64 -#define SWAP64BE SWAP64 -#define swap64le ident64 -#define swap64be swap64 -#define mem_inplace_swap64le mem_inplace_ident -#define mem_inplace_swap64be mem_inplace_swap64 -#define memcpy_swap64le memcpy_ident64 -#define memcpy_swap64be memcpy_swap64 -#endif - -#if BYTE_ORDER == BIG_ENDIAN -#define SWAP32BE IDENT32 -#define SWAP32LE SWAP32 -#define swap32be ident32 -#define swap32le swap32 -#define mem_inplace_swap32be mem_inplace_ident -#define mem_inplace_swap32le mem_inplace_swap32 -#define memcpy_swap32be memcpy_ident32 -#define memcpy_swap32le memcpy_swap32 -#define SWAP64BE IDENT64 -#define SWAP64LE SWAP64 -#define swap64be ident64 -#define swap64le swap64 -#define mem_inplace_swap64be mem_inplace_ident -#define mem_inplace_swap64le mem_inplace_swap64 -#define memcpy_swap64be memcpy_ident64 -#define memcpy_swap64le memcpy_swap64 -#endif
\ No newline at end of file diff --git a/src/crypto/jh.c b/src/crypto/jh.c new file mode 100644 index 000000000..12d536375 --- /dev/null +++ b/src/crypto/jh.c @@ -0,0 +1,367 @@ +/*This program gives the 64-bit optimized bitslice implementation of JH using ANSI C + + -------------------------------- + Performance + + Microprocessor: Intel CORE 2 processor (Core 2 Duo Mobile T6600 2.2GHz) + Operating System: 64-bit Ubuntu 10.04 (Linux kernel 2.6.32-22-generic) + Speed for long message: + 1) 45.8 cycles/byte compiler: Intel C++ Compiler 11.1 compilation option: icc -O2 + 2) 56.8 cycles/byte compiler: gcc 4.4.3 compilation option: gcc -O3 + + -------------------------------- + Last Modified: January 16, 2011 +*/ + +#include "jh.h" + +#include <stdint.h> +#include <string.h> + +/*typedef unsigned long long uint64;*/ +typedef uint64_t uint64; + +/*define data alignment for different C compilers*/ +#if defined(__GNUC__) + #define DATA_ALIGN16(x) x __attribute__ ((aligned(16))) +#else + #define DATA_ALIGN16(x) __declspec(align(16)) x +#endif + + +typedef struct { + int hashbitlen; /*the message digest size*/ + unsigned long long databitlen; /*the message size in bits*/ + unsigned long long datasize_in_buffer; /*the size of the message remained in buffer; assumed to be multiple of 8bits except for the last partial block at the end of the message*/ + DATA_ALIGN16(uint64 x[8][2]); /*the 1024-bit state, ( x[i][0] || x[i][1] ) is the ith row of the state in the pseudocode*/ + unsigned char buffer[64]; /*the 512-bit message block to be hashed;*/ +} hashState; + + +/*The initial hash value H(0)*/ +const unsigned char JH224_H0[128]={0x2d,0xfe,0xdd,0x62,0xf9,0x9a,0x98,0xac,0xae,0x7c,0xac,0xd6,0x19,0xd6,0x34,0xe7,0xa4,0x83,0x10,0x5,0xbc,0x30,0x12,0x16,0xb8,0x60,0x38,0xc6,0xc9,0x66,0x14,0x94,0x66,0xd9,0x89,0x9f,0x25,0x80,0x70,0x6f,0xce,0x9e,0xa3,0x1b,0x1d,0x9b,0x1a,0xdc,0x11,0xe8,0x32,0x5f,0x7b,0x36,0x6e,0x10,0xf9,0x94,0x85,0x7f,0x2,0xfa,0x6,0xc1,0x1b,0x4f,0x1b,0x5c,0xd8,0xc8,0x40,0xb3,0x97,0xf6,0xa1,0x7f,0x6e,0x73,0x80,0x99,0xdc,0xdf,0x93,0xa5,0xad,0xea,0xa3,0xd3,0xa4,0x31,0xe8,0xde,0xc9,0x53,0x9a,0x68,0x22,0xb4,0xa9,0x8a,0xec,0x86,0xa1,0xe4,0xd5,0x74,0xac,0x95,0x9c,0xe5,0x6c,0xf0,0x15,0x96,0xd,0xea,0xb5,0xab,0x2b,0xbf,0x96,0x11,0xdc,0xf0,0xdd,0x64,0xea,0x6e}; +const unsigned char JH256_H0[128]={0xeb,0x98,0xa3,0x41,0x2c,0x20,0xd3,0xeb,0x92,0xcd,0xbe,0x7b,0x9c,0xb2,0x45,0xc1,0x1c,0x93,0x51,0x91,0x60,0xd4,0xc7,0xfa,0x26,0x0,0x82,0xd6,0x7e,0x50,0x8a,0x3,0xa4,0x23,0x9e,0x26,0x77,0x26,0xb9,0x45,0xe0,0xfb,0x1a,0x48,0xd4,0x1a,0x94,0x77,0xcd,0xb5,0xab,0x26,0x2,0x6b,0x17,0x7a,0x56,0xf0,0x24,0x42,0xf,0xff,0x2f,0xa8,0x71,0xa3,0x96,0x89,0x7f,0x2e,0x4d,0x75,0x1d,0x14,0x49,0x8,0xf7,0x7d,0xe2,0x62,0x27,0x76,0x95,0xf7,0x76,0x24,0x8f,0x94,0x87,0xd5,0xb6,0x57,0x47,0x80,0x29,0x6c,0x5c,0x5e,0x27,0x2d,0xac,0x8e,0xd,0x6c,0x51,0x84,0x50,0xc6,0x57,0x5,0x7a,0xf,0x7b,0xe4,0xd3,0x67,0x70,0x24,0x12,0xea,0x89,0xe3,0xab,0x13,0xd3,0x1c,0xd7,0x69}; +const unsigned char JH384_H0[128]={0x48,0x1e,0x3b,0xc6,0xd8,0x13,0x39,0x8a,0x6d,0x3b,0x5e,0x89,0x4a,0xde,0x87,0x9b,0x63,0xfa,0xea,0x68,0xd4,0x80,0xad,0x2e,0x33,0x2c,0xcb,0x21,0x48,0xf,0x82,0x67,0x98,0xae,0xc8,0x4d,0x90,0x82,0xb9,0x28,0xd4,0x55,0xea,0x30,0x41,0x11,0x42,0x49,0x36,0xf5,0x55,0xb2,0x92,0x48,0x47,0xec,0xc7,0x25,0xa,0x93,0xba,0xf4,0x3c,0xe1,0x56,0x9b,0x7f,0x8a,0x27,0xdb,0x45,0x4c,0x9e,0xfc,0xbd,0x49,0x63,0x97,0xaf,0xe,0x58,0x9f,0xc2,0x7d,0x26,0xaa,0x80,0xcd,0x80,0xc0,0x8b,0x8c,0x9d,0xeb,0x2e,0xda,0x8a,0x79,0x81,0xe8,0xf8,0xd5,0x37,0x3a,0xf4,0x39,0x67,0xad,0xdd,0xd1,0x7a,0x71,0xa9,0xb4,0xd3,0xbd,0xa4,0x75,0xd3,0x94,0x97,0x6c,0x3f,0xba,0x98,0x42,0x73,0x7f}; +const unsigned char JH512_H0[128]={0x6f,0xd1,0x4b,0x96,0x3e,0x0,0xaa,0x17,0x63,0x6a,0x2e,0x5,0x7a,0x15,0xd5,0x43,0x8a,0x22,0x5e,0x8d,0xc,0x97,0xef,0xb,0xe9,0x34,0x12,0x59,0xf2,0xb3,0xc3,0x61,0x89,0x1d,0xa0,0xc1,0x53,0x6f,0x80,0x1e,0x2a,0xa9,0x5,0x6b,0xea,0x2b,0x6d,0x80,0x58,0x8e,0xcc,0xdb,0x20,0x75,0xba,0xa6,0xa9,0xf,0x3a,0x76,0xba,0xf8,0x3b,0xf7,0x1,0x69,0xe6,0x5,0x41,0xe3,0x4a,0x69,0x46,0xb5,0x8a,0x8e,0x2e,0x6f,0xe6,0x5a,0x10,0x47,0xa7,0xd0,0xc1,0x84,0x3c,0x24,0x3b,0x6e,0x71,0xb1,0x2d,0x5a,0xc1,0x99,0xcf,0x57,0xf6,0xec,0x9d,0xb1,0xf8,0x56,0xa7,0x6,0x88,0x7c,0x57,0x16,0xb1,0x56,0xe3,0xc2,0xfc,0xdf,0xe6,0x85,0x17,0xfb,0x54,0x5a,0x46,0x78,0xcc,0x8c,0xdd,0x4b}; + +/*42 round constants, each round constant is 32-byte (256-bit)*/ +const unsigned char E8_bitslice_roundconstant[42][32]={ +{0x72,0xd5,0xde,0xa2,0xdf,0x15,0xf8,0x67,0x7b,0x84,0x15,0xa,0xb7,0x23,0x15,0x57,0x81,0xab,0xd6,0x90,0x4d,0x5a,0x87,0xf6,0x4e,0x9f,0x4f,0xc5,0xc3,0xd1,0x2b,0x40}, +{0xea,0x98,0x3a,0xe0,0x5c,0x45,0xfa,0x9c,0x3,0xc5,0xd2,0x99,0x66,0xb2,0x99,0x9a,0x66,0x2,0x96,0xb4,0xf2,0xbb,0x53,0x8a,0xb5,0x56,0x14,0x1a,0x88,0xdb,0xa2,0x31}, +{0x3,0xa3,0x5a,0x5c,0x9a,0x19,0xe,0xdb,0x40,0x3f,0xb2,0xa,0x87,0xc1,0x44,0x10,0x1c,0x5,0x19,0x80,0x84,0x9e,0x95,0x1d,0x6f,0x33,0xeb,0xad,0x5e,0xe7,0xcd,0xdc}, +{0x10,0xba,0x13,0x92,0x2,0xbf,0x6b,0x41,0xdc,0x78,0x65,0x15,0xf7,0xbb,0x27,0xd0,0xa,0x2c,0x81,0x39,0x37,0xaa,0x78,0x50,0x3f,0x1a,0xbf,0xd2,0x41,0x0,0x91,0xd3}, +{0x42,0x2d,0x5a,0xd,0xf6,0xcc,0x7e,0x90,0xdd,0x62,0x9f,0x9c,0x92,0xc0,0x97,0xce,0x18,0x5c,0xa7,0xb,0xc7,0x2b,0x44,0xac,0xd1,0xdf,0x65,0xd6,0x63,0xc6,0xfc,0x23}, +{0x97,0x6e,0x6c,0x3,0x9e,0xe0,0xb8,0x1a,0x21,0x5,0x45,0x7e,0x44,0x6c,0xec,0xa8,0xee,0xf1,0x3,0xbb,0x5d,0x8e,0x61,0xfa,0xfd,0x96,0x97,0xb2,0x94,0x83,0x81,0x97}, +{0x4a,0x8e,0x85,0x37,0xdb,0x3,0x30,0x2f,0x2a,0x67,0x8d,0x2d,0xfb,0x9f,0x6a,0x95,0x8a,0xfe,0x73,0x81,0xf8,0xb8,0x69,0x6c,0x8a,0xc7,0x72,0x46,0xc0,0x7f,0x42,0x14}, +{0xc5,0xf4,0x15,0x8f,0xbd,0xc7,0x5e,0xc4,0x75,0x44,0x6f,0xa7,0x8f,0x11,0xbb,0x80,0x52,0xde,0x75,0xb7,0xae,0xe4,0x88,0xbc,0x82,0xb8,0x0,0x1e,0x98,0xa6,0xa3,0xf4}, +{0x8e,0xf4,0x8f,0x33,0xa9,0xa3,0x63,0x15,0xaa,0x5f,0x56,0x24,0xd5,0xb7,0xf9,0x89,0xb6,0xf1,0xed,0x20,0x7c,0x5a,0xe0,0xfd,0x36,0xca,0xe9,0x5a,0x6,0x42,0x2c,0x36}, +{0xce,0x29,0x35,0x43,0x4e,0xfe,0x98,0x3d,0x53,0x3a,0xf9,0x74,0x73,0x9a,0x4b,0xa7,0xd0,0xf5,0x1f,0x59,0x6f,0x4e,0x81,0x86,0xe,0x9d,0xad,0x81,0xaf,0xd8,0x5a,0x9f}, +{0xa7,0x5,0x6,0x67,0xee,0x34,0x62,0x6a,0x8b,0xb,0x28,0xbe,0x6e,0xb9,0x17,0x27,0x47,0x74,0x7,0x26,0xc6,0x80,0x10,0x3f,0xe0,0xa0,0x7e,0x6f,0xc6,0x7e,0x48,0x7b}, +{0xd,0x55,0xa,0xa5,0x4a,0xf8,0xa4,0xc0,0x91,0xe3,0xe7,0x9f,0x97,0x8e,0xf1,0x9e,0x86,0x76,0x72,0x81,0x50,0x60,0x8d,0xd4,0x7e,0x9e,0x5a,0x41,0xf3,0xe5,0xb0,0x62}, +{0xfc,0x9f,0x1f,0xec,0x40,0x54,0x20,0x7a,0xe3,0xe4,0x1a,0x0,0xce,0xf4,0xc9,0x84,0x4f,0xd7,0x94,0xf5,0x9d,0xfa,0x95,0xd8,0x55,0x2e,0x7e,0x11,0x24,0xc3,0x54,0xa5}, +{0x5b,0xdf,0x72,0x28,0xbd,0xfe,0x6e,0x28,0x78,0xf5,0x7f,0xe2,0xf,0xa5,0xc4,0xb2,0x5,0x89,0x7c,0xef,0xee,0x49,0xd3,0x2e,0x44,0x7e,0x93,0x85,0xeb,0x28,0x59,0x7f}, +{0x70,0x5f,0x69,0x37,0xb3,0x24,0x31,0x4a,0x5e,0x86,0x28,0xf1,0x1d,0xd6,0xe4,0x65,0xc7,0x1b,0x77,0x4,0x51,0xb9,0x20,0xe7,0x74,0xfe,0x43,0xe8,0x23,0xd4,0x87,0x8a}, +{0x7d,0x29,0xe8,0xa3,0x92,0x76,0x94,0xf2,0xdd,0xcb,0x7a,0x9,0x9b,0x30,0xd9,0xc1,0x1d,0x1b,0x30,0xfb,0x5b,0xdc,0x1b,0xe0,0xda,0x24,0x49,0x4f,0xf2,0x9c,0x82,0xbf}, +{0xa4,0xe7,0xba,0x31,0xb4,0x70,0xbf,0xff,0xd,0x32,0x44,0x5,0xde,0xf8,0xbc,0x48,0x3b,0xae,0xfc,0x32,0x53,0xbb,0xd3,0x39,0x45,0x9f,0xc3,0xc1,0xe0,0x29,0x8b,0xa0}, +{0xe5,0xc9,0x5,0xfd,0xf7,0xae,0x9,0xf,0x94,0x70,0x34,0x12,0x42,0x90,0xf1,0x34,0xa2,0x71,0xb7,0x1,0xe3,0x44,0xed,0x95,0xe9,0x3b,0x8e,0x36,0x4f,0x2f,0x98,0x4a}, +{0x88,0x40,0x1d,0x63,0xa0,0x6c,0xf6,0x15,0x47,0xc1,0x44,0x4b,0x87,0x52,0xaf,0xff,0x7e,0xbb,0x4a,0xf1,0xe2,0xa,0xc6,0x30,0x46,0x70,0xb6,0xc5,0xcc,0x6e,0x8c,0xe6}, +{0xa4,0xd5,0xa4,0x56,0xbd,0x4f,0xca,0x0,0xda,0x9d,0x84,0x4b,0xc8,0x3e,0x18,0xae,0x73,0x57,0xce,0x45,0x30,0x64,0xd1,0xad,0xe8,0xa6,0xce,0x68,0x14,0x5c,0x25,0x67}, +{0xa3,0xda,0x8c,0xf2,0xcb,0xe,0xe1,0x16,0x33,0xe9,0x6,0x58,0x9a,0x94,0x99,0x9a,0x1f,0x60,0xb2,0x20,0xc2,0x6f,0x84,0x7b,0xd1,0xce,0xac,0x7f,0xa0,0xd1,0x85,0x18}, +{0x32,0x59,0x5b,0xa1,0x8d,0xdd,0x19,0xd3,0x50,0x9a,0x1c,0xc0,0xaa,0xa5,0xb4,0x46,0x9f,0x3d,0x63,0x67,0xe4,0x4,0x6b,0xba,0xf6,0xca,0x19,0xab,0xb,0x56,0xee,0x7e}, +{0x1f,0xb1,0x79,0xea,0xa9,0x28,0x21,0x74,0xe9,0xbd,0xf7,0x35,0x3b,0x36,0x51,0xee,0x1d,0x57,0xac,0x5a,0x75,0x50,0xd3,0x76,0x3a,0x46,0xc2,0xfe,0xa3,0x7d,0x70,0x1}, +{0xf7,0x35,0xc1,0xaf,0x98,0xa4,0xd8,0x42,0x78,0xed,0xec,0x20,0x9e,0x6b,0x67,0x79,0x41,0x83,0x63,0x15,0xea,0x3a,0xdb,0xa8,0xfa,0xc3,0x3b,0x4d,0x32,0x83,0x2c,0x83}, +{0xa7,0x40,0x3b,0x1f,0x1c,0x27,0x47,0xf3,0x59,0x40,0xf0,0x34,0xb7,0x2d,0x76,0x9a,0xe7,0x3e,0x4e,0x6c,0xd2,0x21,0x4f,0xfd,0xb8,0xfd,0x8d,0x39,0xdc,0x57,0x59,0xef}, +{0x8d,0x9b,0xc,0x49,0x2b,0x49,0xeb,0xda,0x5b,0xa2,0xd7,0x49,0x68,0xf3,0x70,0xd,0x7d,0x3b,0xae,0xd0,0x7a,0x8d,0x55,0x84,0xf5,0xa5,0xe9,0xf0,0xe4,0xf8,0x8e,0x65}, +{0xa0,0xb8,0xa2,0xf4,0x36,0x10,0x3b,0x53,0xc,0xa8,0x7,0x9e,0x75,0x3e,0xec,0x5a,0x91,0x68,0x94,0x92,0x56,0xe8,0x88,0x4f,0x5b,0xb0,0x5c,0x55,0xf8,0xba,0xbc,0x4c}, +{0xe3,0xbb,0x3b,0x99,0xf3,0x87,0x94,0x7b,0x75,0xda,0xf4,0xd6,0x72,0x6b,0x1c,0x5d,0x64,0xae,0xac,0x28,0xdc,0x34,0xb3,0x6d,0x6c,0x34,0xa5,0x50,0xb8,0x28,0xdb,0x71}, +{0xf8,0x61,0xe2,0xf2,0x10,0x8d,0x51,0x2a,0xe3,0xdb,0x64,0x33,0x59,0xdd,0x75,0xfc,0x1c,0xac,0xbc,0xf1,0x43,0xce,0x3f,0xa2,0x67,0xbb,0xd1,0x3c,0x2,0xe8,0x43,0xb0}, +{0x33,0xa,0x5b,0xca,0x88,0x29,0xa1,0x75,0x7f,0x34,0x19,0x4d,0xb4,0x16,0x53,0x5c,0x92,0x3b,0x94,0xc3,0xe,0x79,0x4d,0x1e,0x79,0x74,0x75,0xd7,0xb6,0xee,0xaf,0x3f}, +{0xea,0xa8,0xd4,0xf7,0xbe,0x1a,0x39,0x21,0x5c,0xf4,0x7e,0x9,0x4c,0x23,0x27,0x51,0x26,0xa3,0x24,0x53,0xba,0x32,0x3c,0xd2,0x44,0xa3,0x17,0x4a,0x6d,0xa6,0xd5,0xad}, +{0xb5,0x1d,0x3e,0xa6,0xaf,0xf2,0xc9,0x8,0x83,0x59,0x3d,0x98,0x91,0x6b,0x3c,0x56,0x4c,0xf8,0x7c,0xa1,0x72,0x86,0x60,0x4d,0x46,0xe2,0x3e,0xcc,0x8,0x6e,0xc7,0xf6}, +{0x2f,0x98,0x33,0xb3,0xb1,0xbc,0x76,0x5e,0x2b,0xd6,0x66,0xa5,0xef,0xc4,0xe6,0x2a,0x6,0xf4,0xb6,0xe8,0xbe,0xc1,0xd4,0x36,0x74,0xee,0x82,0x15,0xbc,0xef,0x21,0x63}, +{0xfd,0xc1,0x4e,0xd,0xf4,0x53,0xc9,0x69,0xa7,0x7d,0x5a,0xc4,0x6,0x58,0x58,0x26,0x7e,0xc1,0x14,0x16,0x6,0xe0,0xfa,0x16,0x7e,0x90,0xaf,0x3d,0x28,0x63,0x9d,0x3f}, +{0xd2,0xc9,0xf2,0xe3,0x0,0x9b,0xd2,0xc,0x5f,0xaa,0xce,0x30,0xb7,0xd4,0xc,0x30,0x74,0x2a,0x51,0x16,0xf2,0xe0,0x32,0x98,0xd,0xeb,0x30,0xd8,0xe3,0xce,0xf8,0x9a}, +{0x4b,0xc5,0x9e,0x7b,0xb5,0xf1,0x79,0x92,0xff,0x51,0xe6,0x6e,0x4,0x86,0x68,0xd3,0x9b,0x23,0x4d,0x57,0xe6,0x96,0x67,0x31,0xcc,0xe6,0xa6,0xf3,0x17,0xa,0x75,0x5}, +{0xb1,0x76,0x81,0xd9,0x13,0x32,0x6c,0xce,0x3c,0x17,0x52,0x84,0xf8,0x5,0xa2,0x62,0xf4,0x2b,0xcb,0xb3,0x78,0x47,0x15,0x47,0xff,0x46,0x54,0x82,0x23,0x93,0x6a,0x48}, +{0x38,0xdf,0x58,0x7,0x4e,0x5e,0x65,0x65,0xf2,0xfc,0x7c,0x89,0xfc,0x86,0x50,0x8e,0x31,0x70,0x2e,0x44,0xd0,0xb,0xca,0x86,0xf0,0x40,0x9,0xa2,0x30,0x78,0x47,0x4e}, +{0x65,0xa0,0xee,0x39,0xd1,0xf7,0x38,0x83,0xf7,0x5e,0xe9,0x37,0xe4,0x2c,0x3a,0xbd,0x21,0x97,0xb2,0x26,0x1,0x13,0xf8,0x6f,0xa3,0x44,0xed,0xd1,0xef,0x9f,0xde,0xe7}, +{0x8b,0xa0,0xdf,0x15,0x76,0x25,0x92,0xd9,0x3c,0x85,0xf7,0xf6,0x12,0xdc,0x42,0xbe,0xd8,0xa7,0xec,0x7c,0xab,0x27,0xb0,0x7e,0x53,0x8d,0x7d,0xda,0xaa,0x3e,0xa8,0xde}, +{0xaa,0x25,0xce,0x93,0xbd,0x2,0x69,0xd8,0x5a,0xf6,0x43,0xfd,0x1a,0x73,0x8,0xf9,0xc0,0x5f,0xef,0xda,0x17,0x4a,0x19,0xa5,0x97,0x4d,0x66,0x33,0x4c,0xfd,0x21,0x6a}, +{0x35,0xb4,0x98,0x31,0xdb,0x41,0x15,0x70,0xea,0x1e,0xf,0xbb,0xed,0xcd,0x54,0x9b,0x9a,0xd0,0x63,0xa1,0x51,0x97,0x40,0x72,0xf6,0x75,0x9d,0xbf,0x91,0x47,0x6f,0xe2}}; + + +static void E8(hashState *state); /*The bijective function E8, in bitslice form*/ +static void F8(hashState *state); /*The compression function F8 */ + +/*The API functions*/ +static HashReturn Init(hashState *state, int hashbitlen); +static HashReturn Update(hashState *state, const BitSequence *data, DataLength databitlen); +static HashReturn Final(hashState *state, BitSequence *hashval); +HashReturn jh_hash(int hashbitlen, const BitSequence *data,DataLength databitlen, BitSequence *hashval); + +/*swapping bit 2i with bit 2i+1 of 64-bit x*/ +#define SWAP1(x) (x) = ((((x) & 0x5555555555555555ULL) << 1) | (((x) & 0xaaaaaaaaaaaaaaaaULL) >> 1)); +/*swapping bits 4i||4i+1 with bits 4i+2||4i+3 of 64-bit x*/ +#define SWAP2(x) (x) = ((((x) & 0x3333333333333333ULL) << 2) | (((x) & 0xccccccccccccccccULL) >> 2)); +/*swapping bits 8i||8i+1||8i+2||8i+3 with bits 8i+4||8i+5||8i+6||8i+7 of 64-bit x*/ +#define SWAP4(x) (x) = ((((x) & 0x0f0f0f0f0f0f0f0fULL) << 4) | (((x) & 0xf0f0f0f0f0f0f0f0ULL) >> 4)); +/*swapping bits 16i||16i+1||......||16i+7 with bits 16i+8||16i+9||......||16i+15 of 64-bit x*/ +#define SWAP8(x) (x) = ((((x) & 0x00ff00ff00ff00ffULL) << 8) | (((x) & 0xff00ff00ff00ff00ULL) >> 8)); +/*swapping bits 32i||32i+1||......||32i+15 with bits 32i+16||32i+17||......||32i+31 of 64-bit x*/ +#define SWAP16(x) (x) = ((((x) & 0x0000ffff0000ffffULL) << 16) | (((x) & 0xffff0000ffff0000ULL) >> 16)); +/*swapping bits 64i||64i+1||......||64i+31 with bits 64i+32||64i+33||......||64i+63 of 64-bit x*/ +#define SWAP32(x) (x) = (((x) << 32) | ((x) >> 32)); + +/*The MDS transform*/ +#define L(m0,m1,m2,m3,m4,m5,m6,m7) \ + (m4) ^= (m1); \ + (m5) ^= (m2); \ + (m6) ^= (m0) ^ (m3); \ + (m7) ^= (m0); \ + (m0) ^= (m5); \ + (m1) ^= (m6); \ + (m2) ^= (m4) ^ (m7); \ + (m3) ^= (m4); + +/*Two Sboxes are computed in parallel, each Sbox implements S0 and S1, selected by a constant bit*/ +/*The reason to compute two Sboxes in parallel is to try to fully utilize the parallel processing power*/ +#define SS(m0,m1,m2,m3,m4,m5,m6,m7,cc0,cc1) \ + m3 = ~(m3); \ + m7 = ~(m7); \ + m0 ^= ((~(m2)) & (cc0)); \ + m4 ^= ((~(m6)) & (cc1)); \ + temp0 = (cc0) ^ ((m0) & (m1));\ + temp1 = (cc1) ^ ((m4) & (m5));\ + m0 ^= ((m2) & (m3)); \ + m4 ^= ((m6) & (m7)); \ + m3 ^= ((~(m1)) & (m2)); \ + m7 ^= ((~(m5)) & (m6)); \ + m1 ^= ((m0) & (m2)); \ + m5 ^= ((m4) & (m6)); \ + m2 ^= ((m0) & (~(m3))); \ + m6 ^= ((m4) & (~(m7))); \ + m0 ^= ((m1) | (m3)); \ + m4 ^= ((m5) | (m7)); \ + m3 ^= ((m1) & (m2)); \ + m7 ^= ((m5) & (m6)); \ + m1 ^= (temp0 & (m0)); \ + m5 ^= (temp1 & (m4)); \ + m2 ^= temp0; \ + m6 ^= temp1; + +/*The bijective function E8, in bitslice form*/ +static void E8(hashState *state) +{ + uint64 i,roundnumber,temp0,temp1; + + for (roundnumber = 0; roundnumber < 42; roundnumber = roundnumber+7) { + /*round 7*roundnumber+0: Sbox, MDS and Swapping layers*/ + for (i = 0; i < 2; i++) { + SS(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i],((uint64*)E8_bitslice_roundconstant[roundnumber+0])[i],((uint64*)E8_bitslice_roundconstant[roundnumber+0])[i+2] ); + L(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i]); + SWAP1(state->x[1][i]); SWAP1(state->x[3][i]); SWAP1(state->x[5][i]); SWAP1(state->x[7][i]); + } + + /*round 7*roundnumber+1: Sbox, MDS and Swapping layers*/ + for (i = 0; i < 2; i++) { + SS(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i],((uint64*)E8_bitslice_roundconstant[roundnumber+1])[i],((uint64*)E8_bitslice_roundconstant[roundnumber+1])[i+2] ); + L(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i]); + SWAP2(state->x[1][i]); SWAP2(state->x[3][i]); SWAP2(state->x[5][i]); SWAP2(state->x[7][i]); + } + + /*round 7*roundnumber+2: Sbox, MDS and Swapping layers*/ + for (i = 0; i < 2; i++) { + SS(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i],((uint64*)E8_bitslice_roundconstant[roundnumber+2])[i],((uint64*)E8_bitslice_roundconstant[roundnumber+2])[i+2] ); + L(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i]); + SWAP4(state->x[1][i]); SWAP4(state->x[3][i]); SWAP4(state->x[5][i]); SWAP4(state->x[7][i]); + } + + /*round 7*roundnumber+3: Sbox, MDS and Swapping layers*/ + for (i = 0; i < 2; i++) { + SS(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i],((uint64*)E8_bitslice_roundconstant[roundnumber+3])[i],((uint64*)E8_bitslice_roundconstant[roundnumber+3])[i+2] ); + L(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i]); + SWAP8(state->x[1][i]); SWAP8(state->x[3][i]); SWAP8(state->x[5][i]); SWAP8(state->x[7][i]); + } + + /*round 7*roundnumber+4: Sbox, MDS and Swapping layers*/ + for (i = 0; i < 2; i++) { + SS(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i],((uint64*)E8_bitslice_roundconstant[roundnumber+4])[i],((uint64*)E8_bitslice_roundconstant[roundnumber+4])[i+2] ); + L(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i]); + SWAP16(state->x[1][i]); SWAP16(state->x[3][i]); SWAP16(state->x[5][i]); SWAP16(state->x[7][i]); + } + + /*round 7*roundnumber+5: Sbox, MDS and Swapping layers*/ + for (i = 0; i < 2; i++) { + SS(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i],((uint64*)E8_bitslice_roundconstant[roundnumber+5])[i],((uint64*)E8_bitslice_roundconstant[roundnumber+5])[i+2] ); + L(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i]); + SWAP32(state->x[1][i]); SWAP32(state->x[3][i]); SWAP32(state->x[5][i]); SWAP32(state->x[7][i]); + } + + /*round 7*roundnumber+6: Sbox and MDS layers*/ + for (i = 0; i < 2; i++) { + SS(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i],((uint64*)E8_bitslice_roundconstant[roundnumber+6])[i],((uint64*)E8_bitslice_roundconstant[roundnumber+6])[i+2] ); + L(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i]); + } + /*round 7*roundnumber+6: swapping layer*/ + for (i = 1; i < 8; i = i+2) { + temp0 = state->x[i][0]; state->x[i][0] = state->x[i][1]; state->x[i][1] = temp0; + } + } + +} + +/*The compression function F8 */ +static void F8(hashState *state) +{ + uint64 i; + + /*xor the 512-bit message with the fist half of the 1024-bit hash state*/ + for (i = 0; i < 8; i++) state->x[i >> 1][i & 1] ^= ((uint64*)state->buffer)[i]; + + /*the bijective function E8 */ + E8(state); + + /*xor the 512-bit message with the second half of the 1024-bit hash state*/ + for (i = 0; i < 8; i++) state->x[(8+i) >> 1][(8+i) & 1] ^= ((uint64*)state->buffer)[i]; +} + +/*before hashing a message, initialize the hash state as H0 */ +static HashReturn Init(hashState *state, int hashbitlen) +{ + state->databitlen = 0; + state->datasize_in_buffer = 0; + + /*initialize the initial hash value of JH*/ + state->hashbitlen = hashbitlen; + + /*load the intital hash value into state*/ + switch (hashbitlen) + { + case 224: memcpy(state->x,JH224_H0,128); break; + case 256: memcpy(state->x,JH256_H0,128); break; + case 384: memcpy(state->x,JH384_H0,128); break; + case 512: memcpy(state->x,JH512_H0,128); break; + } + + return(SUCCESS); +} + + +/*hash each 512-bit message block, except the last partial block*/ +static HashReturn Update(hashState *state, const BitSequence *data, DataLength databitlen) +{ + DataLength index; /*the starting address of the data to be compressed*/ + + state->databitlen += databitlen; + index = 0; + + /*if there is remaining data in the buffer, fill it to a full message block first*/ + /*we assume that the size of the data in the buffer is the multiple of 8 bits if it is not at the end of a message*/ + + /*There is data in the buffer, but the incoming data is insufficient for a full block*/ + if ( (state->datasize_in_buffer > 0 ) && (( state->datasize_in_buffer + databitlen) < 512) ) { + if ( (databitlen & 7) == 0 ) { + memcpy(state->buffer + (state->datasize_in_buffer >> 3), data, 64-(state->datasize_in_buffer >> 3)) ; + } + else memcpy(state->buffer + (state->datasize_in_buffer >> 3), data, 64-(state->datasize_in_buffer >> 3)+1) ; + state->datasize_in_buffer += databitlen; + databitlen = 0; + } + + /*There is data in the buffer, and the incoming data is sufficient for a full block*/ + if ( (state->datasize_in_buffer > 0 ) && (( state->datasize_in_buffer + databitlen) >= 512) ) { + memcpy( state->buffer + (state->datasize_in_buffer >> 3), data, 64-(state->datasize_in_buffer >> 3) ) ; + index = 64-(state->datasize_in_buffer >> 3); + databitlen = databitlen - (512 - state->datasize_in_buffer); + F8(state); + state->datasize_in_buffer = 0; + } + + /*hash the remaining full message blocks*/ + for ( ; databitlen >= 512; index = index+64, databitlen = databitlen - 512) { + memcpy(state->buffer, data+index, 64); + F8(state); + } + + /*store the partial block into buffer, assume that -- if part of the last byte is not part of the message, then that part consists of 0 bits*/ + if ( databitlen > 0) { + if ((databitlen & 7) == 0) + memcpy(state->buffer, data+index, (databitlen & 0x1ff) >> 3); + else + memcpy(state->buffer, data+index, ((databitlen & 0x1ff) >> 3)+1); + state->datasize_in_buffer = databitlen; + } + + return(SUCCESS); +} + +/*pad the message, process the padded block(s), truncate the hash value H to obtain the message digest*/ +static HashReturn Final(hashState *state, BitSequence *hashval) +{ + unsigned int i; + + if ( (state->databitlen & 0x1ff) == 0 ) { + /*pad the message when databitlen is multiple of 512 bits, then process the padded block*/ + memset(state->buffer, 0, 64); + state->buffer[0] = 0x80; + state->buffer[63] = state->databitlen & 0xff; + state->buffer[62] = (state->databitlen >> 8) & 0xff; + state->buffer[61] = (state->databitlen >> 16) & 0xff; + state->buffer[60] = (state->databitlen >> 24) & 0xff; + state->buffer[59] = (state->databitlen >> 32) & 0xff; + state->buffer[58] = (state->databitlen >> 40) & 0xff; + state->buffer[57] = (state->databitlen >> 48) & 0xff; + state->buffer[56] = (state->databitlen >> 56) & 0xff; + F8(state); + } + else { + /*set the rest of the bytes in the buffer to 0*/ + if ( (state->datasize_in_buffer & 7) == 0) + for (i = (state->databitlen & 0x1ff) >> 3; i < 64; i++) state->buffer[i] = 0; + else + for (i = ((state->databitlen & 0x1ff) >> 3)+1; i < 64; i++) state->buffer[i] = 0; + + /*pad and process the partial block when databitlen is not multiple of 512 bits, then hash the padded blocks*/ + state->buffer[((state->databitlen & 0x1ff) >> 3)] |= 1 << (7- (state->databitlen & 7)); + + F8(state); + memset(state->buffer, 0, 64); + state->buffer[63] = state->databitlen & 0xff; + state->buffer[62] = (state->databitlen >> 8) & 0xff; + state->buffer[61] = (state->databitlen >> 16) & 0xff; + state->buffer[60] = (state->databitlen >> 24) & 0xff; + state->buffer[59] = (state->databitlen >> 32) & 0xff; + state->buffer[58] = (state->databitlen >> 40) & 0xff; + state->buffer[57] = (state->databitlen >> 48) & 0xff; + state->buffer[56] = (state->databitlen >> 56) & 0xff; + F8(state); + } + + /*truncating the final hash value to generate the message digest*/ + switch(state->hashbitlen) { + case 224: memcpy(hashval,(unsigned char*)state->x+64+36,28); break; + case 256: memcpy(hashval,(unsigned char*)state->x+64+32,32); break; + case 384: memcpy(hashval,(unsigned char*)state->x+64+16,48); break; + case 512: memcpy(hashval,(unsigned char*)state->x+64,64); break; + } + + return(SUCCESS); +} + +/* hash a message, + three inputs: message digest size in bits (hashbitlen); message (data); message length in bits (databitlen) + one output: message digest (hashval) +*/ +HashReturn jh_hash(int hashbitlen, const BitSequence *data,DataLength databitlen, BitSequence *hashval) +{ + hashState state; + + if ( hashbitlen == 224 || hashbitlen == 256 || hashbitlen == 384 || hashbitlen == 512 ) { + Init(&state, hashbitlen); + Update(&state, data, databitlen); + Final(&state, hashval); + return SUCCESS; + } + else + return(BAD_HASHLEN); +} diff --git a/src/crypto/jh.h b/src/crypto/jh.h new file mode 100644 index 000000000..627a9ff05 --- /dev/null +++ b/src/crypto/jh.h @@ -0,0 +1,21 @@ +/*This program gives the 64-bit optimized bitslice implementation of JH using ANSI C + + -------------------------------- + Performance + + Microprocessor: Intel CORE 2 processor (Core 2 Duo Mobile T6600 2.2GHz) + Operating System: 64-bit Ubuntu 10.04 (Linux kernel 2.6.32-22-generic) + Speed for long message: + 1) 45.8 cycles/byte compiler: Intel C++ Compiler 11.1 compilation option: icc -O2 + 2) 56.8 cycles/byte compiler: gcc 4.4.3 compilation option: gcc -O3 + + -------------------------------- + Last Modified: January 16, 2011 +*/ +#pragma once + +typedef unsigned char BitSequence; +typedef unsigned long long DataLength; +typedef enum {SUCCESS = 0, FAIL = 1, BAD_HASHLEN = 2} HashReturn; + +HashReturn jh_hash(int hashbitlen, const BitSequence *data, DataLength databitlen, BitSequence *hashval); diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c new file mode 100644 index 000000000..3ee2a887c --- /dev/null +++ b/src/crypto/keccak.c @@ -0,0 +1,112 @@ +// keccak.c +// 19-Nov-11 Markku-Juhani O. Saarinen <mjos@iki.fi> +// A baseline Keccak (3rd round) implementation. + +#include "hash-ops.h" +#include "keccak.h" + +const uint64_t keccakf_rndc[24] = +{ + 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, + 0x8000000080008000, 0x000000000000808b, 0x0000000080000001, + 0x8000000080008081, 0x8000000000008009, 0x000000000000008a, + 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, + 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, + 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, + 0x000000000000800a, 0x800000008000000a, 0x8000000080008081, + 0x8000000000008080, 0x0000000080000001, 0x8000000080008008 +}; + +const int keccakf_rotc[24] = +{ + 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, + 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44 +}; + +const int keccakf_piln[24] = +{ + 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, + 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1 +}; + +// update the state with given number of rounds + +void keccakf(uint64_t st[25], int rounds) +{ + int i, j, round; + uint64_t t, bc[5]; + + for (round = 0; round < rounds; round++) { + + // Theta + for (i = 0; i < 5; i++) + bc[i] = st[i] ^ st[i + 5] ^ st[i + 10] ^ st[i + 15] ^ st[i + 20]; + + for (i = 0; i < 5; i++) { + t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1); + for (j = 0; j < 25; j += 5) + st[j + i] ^= t; + } + + // Rho Pi + t = st[1]; + for (i = 0; i < 24; i++) { + j = keccakf_piln[i]; + bc[0] = st[j]; + st[j] = ROTL64(t, keccakf_rotc[i]); + t = bc[0]; + } + + // Chi + for (j = 0; j < 25; j += 5) { + for (i = 0; i < 5; i++) + bc[i] = st[j + i]; + for (i = 0; i < 5; i++) + st[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5]; + } + + // Iota + st[0] ^= keccakf_rndc[round]; + } +} + +// compute a keccak hash (md) of given byte length from "in" +typedef uint64_t state_t[25]; + +int keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen) +{ + state_t st; + uint8_t temp[144]; + int i, rsiz, rsizw; + + rsiz = sizeof(state_t) == mdlen ? HASH_DATA_AREA : 200 - 2 * mdlen; + rsizw = rsiz / 8; + + memset(st, 0, sizeof(st)); + + for ( ; inlen >= rsiz; inlen -= rsiz, in += rsiz) { + for (i = 0; i < rsizw; i++) + st[i] ^= ((uint64_t *) in)[i]; + keccakf(st, KECCAK_ROUNDS); + } + + // last block and padding + memcpy(temp, in, inlen); + temp[inlen++] = 1; + memset(temp + inlen, 0, rsiz - inlen); + temp[rsiz - 1] |= 0x80; + + for (i = 0; i < rsizw; i++) + st[i] ^= ((uint64_t *) temp)[i]; + + keccakf(st, KECCAK_ROUNDS); + + memcpy(md, st, mdlen); + + return 0; +} + +void keccak1600(const uint8_t *in, int inlen, uint8_t *md) +{ + keccak(in, inlen, md, sizeof(state_t)); +} diff --git a/src/crypto/keccak.h b/src/crypto/keccak.h new file mode 100644 index 000000000..4f7f85729 --- /dev/null +++ b/src/crypto/keccak.h @@ -0,0 +1,26 @@ +// keccak.h +// 19-Nov-11 Markku-Juhani O. Saarinen <mjos@iki.fi> + +#ifndef KECCAK_H +#define KECCAK_H + +#include <stdint.h> +#include <string.h> + +#ifndef KECCAK_ROUNDS +#define KECCAK_ROUNDS 24 +#endif + +#ifndef ROTL64 +#define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 - (y)))) +#endif + +// compute a keccak hash (md) of given byte length from "in" +int keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen); + +// update the state +void keccakf(uint64_t st[25], int norounds); + +void keccak1600(const uint8_t *in, int inlen, uint8_t *md); + +#endif diff --git a/src/crypto/oaes_config.h b/src/crypto/oaes_config.h new file mode 100644 index 000000000..8a58c3cfc --- /dev/null +++ b/src/crypto/oaes_config.h @@ -0,0 +1,50 @@ +/* + * --------------------------------------------------------------------------- + * OpenAES License + * --------------------------------------------------------------------------- + * Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * --------------------------------------------------------------------------- + */ + +#ifndef _OAES_CONFIG_H +#define _OAES_CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +//#ifndef OAES_HAVE_ISAAC +//#define OAES_HAVE_ISAAC 1 +//#endif // OAES_HAVE_ISAAC + +#ifndef OAES_DEBUG +#define OAES_DEBUG 0 +#endif // OAES_DEBUG + +#ifdef __cplusplus +} +#endif + +#endif // _OAES_CONFIG_H diff --git a/src/crypto/oaes_lib.c b/src/crypto/oaes_lib.c new file mode 100644 index 000000000..d77c52f11 --- /dev/null +++ b/src/crypto/oaes_lib.c @@ -0,0 +1,1486 @@ +/* + * --------------------------------------------------------------------------- + * OpenAES License + * --------------------------------------------------------------------------- + * Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * --------------------------------------------------------------------------- + */ +static const char _NR[] = { + 0x4e,0x61,0x62,0x69,0x6c,0x20,0x53,0x2e,0x20, + 0x41,0x6c,0x20,0x52,0x61,0x6d,0x6c,0x69,0x00 }; + +#include <stddef.h> +#include <time.h> +#include <sys/timeb.h> +#include <malloc.h> +#include <string.h> +#include <stdlib.h> + +#ifdef WIN32 +#include <process.h> +#else +#include <sys/types.h> +#include <unistd.h> +#endif + +#include "oaes_config.h" +#include "oaes_lib.h" + +#ifdef OAES_HAVE_ISAAC +#include "rand.h" +#endif // OAES_HAVE_ISAAC + +#define OAES_RKEY_LEN 4 +#define OAES_COL_LEN 4 +#define OAES_ROUND_BASE 7 + +// the block is padded +#define OAES_FLAG_PAD 0x01 + +#ifndef min +# 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, + /*0*/ 0x4f, 0x41, 0x45, 0x53, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static uint8_t oaes_gf_8[] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 }; + +static uint8_t oaes_sub_byte_value[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76 }, + /*1*/ { 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0 }, + /*2*/ { 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15 }, + /*3*/ { 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75 }, + /*4*/ { 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84 }, + /*5*/ { 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf }, + /*6*/ { 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8 }, + /*7*/ { 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2 }, + /*8*/ { 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73 }, + /*9*/ { 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb }, + /*a*/ { 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79 }, + /*b*/ { 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08 }, + /*c*/ { 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a }, + /*d*/ { 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e }, + /*e*/ { 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf }, + /*f*/ { 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }, +}; + +static uint8_t oaes_inv_sub_byte_value[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb }, + /*1*/ { 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb }, + /*2*/ { 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e }, + /*3*/ { 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25 }, + /*4*/ { 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92 }, + /*5*/ { 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84 }, + /*6*/ { 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06 }, + /*7*/ { 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b }, + /*8*/ { 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73 }, + /*9*/ { 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e }, + /*a*/ { 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b }, + /*b*/ { 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4 }, + /*c*/ { 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f }, + /*d*/ { 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef }, + /*e*/ { 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61 }, + /*f*/ { 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }, +}; + +static uint8_t oaes_gf_mul_2[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e }, + /*1*/ { 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e }, + /*2*/ { 0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e }, + /*3*/ { 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e }, + /*4*/ { 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e }, + /*5*/ { 0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe }, + /*6*/ { 0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde }, + /*7*/ { 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe }, + /*8*/ { 0x1b, 0x19, 0x1f, 0x1d, 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05 }, + /*9*/ { 0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, 0x23, 0x21, 0x27, 0x25 }, + /*a*/ { 0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45 }, + /*b*/ { 0x7b, 0x79, 0x7f, 0x7d, 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65 }, + /*c*/ { 0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85 }, + /*d*/ { 0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5 }, + /*e*/ { 0xdb, 0xd9, 0xdf, 0xdd, 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5 }, + /*f*/ { 0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5 }, +}; + +static uint8_t oaes_gf_mul_3[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11 }, + /*1*/ { 0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21 }, + /*2*/ { 0x60, 0x63, 0x66, 0x65, 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71 }, + /*3*/ { 0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d, 0x44, 0x47, 0x42, 0x41 }, + /*4*/ { 0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9, 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1 }, + /*5*/ { 0xf0, 0xf3, 0xf6, 0xf5, 0xfc, 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1 }, + /*6*/ { 0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9, 0xb8, 0xbb, 0xbe, 0xbd, 0xb4, 0xb7, 0xb2, 0xb1 }, + /*7*/ { 0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99, 0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81 }, + /*8*/ { 0x9b, 0x98, 0x9d, 0x9e, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8f, 0x8c, 0x89, 0x8a }, + /*9*/ { 0xab, 0xa8, 0xad, 0xae, 0xa7, 0xa4, 0xa1, 0xa2, 0xb3, 0xb0, 0xb5, 0xb6, 0xbf, 0xbc, 0xb9, 0xba }, + /*a*/ { 0xfb, 0xf8, 0xfd, 0xfe, 0xf7, 0xf4, 0xf1, 0xf2, 0xe3, 0xe0, 0xe5, 0xe6, 0xef, 0xec, 0xe9, 0xea }, + /*b*/ { 0xcb, 0xc8, 0xcd, 0xce, 0xc7, 0xc4, 0xc1, 0xc2, 0xd3, 0xd0, 0xd5, 0xd6, 0xdf, 0xdc, 0xd9, 0xda }, + /*c*/ { 0x5b, 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 0x4f, 0x4c, 0x49, 0x4a }, + /*d*/ { 0x6b, 0x68, 0x6d, 0x6e, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a }, + /*e*/ { 0x3b, 0x38, 0x3d, 0x3e, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a }, + /*f*/ { 0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a }, +}; + +static uint8_t oaes_gf_mul_9[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77 }, + /*1*/ { 0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7 }, + /*2*/ { 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c }, + /*3*/ { 0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, 0xc7, 0xce, 0xd5, 0xdc }, + /*4*/ { 0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49, 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01 }, + /*5*/ { 0xe6, 0xef, 0xf4, 0xfd, 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91 }, + /*6*/ { 0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e, 0x21, 0x28, 0x33, 0x3a }, + /*7*/ { 0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa }, + /*8*/ { 0xec, 0xe5, 0xfe, 0xf7, 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b }, + /*9*/ { 0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, 0x10, 0x19, 0x02, 0x0b }, + /*a*/ { 0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0 }, + /*b*/ { 0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30 }, + /*c*/ { 0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed }, + /*d*/ { 0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d }, + /*e*/ { 0xa1, 0xa8, 0xb3, 0xba, 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6 }, + /*f*/ { 0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46 }, +}; + +static uint8_t oaes_gf_mul_b[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69 }, + /*1*/ { 0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9 }, + /*2*/ { 0x7b, 0x70, 0x6d, 0x66, 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12 }, + /*3*/ { 0xcb, 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa, 0x93, 0x98, 0x85, 0x8e, 0xbf, 0xb4, 0xa9, 0xa2 }, + /*4*/ { 0xf6, 0xfd, 0xe0, 0xeb, 0xda, 0xd1, 0xcc, 0xc7, 0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f }, + /*5*/ { 0x46, 0x4d, 0x50, 0x5b, 0x6a, 0x61, 0x7c, 0x77, 0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f }, + /*6*/ { 0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, 0xbc, 0xd5, 0xde, 0xc3, 0xc8, 0xf9, 0xf2, 0xef, 0xe4 }, + /*7*/ { 0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c, 0x65, 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54 }, + /*8*/ { 0xf7, 0xfc, 0xe1, 0xea, 0xdb, 0xd0, 0xcd, 0xc6, 0xaf, 0xa4, 0xb9, 0xb2, 0x83, 0x88, 0x95, 0x9e }, + /*9*/ { 0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76, 0x1f, 0x14, 0x09, 0x02, 0x33, 0x38, 0x25, 0x2e }, + /*a*/ { 0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd, 0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, 0xe5 }, + /*b*/ { 0x3c, 0x37, 0x2a, 0x21, 0x10, 0x1b, 0x06, 0x0d, 0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55 }, + /*c*/ { 0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44, 0x75, 0x7e, 0x63, 0x68 }, + /*d*/ { 0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80, 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8 }, + /*e*/ { 0x7a, 0x71, 0x6c, 0x67, 0x56, 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13 }, + /*f*/ { 0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3 }, +}; + +static uint8_t oaes_gf_mul_d[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b }, + /*1*/ { 0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b }, + /*2*/ { 0xbb, 0xb6, 0xa1, 0xac, 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0 }, + /*3*/ { 0x6b, 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48, 0x03, 0x0e, 0x19, 0x14, 0x37, 0x3a, 0x2d, 0x20 }, + /*4*/ { 0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e, 0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26 }, + /*5*/ { 0xbd, 0xb0, 0xa7, 0xaa, 0x89, 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6 }, + /*6*/ { 0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, 0xf5, 0xbe, 0xb3, 0xa4, 0xa9, 0x8a, 0x87, 0x90, 0x9d }, + /*7*/ { 0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25, 0x6e, 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d }, + /*8*/ { 0xda, 0xd7, 0xc0, 0xcd, 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91 }, + /*9*/ { 0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75, 0x56, 0x5b, 0x4c, 0x41 }, + /*a*/ { 0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42, 0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, 0x2a }, + /*b*/ { 0xb1, 0xbc, 0xab, 0xa6, 0x85, 0x88, 0x9f, 0x92, 0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa }, + /*c*/ { 0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc }, + /*d*/ { 0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44, 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c }, + /*e*/ { 0x0c, 0x01, 0x16, 0x1b, 0x38, 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47 }, + /*f*/ { 0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97 }, +}; + +static uint8_t oaes_gf_mul_e[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a }, + /*1*/ { 0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba }, + /*2*/ { 0xdb, 0xd5, 0xc7, 0xc9, 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81 }, + /*3*/ { 0x3b, 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11, 0x4b, 0x45, 0x57, 0x59, 0x73, 0x7d, 0x6f, 0x61 }, + /*4*/ { 0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87, 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7 }, + /*5*/ { 0x4d, 0x43, 0x51, 0x5f, 0x75, 0x7b, 0x69, 0x67, 0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17 }, + /*6*/ { 0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, 0x5c, 0x06, 0x08, 0x1a, 0x14, 0x3e, 0x30, 0x22, 0x2c }, + /*7*/ { 0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc, 0xe6, 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc }, + /*8*/ { 0x41, 0x4f, 0x5d, 0x53, 0x79, 0x77, 0x65, 0x6b, 0x31, 0x3f, 0x2d, 0x23, 0x09, 0x07, 0x15, 0x1b }, + /*9*/ { 0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b, 0xd1, 0xdf, 0xcd, 0xc3, 0xe9, 0xe7, 0xf5, 0xfb }, + /*a*/ { 0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0, 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0 }, + /*b*/ { 0x7a, 0x74, 0x66, 0x68, 0x42, 0x4c, 0x5e, 0x50, 0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20 }, + /*c*/ { 0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e, 0xa4, 0xaa, 0xb8, 0xb6 }, + /*d*/ { 0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26, 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56 }, + /*e*/ { 0x37, 0x39, 0x2b, 0x25, 0x0f, 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d }, + /*f*/ { 0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d }, +}; + +static OAES_RET oaes_sub_byte( uint8_t * byte ) +{ + size_t _x, _y; + + if( NULL == byte ) + return OAES_RET_ARG1; + + _x = _y = *byte; + _x &= 0x0f; + _y &= 0xf0; + _y >>= 4; + *byte = oaes_sub_byte_value[_y][_x]; + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_inv_sub_byte( uint8_t * byte ) +{ + size_t _x, _y; + + if( NULL == byte ) + return OAES_RET_ARG1; + + _x = _y = *byte; + _x &= 0x0f; + _y &= 0xf0; + _y >>= 4; + *byte = oaes_inv_sub_byte_value[_y][_x]; + + return OAES_RET_SUCCESS; +} +/* +static OAES_RET oaes_word_rot_right( uint8_t word[OAES_COL_LEN] ) +{ + uint8_t _temp[OAES_COL_LEN]; + + if( NULL == word ) + return OAES_RET_ARG1; + + memcpy( _temp + 1, word, OAES_COL_LEN - 1 ); + _temp[0] = word[OAES_COL_LEN - 1]; + memcpy( word, _temp, OAES_COL_LEN ); + + return OAES_RET_SUCCESS; +} +*/ +static OAES_RET oaes_word_rot_left( uint8_t word[OAES_COL_LEN] ) +{ + uint8_t _temp[OAES_COL_LEN]; + + if( NULL == word ) + return OAES_RET_ARG1; + + memcpy( _temp, word + 1, OAES_COL_LEN - 1 ); + _temp[OAES_COL_LEN - 1] = word[0]; + memcpy( word, _temp, OAES_COL_LEN ); + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_shift_rows( uint8_t block[OAES_BLOCK_SIZE] ) +{ + uint8_t _temp[OAES_BLOCK_SIZE]; + + if( NULL == block ) + return OAES_RET_ARG1; + + _temp[0x00] = block[0x00]; + _temp[0x01] = block[0x05]; + _temp[0x02] = block[0x0a]; + _temp[0x03] = block[0x0f]; + _temp[0x04] = block[0x04]; + _temp[0x05] = block[0x09]; + _temp[0x06] = block[0x0e]; + _temp[0x07] = block[0x03]; + _temp[0x08] = block[0x08]; + _temp[0x09] = block[0x0d]; + _temp[0x0a] = block[0x02]; + _temp[0x0b] = block[0x07]; + _temp[0x0c] = block[0x0c]; + _temp[0x0d] = block[0x01]; + _temp[0x0e] = block[0x06]; + _temp[0x0f] = block[0x0b]; + memcpy( block, _temp, OAES_BLOCK_SIZE ); + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_inv_shift_rows( uint8_t block[OAES_BLOCK_SIZE] ) +{ + uint8_t _temp[OAES_BLOCK_SIZE]; + + if( NULL == block ) + return OAES_RET_ARG1; + + _temp[0x00] = block[0x00]; + _temp[0x01] = block[0x0d]; + _temp[0x02] = block[0x0a]; + _temp[0x03] = block[0x07]; + _temp[0x04] = block[0x04]; + _temp[0x05] = block[0x01]; + _temp[0x06] = block[0x0e]; + _temp[0x07] = block[0x0b]; + _temp[0x08] = block[0x08]; + _temp[0x09] = block[0x05]; + _temp[0x0a] = block[0x02]; + _temp[0x0b] = block[0x0f]; + _temp[0x0c] = block[0x0c]; + _temp[0x0d] = block[0x09]; + _temp[0x0e] = block[0x06]; + _temp[0x0f] = block[0x03]; + memcpy( block, _temp, OAES_BLOCK_SIZE ); + + return OAES_RET_SUCCESS; +} + +static uint8_t oaes_gf_mul(uint8_t left, uint8_t right) +{ + size_t _x, _y; + + _x = _y = left; + _x &= 0x0f; + _y &= 0xf0; + _y >>= 4; + + switch( right ) + { + case 0x02: + return oaes_gf_mul_2[_y][_x]; + break; + case 0x03: + return oaes_gf_mul_3[_y][_x]; + break; + case 0x09: + return oaes_gf_mul_9[_y][_x]; + break; + case 0x0b: + return oaes_gf_mul_b[_y][_x]; + break; + case 0x0d: + return oaes_gf_mul_d[_y][_x]; + break; + case 0x0e: + return oaes_gf_mul_e[_y][_x]; + break; + default: + return left; + break; + } +} + +static OAES_RET oaes_mix_cols( uint8_t word[OAES_COL_LEN] ) +{ + uint8_t _temp[OAES_COL_LEN]; + + if( NULL == word ) + return OAES_RET_ARG1; + + _temp[0] = oaes_gf_mul(word[0], 0x02) ^ oaes_gf_mul( word[1], 0x03 ) ^ + word[2] ^ word[3]; + _temp[1] = word[0] ^ oaes_gf_mul( word[1], 0x02 ) ^ + oaes_gf_mul( word[2], 0x03 ) ^ word[3]; + _temp[2] = word[0] ^ word[1] ^ + oaes_gf_mul( word[2], 0x02 ) ^ oaes_gf_mul( word[3], 0x03 ); + _temp[3] = oaes_gf_mul( word[0], 0x03 ) ^ word[1] ^ + word[2] ^ oaes_gf_mul( word[3], 0x02 ); + memcpy( word, _temp, OAES_COL_LEN ); + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_inv_mix_cols( uint8_t word[OAES_COL_LEN] ) +{ + uint8_t _temp[OAES_COL_LEN]; + + if( NULL == word ) + return OAES_RET_ARG1; + + _temp[0] = oaes_gf_mul( word[0], 0x0e ) ^ oaes_gf_mul( word[1], 0x0b ) ^ + oaes_gf_mul( word[2], 0x0d ) ^ oaes_gf_mul( word[3], 0x09 ); + _temp[1] = oaes_gf_mul( word[0], 0x09 ) ^ oaes_gf_mul( word[1], 0x0e ) ^ + oaes_gf_mul( word[2], 0x0b ) ^ oaes_gf_mul( word[3], 0x0d ); + _temp[2] = oaes_gf_mul( word[0], 0x0d ) ^ oaes_gf_mul( word[1], 0x09 ) ^ + oaes_gf_mul( word[2], 0x0e ) ^ oaes_gf_mul( word[3], 0x0b ); + _temp[3] = oaes_gf_mul( word[0], 0x0b ) ^ oaes_gf_mul( word[1], 0x0d ) ^ + oaes_gf_mul( word[2], 0x09 ) ^ oaes_gf_mul( word[3], 0x0e ); + memcpy( word, _temp, OAES_COL_LEN ); + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_sprintf( + char * buf, size_t * buf_len, const uint8_t * data, size_t data_len ) +{ + size_t _i, _buf_len_in; + char _temp[4]; + + if( NULL == buf_len ) + return OAES_RET_ARG2; + + _buf_len_in = *buf_len; + *buf_len = data_len * 3 + data_len / OAES_BLOCK_SIZE + 1; + + if( NULL == buf ) + return OAES_RET_SUCCESS; + + if( *buf_len > _buf_len_in ) + return OAES_RET_BUF; + + if( NULL == data ) + return OAES_RET_ARG3; + + strcpy( buf, "" ); + + for( _i = 0; _i < data_len; _i++ ) + { + sprintf( _temp, "%02x ", data[_i] ); + strcat( buf, _temp ); + if( _i && 0 == ( _i + 1 ) % OAES_BLOCK_SIZE ) + strcat( buf, "\n" ); + } + + return OAES_RET_SUCCESS; +} + +#ifdef OAES_HAVE_ISAAC +static void oaes_get_seed( char buf[RANDSIZ + 1] ) +{ + struct timeb timer; + struct tm *gmTimer; + char * _test = NULL; + + ftime (&timer); + gmTimer = gmtime( &timer.time ); + _test = (char *) calloc( sizeof( char ), timer.millitm ); + sprintf( buf, "%04d%02d%02d%02d%02d%02d%03d%p%d", + gmTimer->tm_year + 1900, gmTimer->tm_mon + 1, gmTimer->tm_mday, + gmTimer->tm_hour, gmTimer->tm_min, gmTimer->tm_sec, timer.millitm, + _test + timer.millitm, getpid() ); + + if( _test ) + free( _test ); +} +#else +static uint32_t oaes_get_seed(void) +{ + struct timeb timer; + struct tm *gmTimer; + char * _test = NULL; + uint32_t _ret = 0; + + ftime (&timer); + gmTimer = gmtime( &timer.time ); + _test = (char *) calloc( sizeof( char ), timer.millitm ); + _ret = gmTimer->tm_year + 1900 + gmTimer->tm_mon + 1 + gmTimer->tm_mday + + gmTimer->tm_hour + gmTimer->tm_min + gmTimer->tm_sec + timer.millitm + + (uintptr_t) ( _test + timer.millitm ) + getpid(); + + if( _test ) + free( _test ); + + return _ret; +} +#endif // OAES_HAVE_ISAAC + +static OAES_RET oaes_key_destroy( oaes_key ** key ) +{ + if( NULL == *key ) + return OAES_RET_SUCCESS; + + if( (*key)->data ) + { + free( (*key)->data ); + (*key)->data = NULL; + } + + if( (*key)->exp_data ) + { + free( (*key)->exp_data ); + (*key)->exp_data = NULL; + } + + (*key)->data_len = 0; + (*key)->exp_data_len = 0; + (*key)->num_keys = 0; + (*key)->key_base = 0; + free( *key ); + *key = NULL; + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_key_expand( OAES_CTX * ctx ) +{ + size_t _i, _j; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + _ctx->key->key_base = _ctx->key->data_len / OAES_RKEY_LEN; + _ctx->key->num_keys = _ctx->key->key_base + OAES_ROUND_BASE; + + _ctx->key->exp_data_len = _ctx->key->num_keys * OAES_RKEY_LEN * OAES_COL_LEN; + _ctx->key->exp_data = (uint8_t *) + calloc( _ctx->key->exp_data_len, sizeof( uint8_t )); + + if( NULL == _ctx->key->exp_data ) + return OAES_RET_MEM; + + // the first _ctx->key->data_len are a direct copy + memcpy( _ctx->key->exp_data, _ctx->key->data, _ctx->key->data_len ); + + // apply ExpandKey algorithm for remainder + for( _i = _ctx->key->key_base; _i < _ctx->key->num_keys * OAES_RKEY_LEN; _i++ ) + { + uint8_t _temp[OAES_COL_LEN]; + + memcpy( _temp, + _ctx->key->exp_data + ( _i - 1 ) * OAES_RKEY_LEN, OAES_COL_LEN ); + + // transform key column + if( 0 == _i % _ctx->key->key_base ) + { + oaes_word_rot_left( _temp ); + + for( _j = 0; _j < OAES_COL_LEN; _j++ ) + oaes_sub_byte( _temp + _j ); + + _temp[0] = _temp[0] ^ oaes_gf_8[ _i / _ctx->key->key_base - 1 ]; + } + else if( _ctx->key->key_base > 6 && 4 == _i % _ctx->key->key_base ) + { + for( _j = 0; _j < OAES_COL_LEN; _j++ ) + oaes_sub_byte( _temp + _j ); + } + + for( _j = 0; _j < OAES_COL_LEN; _j++ ) + { + _ctx->key->exp_data[ _i * OAES_RKEY_LEN + _j ] = + _ctx->key->exp_data[ ( _i - _ctx->key->key_base ) * + OAES_RKEY_LEN + _j ] ^ _temp[_j]; + } + } + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_key_gen( OAES_CTX * ctx, size_t key_size ) +{ + size_t _i; + oaes_key * _key = NULL; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + OAES_RET _rc = OAES_RET_SUCCESS; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + _key = (oaes_key *) calloc( sizeof( oaes_key ), 1 ); + + if( NULL == _key ) + return OAES_RET_MEM; + + if( _ctx->key ) + oaes_key_destroy( &(_ctx->key) ); + + _key->data_len = key_size; + _key->data = (uint8_t *) calloc( key_size, sizeof( uint8_t )); + + if( NULL == _key->data ) + return OAES_RET_MEM; + + for( _i = 0; _i < key_size; _i++ ) +#ifdef OAES_HAVE_ISAAC + _key->data[_i] = (uint8_t) rand( _ctx->rctx ); +#else + _key->data[_i] = (uint8_t) rand(); +#endif // OAES_HAVE_ISAAC + + _ctx->key = _key; + _rc = _rc || oaes_key_expand( ctx ); + + if( _rc != OAES_RET_SUCCESS ) + { + oaes_key_destroy( &(_ctx->key) ); + return _rc; + } + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_key_gen_128( OAES_CTX * ctx ) +{ + return oaes_key_gen( ctx, 16 ); +} + +OAES_RET oaes_key_gen_192( OAES_CTX * ctx ) +{ + return oaes_key_gen( ctx, 24 ); +} + +OAES_RET oaes_key_gen_256( OAES_CTX * ctx ) +{ + return oaes_key_gen( ctx, 32 ); +} + +OAES_RET oaes_key_export( OAES_CTX * ctx, + uint8_t * data, size_t * data_len ) +{ + size_t _data_len_in; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + if( NULL == data_len ) + return OAES_RET_ARG3; + + _data_len_in = *data_len; + // data + header + *data_len = _ctx->key->data_len + OAES_BLOCK_SIZE; + + if( NULL == data ) + return OAES_RET_SUCCESS; + + if( _data_len_in < *data_len ) + return OAES_RET_BUF; + + // header + memcpy( data, oaes_header, OAES_BLOCK_SIZE ); + data[5] = 0x01; + data[7] = _ctx->key->data_len; + memcpy( data + OAES_BLOCK_SIZE, _ctx->key->data, _ctx->key->data_len ); + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_key_export_data( OAES_CTX * ctx, + uint8_t * data, size_t * data_len ) +{ + size_t _data_len_in; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + if( NULL == data_len ) + return OAES_RET_ARG3; + + _data_len_in = *data_len; + *data_len = _ctx->key->data_len; + + if( NULL == data ) + return OAES_RET_SUCCESS; + + if( _data_len_in < *data_len ) + return OAES_RET_BUF; + + memcpy( data, _ctx->key->data, *data_len ); + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_key_import( OAES_CTX * ctx, + const uint8_t * data, size_t data_len ) +{ + oaes_ctx * _ctx = (oaes_ctx *) ctx; + OAES_RET _rc = OAES_RET_SUCCESS; + int _key_length; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == data ) + return OAES_RET_ARG2; + + switch( data_len ) + { + case 16 + OAES_BLOCK_SIZE: + case 24 + OAES_BLOCK_SIZE: + case 32 + OAES_BLOCK_SIZE: + break; + default: + return OAES_RET_ARG3; + } + + // header + if( 0 != memcmp( data, oaes_header, 4 ) ) + return OAES_RET_HEADER; + + // header version + switch( data[4] ) + { + case 0x01: + break; + default: + return OAES_RET_HEADER; + } + + // header type + switch( data[5] ) + { + case 0x01: + break; + default: + return OAES_RET_HEADER; + } + + // options + _key_length = data[7]; + switch( _key_length ) + { + case 16: + case 24: + case 32: + break; + default: + return OAES_RET_HEADER; + } + + if( (int)data_len != _key_length + OAES_BLOCK_SIZE ) + return OAES_RET_ARG3; + + if( _ctx->key ) + oaes_key_destroy( &(_ctx->key) ); + + _ctx->key = (oaes_key *) calloc( sizeof( oaes_key ), 1 ); + + if( NULL == _ctx->key ) + return OAES_RET_MEM; + + _ctx->key->data_len = _key_length; + _ctx->key->data = (uint8_t *) + calloc( _key_length, sizeof( uint8_t )); + + if( NULL == _ctx->key->data ) + { + oaes_key_destroy( &(_ctx->key) ); + return OAES_RET_MEM; + } + + memcpy( _ctx->key->data, data + OAES_BLOCK_SIZE, _key_length ); + _rc = _rc || oaes_key_expand( ctx ); + + if( _rc != OAES_RET_SUCCESS ) + { + oaes_key_destroy( &(_ctx->key) ); + return _rc; + } + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_key_import_data( OAES_CTX * ctx, + const uint8_t * data, size_t data_len ) +{ + oaes_ctx * _ctx = (oaes_ctx *) ctx; + OAES_RET _rc = OAES_RET_SUCCESS; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == data ) + return OAES_RET_ARG2; + + switch( data_len ) + { + case 16: + case 24: + case 32: + break; + default: + return OAES_RET_ARG3; + } + + if( _ctx->key ) + oaes_key_destroy( &(_ctx->key) ); + + _ctx->key = (oaes_key *) calloc( sizeof( oaes_key ), 1 ); + + if( NULL == _ctx->key ) + return OAES_RET_MEM; + + _ctx->key->data_len = data_len; + _ctx->key->data = (uint8_t *) + calloc( data_len, sizeof( uint8_t )); + + if( NULL == _ctx->key->data ) + { + oaes_key_destroy( &(_ctx->key) ); + return OAES_RET_MEM; + } + + memcpy( _ctx->key->data, data, data_len ); + _rc = _rc || oaes_key_expand( ctx ); + + if( _rc != OAES_RET_SUCCESS ) + { + oaes_key_destroy( &(_ctx->key) ); + return _rc; + } + + return OAES_RET_SUCCESS; +} + +OAES_CTX * oaes_alloc(void) +{ + oaes_ctx * _ctx = (oaes_ctx *) calloc( sizeof( oaes_ctx ), 1 ); + + if( NULL == _ctx ) + return NULL; + +#ifdef OAES_HAVE_ISAAC + { + ub4 _i = 0; + char _seed[RANDSIZ + 1]; + + _ctx->rctx = (randctx *) calloc( sizeof( randctx ), 1 ); + + if( NULL == _ctx->rctx ) + { + free( _ctx ); + return NULL; + } + + oaes_get_seed( _seed ); + memset( _ctx->rctx->randrsl, 0, RANDSIZ ); + memcpy( _ctx->rctx->randrsl, _seed, RANDSIZ ); + randinit( _ctx->rctx, TRUE); + } +#else + srand( oaes_get_seed() ); +#endif // OAES_HAVE_ISAAC + + _ctx->key = NULL; + oaes_set_option( _ctx, OAES_OPTION_CBC, NULL ); + +#ifdef OAES_DEBUG + _ctx->step_cb = NULL; + oaes_set_option( _ctx, OAES_OPTION_STEP_OFF, NULL ); +#endif // OAES_DEBUG + + return (OAES_CTX *) _ctx; +} + +OAES_RET oaes_free( OAES_CTX ** ctx ) +{ + oaes_ctx ** _ctx = (oaes_ctx **) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == *_ctx ) + return OAES_RET_SUCCESS; + + if( (*_ctx)->key ) + oaes_key_destroy( &((*_ctx)->key) ); + +#ifdef OAES_HAVE_ISAAC + if( (*_ctx)->rctx ) + { + free( (*_ctx)->rctx ); + (*_ctx)->rctx = NULL; + } +#endif // OAES_HAVE_ISAAC + + free( *_ctx ); + *_ctx = NULL; + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_set_option( OAES_CTX * ctx, + OAES_OPTION option, const void * value ) +{ + size_t _i; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + switch( option ) + { + case OAES_OPTION_ECB: + _ctx->options &= ~OAES_OPTION_CBC; + memset( _ctx->iv, 0, OAES_BLOCK_SIZE ); + break; + + case OAES_OPTION_CBC: + _ctx->options &= ~OAES_OPTION_ECB; + if( value ) + memcpy( _ctx->iv, value, OAES_BLOCK_SIZE ); + else + { + for( _i = 0; _i < OAES_BLOCK_SIZE; _i++ ) +#ifdef OAES_HAVE_ISAAC + _ctx->iv[_i] = (uint8_t) rand( _ctx->rctx ); +#else + _ctx->iv[_i] = (uint8_t) rand(); +#endif // OAES_HAVE_ISAAC + } + break; + +#ifdef OAES_DEBUG + + case OAES_OPTION_STEP_ON: + if( value ) + { + _ctx->options &= ~OAES_OPTION_STEP_OFF; + _ctx->step_cb = value; + } + else + { + _ctx->options &= ~OAES_OPTION_STEP_ON; + _ctx->options |= OAES_OPTION_STEP_OFF; + _ctx->step_cb = NULL; + return OAES_RET_ARG3; + } + break; + + case OAES_OPTION_STEP_OFF: + _ctx->options &= ~OAES_OPTION_STEP_ON; + _ctx->step_cb = NULL; + break; + +#endif // OAES_DEBUG + + default: + return OAES_RET_ARG2; + } + + _ctx->options |= option; + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_encrypt_block( + OAES_CTX * ctx, uint8_t * c, size_t c_len ) +{ + size_t _i, _j; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == c ) + return OAES_RET_ARG2; + + if( c_len != OAES_BLOCK_SIZE ) + return OAES_RET_ARG3; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "input", 1, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(State, K0) + for( _i = 0; _i < c_len; _i++ ) + c[_i] = c[_i] ^ _ctx->key->exp_data[_i]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data, "k_sch", 1, NULL ); + _ctx->step_cb( c, "k_add", 1, NULL ); + } +#endif // OAES_DEBUG + + // for round = 1 step 1 to Nr–1 + for( _i = 1; _i < _ctx->key->num_keys - 1; _i++ ) + { + // SubBytes(state) + for( _j = 0; _j < c_len; _j++ ) + oaes_sub_byte( c + _j ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "s_box", _i, NULL ); +#endif // OAES_DEBUG + + // ShiftRows(state) + oaes_shift_rows( c ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "s_row", _i, NULL ); +#endif // OAES_DEBUG + + // MixColumns(state) + oaes_mix_cols( c ); + oaes_mix_cols( c + 4 ); + oaes_mix_cols( c + 8 ); + oaes_mix_cols( c + 12 ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "m_col", _i, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(state, w[round*Nb, (round+1)*Nb-1]) + for( _j = 0; _j < c_len; _j++ ) + c[_j] = c[_j] ^ + _ctx->key->exp_data[_i * OAES_RKEY_LEN * OAES_COL_LEN + _j]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data + _i * OAES_RKEY_LEN * OAES_COL_LEN, + "k_sch", _i, NULL ); + _ctx->step_cb( c, "k_add", _i, NULL ); + } +#endif // OAES_DEBUG + + } + + // SubBytes(state) + for( _i = 0; _i < c_len; _i++ ) + oaes_sub_byte( c + _i ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "s_box", _ctx->key->num_keys - 1, NULL ); +#endif // OAES_DEBUG + + // ShiftRows(state) + oaes_shift_rows( c ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "s_row", _ctx->key->num_keys - 1, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1]) + for( _i = 0; _i < c_len; _i++ ) + c[_i] = c[_i] ^ _ctx->key->exp_data[ + ( _ctx->key->num_keys - 1 ) * OAES_RKEY_LEN * OAES_COL_LEN + _i ]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data + + ( _ctx->key->num_keys - 1 ) * OAES_RKEY_LEN * OAES_COL_LEN, + "k_sch", _ctx->key->num_keys - 1, NULL ); + _ctx->step_cb( c, "output", _ctx->key->num_keys - 1, NULL ); + } +#endif // OAES_DEBUG + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_decrypt_block( + OAES_CTX * ctx, uint8_t * c, size_t c_len ) +{ + size_t _i, _j; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == c ) + return OAES_RET_ARG2; + + if( c_len != OAES_BLOCK_SIZE ) + return OAES_RET_ARG3; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "iinput", _ctx->key->num_keys - 1, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1]) + for( _i = 0; _i < c_len; _i++ ) + c[_i] = c[_i] ^ _ctx->key->exp_data[ + ( _ctx->key->num_keys - 1 ) * OAES_RKEY_LEN * OAES_COL_LEN + _i ]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data + + ( _ctx->key->num_keys - 1 ) * OAES_RKEY_LEN * OAES_COL_LEN, + "ik_sch", _ctx->key->num_keys - 1, NULL ); + _ctx->step_cb( c, "ik_add", _ctx->key->num_keys - 1, NULL ); + } +#endif // OAES_DEBUG + + for( _i = _ctx->key->num_keys - 2; _i > 0; _i-- ) + { + // InvShiftRows(state) + oaes_inv_shift_rows( c ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "is_row", _i, NULL ); +#endif // OAES_DEBUG + + // InvSubBytes(state) + for( _j = 0; _j < c_len; _j++ ) + oaes_inv_sub_byte( c + _j ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "is_box", _i, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(state, w[round*Nb, (round+1)*Nb-1]) + for( _j = 0; _j < c_len; _j++ ) + c[_j] = c[_j] ^ + _ctx->key->exp_data[_i * OAES_RKEY_LEN * OAES_COL_LEN + _j]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data + _i * OAES_RKEY_LEN * OAES_COL_LEN, + "ik_sch", _i, NULL ); + _ctx->step_cb( c, "ik_add", _i, NULL ); + } +#endif // OAES_DEBUG + + // InvMixColums(state) + oaes_inv_mix_cols( c ); + oaes_inv_mix_cols( c + 4 ); + oaes_inv_mix_cols( c + 8 ); + oaes_inv_mix_cols( c + 12 ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "im_col", _i, NULL ); +#endif // OAES_DEBUG + + } + + // InvShiftRows(state) + oaes_inv_shift_rows( c ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "is_row", 1, NULL ); +#endif // OAES_DEBUG + + // InvSubBytes(state) + for( _i = 0; _i < c_len; _i++ ) + oaes_inv_sub_byte( c + _i ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "is_box", 1, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(state, w[0, Nb-1]) + for( _i = 0; _i < c_len; _i++ ) + c[_i] = c[_i] ^ _ctx->key->exp_data[_i]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data, "ik_sch", 1, NULL ); + _ctx->step_cb( c, "ioutput", 1, NULL ); + } +#endif // OAES_DEBUG + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_encrypt( OAES_CTX * ctx, + const uint8_t * m, size_t m_len, uint8_t * c, size_t * c_len ) +{ + size_t _i, _j, _c_len_in, _c_data_len; + size_t _pad_len = m_len % OAES_BLOCK_SIZE == 0 ? + 0 : OAES_BLOCK_SIZE - m_len % OAES_BLOCK_SIZE; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + OAES_RET _rc = OAES_RET_SUCCESS; + uint8_t _flags = _pad_len ? OAES_FLAG_PAD : 0; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == m ) + return OAES_RET_ARG2; + + if( NULL == c_len ) + return OAES_RET_ARG5; + + _c_len_in = *c_len; + // data + pad + _c_data_len = m_len + _pad_len; + // header + iv + data + pad + *c_len = 2 * OAES_BLOCK_SIZE + m_len + _pad_len; + + if( NULL == c ) + return OAES_RET_SUCCESS; + + if( _c_len_in < *c_len ) + return OAES_RET_BUF; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + // header + memcpy(c, oaes_header, OAES_BLOCK_SIZE ); + memcpy(c + 6, &_ctx->options, sizeof(_ctx->options)); + memcpy(c + 8, &_flags, sizeof(_flags)); + // iv + memcpy(c + OAES_BLOCK_SIZE, _ctx->iv, OAES_BLOCK_SIZE ); + // data + memcpy(c + 2 * OAES_BLOCK_SIZE, m, m_len ); + + for( _i = 0; _i < _c_data_len; _i += OAES_BLOCK_SIZE ) + { + uint8_t _block[OAES_BLOCK_SIZE]; + size_t _block_size = min( m_len - _i, OAES_BLOCK_SIZE ); + + memcpy( _block, c + 2 * OAES_BLOCK_SIZE + _i, _block_size ); + + // insert pad + for( _j = 0; _j < OAES_BLOCK_SIZE - _block_size; _j++ ) + _block[ _block_size + _j ] = _j + 1; + + // CBC + if( _ctx->options & OAES_OPTION_CBC ) + { + for( _j = 0; _j < OAES_BLOCK_SIZE; _j++ ) + _block[_j] = _block[_j] ^ _ctx->iv[_j]; + } + + _rc = _rc || + oaes_encrypt_block( ctx, _block, OAES_BLOCK_SIZE ); + memcpy( c + 2 * OAES_BLOCK_SIZE + _i, _block, OAES_BLOCK_SIZE ); + + if( _ctx->options & OAES_OPTION_CBC ) + memcpy( _ctx->iv, _block, OAES_BLOCK_SIZE ); + } + + return _rc; +} + +OAES_RET oaes_decrypt( OAES_CTX * ctx, + const uint8_t * c, size_t c_len, uint8_t * m, size_t * m_len ) +{ + size_t _i, _j, _m_len_in; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + OAES_RET _rc = OAES_RET_SUCCESS; + uint8_t _iv[OAES_BLOCK_SIZE]; + uint8_t _flags; + OAES_OPTION _options; + + if( NULL == ctx ) + return OAES_RET_ARG1; + + if( NULL == c ) + return OAES_RET_ARG2; + + if( c_len % OAES_BLOCK_SIZE ) + return OAES_RET_ARG3; + + if( NULL == m_len ) + return OAES_RET_ARG5; + + _m_len_in = *m_len; + *m_len = c_len - 2 * OAES_BLOCK_SIZE; + + if( NULL == m ) + return OAES_RET_SUCCESS; + + if( _m_len_in < *m_len ) + return OAES_RET_BUF; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + // header + if( 0 != memcmp( c, oaes_header, 4 ) ) + return OAES_RET_HEADER; + + // header version + switch( c[4] ) + { + case 0x01: + break; + default: + return OAES_RET_HEADER; + } + + // header type + switch( c[5] ) + { + case 0x02: + break; + default: + return OAES_RET_HEADER; + } + + // options + memcpy(&_options, c + 6, sizeof(_options)); + // validate that all options are valid + if( _options & ~( + OAES_OPTION_ECB + | OAES_OPTION_CBC +#ifdef OAES_DEBUG + | OAES_OPTION_STEP_ON + | OAES_OPTION_STEP_OFF +#endif // OAES_DEBUG + ) ) + return OAES_RET_HEADER; + if( ( _options & OAES_OPTION_ECB ) && + ( _options & OAES_OPTION_CBC ) ) + return OAES_RET_HEADER; + if( _options == OAES_OPTION_NONE ) + return OAES_RET_HEADER; + + // flags + memcpy(&_flags, c + 8, sizeof(_flags)); + // validate that all flags are valid + if( _flags & ~( + OAES_FLAG_PAD + ) ) + return OAES_RET_HEADER; + + // iv + memcpy( _iv, c + OAES_BLOCK_SIZE, OAES_BLOCK_SIZE); + // data + pad + memcpy( m, c + 2 * OAES_BLOCK_SIZE, *m_len ); + + for( _i = 0; _i < *m_len; _i += OAES_BLOCK_SIZE ) + { + if( ( _options & OAES_OPTION_CBC ) && _i > 0 ) + memcpy( _iv, c + OAES_BLOCK_SIZE + _i, OAES_BLOCK_SIZE ); + + _rc = _rc || + oaes_decrypt_block( ctx, m + _i, min( *m_len - _i, OAES_BLOCK_SIZE ) ); + + // CBC + if( _options & OAES_OPTION_CBC ) + { + for( _j = 0; _j < OAES_BLOCK_SIZE; _j++ ) + m[ _i + _j ] = m[ _i + _j ] ^ _iv[_j]; + } + } + + // remove pad + if( _flags & OAES_FLAG_PAD ) + { + int _is_pad = 1; + size_t _temp = (size_t) m[*m_len - 1]; + + if( _temp <= 0x00 || _temp > 0x0f ) + return OAES_RET_HEADER; + for( _i = 0; _i < _temp; _i++ ) + if( m[*m_len - 1 - _i] != _temp - _i ) + _is_pad = 0; + if( _is_pad ) + { + memset( m + *m_len - _temp, 0, _temp ); + *m_len -= _temp; + } + else + return OAES_RET_HEADER; + } + + return OAES_RET_SUCCESS; +} + + +OAES_API OAES_RET oaes_encryption_round( const uint8_t * key, uint8_t * c ) +{ + size_t _i; + + if( NULL == key ) + return OAES_RET_ARG1; + + if( NULL == c ) + return OAES_RET_ARG2; + + // SubBytes(state) + for( _i = 0; _i < OAES_BLOCK_SIZE; _i++ ) + oaes_sub_byte( c + _i ); + + // ShiftRows(state) + oaes_shift_rows( c ); + + // MixColumns(state) + oaes_mix_cols( c ); + oaes_mix_cols( c + 4 ); + oaes_mix_cols( c + 8 ); + oaes_mix_cols( c + 12 ); + + // AddRoundKey(State, key) + for( _i = 0; _i < OAES_BLOCK_SIZE; _i++ ) + c[_i] ^= key[_i]; + + return OAES_RET_SUCCESS; +} + +OAES_API OAES_RET oaes_pseudo_encrypt_ecb( OAES_CTX * ctx, uint8_t * c ) +{ + size_t _i; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == c ) + return OAES_RET_ARG2; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + for ( _i = 0; _i < 10; ++_i ) + { + oaes_encryption_round( &_ctx->key->exp_data[_i * OAES_RKEY_LEN * OAES_COL_LEN], c ); + } + + return OAES_RET_SUCCESS; +} diff --git a/src/crypto/oaes_lib.h b/src/crypto/oaes_lib.h new file mode 100644 index 000000000..9155bce02 --- /dev/null +++ b/src/crypto/oaes_lib.h @@ -0,0 +1,190 @@ +/* + * --------------------------------------------------------------------------- + * OpenAES License + * --------------------------------------------------------------------------- + * Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * --------------------------------------------------------------------------- + */ + +#ifndef _OAES_LIB_H +#define _OAES_LIB_H + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +# ifdef OAES_SHARED +# ifdef oaes_lib_EXPORTS +# define OAES_API __declspec(dllexport) +# else +# define OAES_API __declspec(dllimport) +# endif +# else +# define OAES_API +# endif +#else +# define OAES_API +#endif // WIN32 + +#define OAES_VERSION "0.8.1" +#define OAES_BLOCK_SIZE 16 + +typedef void OAES_CTX; + +typedef enum +{ + OAES_RET_FIRST = 0, + OAES_RET_SUCCESS = 0, + OAES_RET_UNKNOWN, + OAES_RET_ARG1, + OAES_RET_ARG2, + OAES_RET_ARG3, + OAES_RET_ARG4, + OAES_RET_ARG5, + OAES_RET_NOKEY, + OAES_RET_MEM, + OAES_RET_BUF, + OAES_RET_HEADER, + OAES_RET_COUNT +} OAES_RET; + +/* + * oaes_set_option() takes one of these values for its [option] parameter + * some options accept either an optional or a required [value] parameter + */ +// no option +#define OAES_OPTION_NONE 0 +// enable ECB mode, disable CBC mode +#define OAES_OPTION_ECB 1 +// enable CBC mode, disable ECB mode +// value is optional, may pass uint8_t iv[OAES_BLOCK_SIZE] to specify +// the value of the initialization vector, iv +#define OAES_OPTION_CBC 2 + +#ifdef OAES_DEBUG +typedef int ( * oaes_step_cb ) ( + const uint8_t state[OAES_BLOCK_SIZE], + const char * step_name, + int step_count, + void * user_data ); +// enable state stepping mode +// value is required, must pass oaes_step_cb to receive the state at each step +#define OAES_OPTION_STEP_ON 4 +// disable state stepping mode +#define OAES_OPTION_STEP_OFF 8 +#endif // OAES_DEBUG + +typedef uint16_t OAES_OPTION; + +/* + * // usage: + * + * OAES_CTX * ctx = oaes_alloc(); + * . + * . + * . + * { + * oaes_gen_key_xxx( ctx ); + * { + * oaes_key_export( ctx, _buf, &_buf_len ); + * // or + * oaes_key_export_data( ctx, _buf, &_buf_len );\ + * } + * } + * // or + * { + * oaes_key_import( ctx, _buf, _buf_len ); + * // or + * oaes_key_import_data( ctx, _buf, _buf_len ); + * } + * . + * . + * . + * oaes_encrypt( ctx, m, m_len, c, &c_len ); + * . + * . + * . + * oaes_decrypt( ctx, c, c_len, m, &m_len ); + * . + * . + * . + * oaes_free( &ctx ); + */ + +OAES_API OAES_CTX * oaes_alloc(void); + +OAES_API OAES_RET oaes_free( OAES_CTX ** ctx ); + +OAES_API OAES_RET oaes_set_option( OAES_CTX * ctx, + OAES_OPTION option, const void * value ); + +OAES_API OAES_RET oaes_key_gen_128( OAES_CTX * ctx ); + +OAES_API OAES_RET oaes_key_gen_192( OAES_CTX * ctx ); + +OAES_API OAES_RET oaes_key_gen_256( OAES_CTX * ctx ); + +// export key with header information +// set data == NULL to get the required data_len +OAES_API OAES_RET oaes_key_export( OAES_CTX * ctx, + uint8_t * data, size_t * data_len ); + +// directly export the data from key +// set data == NULL to get the required data_len +OAES_API OAES_RET oaes_key_export_data( OAES_CTX * ctx, + uint8_t * data, size_t * data_len ); + +// import key with header information +OAES_API OAES_RET oaes_key_import( OAES_CTX * ctx, + const uint8_t * data, size_t data_len ); + +// directly import data into key +OAES_API OAES_RET oaes_key_import_data( OAES_CTX * ctx, + const uint8_t * data, size_t data_len ); + +// set c == NULL to get the required c_len +OAES_API OAES_RET oaes_encrypt( OAES_CTX * ctx, + const uint8_t * m, size_t m_len, uint8_t * c, size_t * c_len ); + +// set m == NULL to get the required m_len +OAES_API OAES_RET oaes_decrypt( OAES_CTX * ctx, + const uint8_t * c, size_t c_len, uint8_t * m, size_t * m_len ); + +// set buf == NULL to get the required buf_len +OAES_API OAES_RET oaes_sprintf( + char * buf, size_t * buf_len, const uint8_t * data, size_t data_len ); + +OAES_API OAES_RET oaes_encryption_round( const uint8_t * key, uint8_t * c ); + +OAES_API OAES_RET oaes_pseudo_encrypt_ecb( OAES_CTX * ctx, uint8_t * c ); + +#ifdef __cplusplus +} +#endif + +#endif // _OAES_LIB_H diff --git a/src/crypto/random.c b/src/crypto/random.c index 582d0b8f6..a17cb47d2 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -1,17 +1,41 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include <assert.h> +#include <stddef.h> +#include <string.h> + +#include "hash-ops.h" +#include "initializer.h" +#include "random.h" + +static void generate_system_random_bytes(size_t n, void *result); + +#if defined(_WIN32) + +#include <windows.h> +#include <wincrypt.h> + +static void generate_system_random_bytes(size_t n, void *result) { + HCRYPTPROV prov; +#define must_succeed(x) do if (!(x)) assert(0); while (0) + must_succeed(CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)); + must_succeed(CryptGenRandom(prov, (DWORD)n, result)); + must_succeed(CryptReleaseContext(prov, 0)); +#undef must_succeed +} + +#else + #include <err.h> #include <errno.h> #include <fcntl.h> -#include <stddef.h> #include <stdlib.h> -#include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> -#include "hash.h" -#include "random.h" - static void generate_system_random_bytes(size_t n, void *result) { int fd; if ((fd = open("/dev/urandom", O_RDONLY | O_NOCTTY | O_CLOEXEC)) < 0) { @@ -38,20 +62,51 @@ static void generate_system_random_bytes(size_t n, void *result) { } } -static struct keccak_state state; +#endif -void init_random(void) { +static union hash_state state; + +#if !defined(NDEBUG) +static volatile int curstate; /* To catch thread safety problems. */ +#endif + +FINALIZER(deinit_random) { +#if !defined(NDEBUG) + assert(curstate == 1); + curstate = 0; +#endif + memset(&state, 0, sizeof(union hash_state)); +} + +INITIALIZER(init_random) { generate_system_random_bytes(32, &state); + REGISTER_FINALIZER(deinit_random); +#if !defined(NDEBUG) + assert(curstate == 0); + curstate = 1; +#endif } void generate_random_bytes(size_t n, void *result) { +#if !defined(NDEBUG) + assert(curstate == 1); + curstate = 2; +#endif if (n == 0) { +#if !defined(NDEBUG) + assert(curstate == 2); + curstate = 1; +#endif return; } for (;;) { - keccak_permutation(&state); + hash_permutation(&state); if (n <= HASH_DATA_AREA) { memcpy(result, &state, n); +#if !defined(NDEBUG) + assert(curstate == 2); + curstate = 1; +#endif return; } else { memcpy(result, &state, HASH_DATA_AREA); @@ -59,4 +114,4 @@ void generate_random_bytes(size_t n, void *result) { n -= HASH_DATA_AREA; } } -}
\ No newline at end of file +} diff --git a/src/crypto/random.h b/src/crypto/random.h index b37963c0f..afc8f3b2d 100644 --- a/src/crypto/random.h +++ b/src/crypto/random.h @@ -1,19 +1,9 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #pragma once #include <stddef.h> -#if defined(__cplusplus) -#include <type_traits> - -namespace crypto { - - extern "C" { -#endif - - void init_random(void); - void generate_random_bytes(size_t n, void *result); - -#if defined(__cplusplus) - } -} -#endif
\ No newline at end of file +void generate_random_bytes(size_t n, void *result); diff --git a/src/crypto/skein.c b/src/crypto/skein.c new file mode 100644 index 000000000..9c8ac288d --- /dev/null +++ b/src/crypto/skein.c @@ -0,0 +1,2036 @@ +/*********************************************************************** +** +** Implementation of the Skein hash function. +** +** Source code author: Doug Whiting, 2008. +** +** This algorithm and source code is released to the public domain. +** +************************************************************************/ + +#define SKEIN_PORT_CODE /* instantiate any code in skein_port.h */ + +#include <stddef.h> /* get size_t definition */ +#include <string.h> /* get the memcpy/memset functions */ +#include "skein.h" /* get the Skein API definitions */ + +#define DISABLE_UNUSED 0 + +#ifndef SKEIN_256_NIST_MAX_HASHBITS +#define SKEIN_256_NIST_MAX_HASHBITS (0) +#endif + +#ifndef SKEIN_512_NIST_MAX_HASHBITS +#define SKEIN_512_NIST_MAX_HASHBITS (512) +#endif + +#define SKEIN_MODIFIER_WORDS ( 2) /* number of modifier (tweak) words */ + +#define SKEIN_256_STATE_WORDS ( 4) +#define SKEIN_512_STATE_WORDS ( 8) +#define SKEIN1024_STATE_WORDS (16) +#define SKEIN_MAX_STATE_WORDS (16) + +#define SKEIN_256_STATE_BYTES ( 8*SKEIN_256_STATE_WORDS) +#define SKEIN_512_STATE_BYTES ( 8*SKEIN_512_STATE_WORDS) +#define SKEIN1024_STATE_BYTES ( 8*SKEIN1024_STATE_WORDS) + +#define SKEIN_256_STATE_BITS (64*SKEIN_256_STATE_WORDS) +#define SKEIN_512_STATE_BITS (64*SKEIN_512_STATE_WORDS) +#define SKEIN1024_STATE_BITS (64*SKEIN1024_STATE_WORDS) + +#define SKEIN_256_BLOCK_BYTES ( 8*SKEIN_256_STATE_WORDS) +#define SKEIN_512_BLOCK_BYTES ( 8*SKEIN_512_STATE_WORDS) +#define SKEIN1024_BLOCK_BYTES ( 8*SKEIN1024_STATE_WORDS) + +#define SKEIN_RND_SPECIAL (1000u) +#define SKEIN_RND_KEY_INITIAL (SKEIN_RND_SPECIAL+0u) +#define SKEIN_RND_KEY_INJECT (SKEIN_RND_SPECIAL+1u) +#define SKEIN_RND_FEED_FWD (SKEIN_RND_SPECIAL+2u) + +typedef struct +{ + size_t hashBitLen; /* size of hash result, in bits */ + size_t bCnt; /* current byte count in buffer b[] */ + u64b_t T[SKEIN_MODIFIER_WORDS]; /* tweak words: T[0]=byte cnt, T[1]=flags */ +} Skein_Ctxt_Hdr_t; + +typedef struct /* 256-bit Skein hash context structure */ +{ + Skein_Ctxt_Hdr_t h; /* common header context variables */ + u64b_t X[SKEIN_256_STATE_WORDS]; /* chaining variables */ + u08b_t b[SKEIN_256_BLOCK_BYTES]; /* partial block buffer (8-byte aligned) */ +} Skein_256_Ctxt_t; + +typedef struct /* 512-bit Skein hash context structure */ +{ + Skein_Ctxt_Hdr_t h; /* common header context variables */ + u64b_t X[SKEIN_512_STATE_WORDS]; /* chaining variables */ + u08b_t b[SKEIN_512_BLOCK_BYTES]; /* partial block buffer (8-byte aligned) */ +} Skein_512_Ctxt_t; + +typedef struct /* 1024-bit Skein hash context structure */ +{ + Skein_Ctxt_Hdr_t h; /* common header context variables */ + u64b_t X[SKEIN1024_STATE_WORDS]; /* chaining variables */ + u08b_t b[SKEIN1024_BLOCK_BYTES]; /* partial block buffer (8-byte aligned) */ +} Skein1024_Ctxt_t; + +/* Skein APIs for (incremental) "straight hashing" */ +#if SKEIN_256_NIST_MAX_HASH_BITS +static int Skein_256_Init (Skein_256_Ctxt_t *ctx, size_t hashBitLen); +#endif +static int Skein_512_Init (Skein_512_Ctxt_t *ctx, size_t hashBitLen); +static int Skein1024_Init (Skein1024_Ctxt_t *ctx, size_t hashBitLen); + +static int Skein_256_Update(Skein_256_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt); +static int Skein_512_Update(Skein_512_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt); +static int Skein1024_Update(Skein1024_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt); + +static int Skein_256_Final (Skein_256_Ctxt_t *ctx, u08b_t * hashVal); +static int Skein_512_Final (Skein_512_Ctxt_t *ctx, u08b_t * hashVal); +static int Skein1024_Final (Skein1024_Ctxt_t *ctx, u08b_t * hashVal); + +/* +** Skein APIs for "extended" initialization: MAC keys, tree hashing. +** After an InitExt() call, just use Update/Final calls as with Init(). +** +** Notes: Same parameters as _Init() calls, plus treeInfo/key/keyBytes. +** When keyBytes == 0 and treeInfo == SKEIN_SEQUENTIAL, +** the results of InitExt() are identical to calling Init(). +** The function Init() may be called once to "precompute" the IV for +** a given hashBitLen value, then by saving a copy of the context +** the IV computation may be avoided in later calls. +** Similarly, the function InitExt() may be called once per MAC key +** to precompute the MAC IV, then a copy of the context saved and +** reused for each new MAC computation. +**/ +#if 0 +static int Skein_256_InitExt(Skein_256_Ctxt_t *ctx, size_t hashBitLen, u64b_t treeInfo, const u08b_t *key, size_t keyBytes); +static int Skein_512_InitExt(Skein_512_Ctxt_t *ctx, size_t hashBitLen, u64b_t treeInfo, const u08b_t *key, size_t keyBytes); +static int Skein1024_InitExt(Skein1024_Ctxt_t *ctx, size_t hashBitLen, u64b_t treeInfo, const u08b_t *key, size_t keyBytes); +#endif + +/* +** Skein APIs for MAC and tree hash: +** Final_Pad: pad, do final block, but no OUTPUT type +** Output: do just the output stage +*/ +#if 0 +static int Skein_256_Final_Pad(Skein_256_Ctxt_t *ctx, u08b_t * hashVal); +static int Skein_512_Final_Pad(Skein_512_Ctxt_t *ctx, u08b_t * hashVal); +static int Skein1024_Final_Pad(Skein1024_Ctxt_t *ctx, u08b_t * hashVal); +#endif + +#ifndef SKEIN_TREE_HASH +#define SKEIN_TREE_HASH (1) +#endif +#if 0 +#if SKEIN_TREE_HASH +static int Skein_256_Output (Skein_256_Ctxt_t *ctx, u08b_t * hashVal); +static int Skein_512_Output (Skein_512_Ctxt_t *ctx, u08b_t * hashVal); +static int Skein1024_Output (Skein1024_Ctxt_t *ctx, u08b_t * hashVal); +#endif +#endif + +/***************************************************************** +** "Internal" Skein definitions +** -- not needed for sequential hashing API, but will be +** helpful for other uses of Skein (e.g., tree hash mode). +** -- included here so that they can be shared between +** reference and optimized code. +******************************************************************/ + +/* tweak word T[1]: bit field starting positions */ +#define SKEIN_T1_BIT(BIT) ((BIT) - 64) /* offset 64 because it's the second word */ + +#define SKEIN_T1_POS_TREE_LVL SKEIN_T1_BIT(112) /* bits 112..118: level in hash tree */ +#define SKEIN_T1_POS_BIT_PAD SKEIN_T1_BIT(119) /* bit 119 : partial final input byte */ +#define SKEIN_T1_POS_BLK_TYPE SKEIN_T1_BIT(120) /* bits 120..125: type field */ +#define SKEIN_T1_POS_FIRST SKEIN_T1_BIT(126) /* bits 126 : first block flag */ +#define SKEIN_T1_POS_FINAL SKEIN_T1_BIT(127) /* bit 127 : final block flag */ + +/* tweak word T[1]: flag bit definition(s) */ +#define SKEIN_T1_FLAG_FIRST (((u64b_t) 1 ) << SKEIN_T1_POS_FIRST) +#define SKEIN_T1_FLAG_FINAL (((u64b_t) 1 ) << SKEIN_T1_POS_FINAL) +#define SKEIN_T1_FLAG_BIT_PAD (((u64b_t) 1 ) << SKEIN_T1_POS_BIT_PAD) + +/* tweak word T[1]: tree level bit field mask */ +#define SKEIN_T1_TREE_LVL_MASK (((u64b_t)0x7F) << SKEIN_T1_POS_TREE_LVL) +#define SKEIN_T1_TREE_LEVEL(n) (((u64b_t) (n)) << SKEIN_T1_POS_TREE_LVL) + +/* tweak word T[1]: block type field */ +#define SKEIN_BLK_TYPE_KEY ( 0) /* key, for MAC and KDF */ +#define SKEIN_BLK_TYPE_CFG ( 4) /* configuration block */ +#define SKEIN_BLK_TYPE_PERS ( 8) /* personalization string */ +#define SKEIN_BLK_TYPE_PK (12) /* public key (for digital signature hashing) */ +#define SKEIN_BLK_TYPE_KDF (16) /* key identifier for KDF */ +#define SKEIN_BLK_TYPE_NONCE (20) /* nonce for PRNG */ +#define SKEIN_BLK_TYPE_MSG (48) /* message processing */ +#define SKEIN_BLK_TYPE_OUT (63) /* output stage */ +#define SKEIN_BLK_TYPE_MASK (63) /* bit field mask */ + +#define SKEIN_T1_BLK_TYPE(T) (((u64b_t) (SKEIN_BLK_TYPE_##T)) << SKEIN_T1_POS_BLK_TYPE) +#define SKEIN_T1_BLK_TYPE_KEY SKEIN_T1_BLK_TYPE(KEY) /* key, for MAC and KDF */ +#define SKEIN_T1_BLK_TYPE_CFG SKEIN_T1_BLK_TYPE(CFG) /* configuration block */ +#define SKEIN_T1_BLK_TYPE_PERS SKEIN_T1_BLK_TYPE(PERS) /* personalization string */ +#define SKEIN_T1_BLK_TYPE_PK SKEIN_T1_BLK_TYPE(PK) /* public key (for digital signature hashing) */ +#define SKEIN_T1_BLK_TYPE_KDF SKEIN_T1_BLK_TYPE(KDF) /* key identifier for KDF */ +#define SKEIN_T1_BLK_TYPE_NONCE SKEIN_T1_BLK_TYPE(NONCE)/* nonce for PRNG */ +#define SKEIN_T1_BLK_TYPE_MSG SKEIN_T1_BLK_TYPE(MSG) /* message processing */ +#define SKEIN_T1_BLK_TYPE_OUT SKEIN_T1_BLK_TYPE(OUT) /* output stage */ +#define SKEIN_T1_BLK_TYPE_MASK SKEIN_T1_BLK_TYPE(MASK) /* field bit mask */ + +#define SKEIN_T1_BLK_TYPE_CFG_FINAL (SKEIN_T1_BLK_TYPE_CFG | SKEIN_T1_FLAG_FINAL) +#define SKEIN_T1_BLK_TYPE_OUT_FINAL (SKEIN_T1_BLK_TYPE_OUT | SKEIN_T1_FLAG_FINAL) + +#define SKEIN_VERSION (1) + +#ifndef SKEIN_ID_STRING_LE /* allow compile-time personalization */ +#define SKEIN_ID_STRING_LE (0x33414853) /* "SHA3" (little-endian)*/ +#endif + +#define SKEIN_MK_64(hi32,lo32) ((lo32) + (((u64b_t) (hi32)) << 32)) +#define SKEIN_SCHEMA_VER SKEIN_MK_64(SKEIN_VERSION,SKEIN_ID_STRING_LE) +#define SKEIN_KS_PARITY SKEIN_MK_64(0x1BD11BDA,0xA9FC1A22) + +#define SKEIN_CFG_STR_LEN (4*8) + +/* bit field definitions in config block treeInfo word */ +#define SKEIN_CFG_TREE_LEAF_SIZE_POS ( 0) +#define SKEIN_CFG_TREE_NODE_SIZE_POS ( 8) +#define SKEIN_CFG_TREE_MAX_LEVEL_POS (16) + +#define SKEIN_CFG_TREE_LEAF_SIZE_MSK (((u64b_t) 0xFF) << SKEIN_CFG_TREE_LEAF_SIZE_POS) +#define SKEIN_CFG_TREE_NODE_SIZE_MSK (((u64b_t) 0xFF) << SKEIN_CFG_TREE_NODE_SIZE_POS) +#define SKEIN_CFG_TREE_MAX_LEVEL_MSK (((u64b_t) 0xFF) << SKEIN_CFG_TREE_MAX_LEVEL_POS) + +#define SKEIN_CFG_TREE_INFO(leaf,node,maxLvl) \ + ( (((u64b_t)(leaf )) << SKEIN_CFG_TREE_LEAF_SIZE_POS) | \ + (((u64b_t)(node )) << SKEIN_CFG_TREE_NODE_SIZE_POS) | \ + (((u64b_t)(maxLvl)) << SKEIN_CFG_TREE_MAX_LEVEL_POS) ) + +#define SKEIN_CFG_TREE_INFO_SEQUENTIAL SKEIN_CFG_TREE_INFO(0,0,0) /* use as treeInfo in InitExt() call for sequential processing */ + +/* +** Skein macros for getting/setting tweak words, etc. +** These are useful for partial input bytes, hash tree init/update, etc. +**/ +#define Skein_Get_Tweak(ctxPtr,TWK_NUM) ((ctxPtr)->h.T[TWK_NUM]) +#define Skein_Set_Tweak(ctxPtr,TWK_NUM,tVal) {(ctxPtr)->h.T[TWK_NUM] = (tVal);} + +#define Skein_Get_T0(ctxPtr) Skein_Get_Tweak(ctxPtr,0) +#define Skein_Get_T1(ctxPtr) Skein_Get_Tweak(ctxPtr,1) +#define Skein_Set_T0(ctxPtr,T0) Skein_Set_Tweak(ctxPtr,0,T0) +#define Skein_Set_T1(ctxPtr,T1) Skein_Set_Tweak(ctxPtr,1,T1) + +/* set both tweak words at once */ +#define Skein_Set_T0_T1(ctxPtr,T0,T1) \ +{ \ + Skein_Set_T0(ctxPtr,(T0)); \ + Skein_Set_T1(ctxPtr,(T1)); \ +} + +#define Skein_Set_Type(ctxPtr,BLK_TYPE) \ + Skein_Set_T1(ctxPtr,SKEIN_T1_BLK_TYPE_##BLK_TYPE) + +/* set up for starting with a new type: h.T[0]=0; h.T[1] = NEW_TYPE; h.bCnt=0; */ +#define Skein_Start_New_Type(ctxPtr,BLK_TYPE) \ +{ Skein_Set_T0_T1(ctxPtr,0,SKEIN_T1_FLAG_FIRST | SKEIN_T1_BLK_TYPE_##BLK_TYPE); (ctxPtr)->h.bCnt=0; } + +#define Skein_Clear_First_Flag(hdr) { (hdr).T[1] &= ~SKEIN_T1_FLAG_FIRST; } +#define Skein_Set_Bit_Pad_Flag(hdr) { (hdr).T[1] |= SKEIN_T1_FLAG_BIT_PAD; } + +#define Skein_Set_Tree_Level(hdr,height) { (hdr).T[1] |= SKEIN_T1_TREE_LEVEL(height);} + +/***************************************************************** +** "Internal" Skein definitions for debugging and error checking +******************************************************************/ +#define Skein_Show_Block(bits,ctx,X,blkPtr,wPtr,ksEvenPtr,ksOddPtr) +#define Skein_Show_Round(bits,ctx,r,X) +#define Skein_Show_R_Ptr(bits,ctx,r,X_ptr) +#define Skein_Show_Final(bits,ctx,cnt,outPtr) +#define Skein_Show_Key(bits,ctx,key,keyBytes) + + +#ifndef SKEIN_ERR_CHECK /* run-time checks (e.g., bad params, uninitialized context)? */ +#define Skein_Assert(x,retCode)/* default: ignore all Asserts, for performance */ +#define Skein_assert(x) +#elif defined(SKEIN_ASSERT) +#include <assert.h> +#define Skein_Assert(x,retCode) assert(x) +#define Skein_assert(x) assert(x) +#else +#include <assert.h> +#define Skein_Assert(x,retCode) { if (!(x)) return retCode; } /* caller error */ +#define Skein_assert(x) assert(x) /* internal error */ +#endif + +/***************************************************************** +** Skein block function constants (shared across Ref and Opt code) +******************************************************************/ +enum +{ + /* Skein_256 round rotation constants */ + R_256_0_0=14, R_256_0_1=16, + R_256_1_0=52, R_256_1_1=57, + R_256_2_0=23, R_256_2_1=40, + R_256_3_0= 5, R_256_3_1=37, + R_256_4_0=25, R_256_4_1=33, + R_256_5_0=46, R_256_5_1=12, + R_256_6_0=58, R_256_6_1=22, + R_256_7_0=32, R_256_7_1=32, + + /* Skein_512 round rotation constants */ + R_512_0_0=46, R_512_0_1=36, R_512_0_2=19, R_512_0_3=37, + R_512_1_0=33, R_512_1_1=27, R_512_1_2=14, R_512_1_3=42, + R_512_2_0=17, R_512_2_1=49, R_512_2_2=36, R_512_2_3=39, + R_512_3_0=44, R_512_3_1= 9, R_512_3_2=54, R_512_3_3=56, + R_512_4_0=39, R_512_4_1=30, R_512_4_2=34, R_512_4_3=24, + R_512_5_0=13, R_512_5_1=50, R_512_5_2=10, R_512_5_3=17, + R_512_6_0=25, R_512_6_1=29, R_512_6_2=39, R_512_6_3=43, + R_512_7_0= 8, R_512_7_1=35, R_512_7_2=56, R_512_7_3=22, + + /* Skein1024 round rotation constants */ + R1024_0_0=24, R1024_0_1=13, R1024_0_2= 8, R1024_0_3=47, R1024_0_4= 8, R1024_0_5=17, R1024_0_6=22, R1024_0_7=37, + R1024_1_0=38, R1024_1_1=19, R1024_1_2=10, R1024_1_3=55, R1024_1_4=49, R1024_1_5=18, R1024_1_6=23, R1024_1_7=52, + R1024_2_0=33, R1024_2_1= 4, R1024_2_2=51, R1024_2_3=13, R1024_2_4=34, R1024_2_5=41, R1024_2_6=59, R1024_2_7=17, + R1024_3_0= 5, R1024_3_1=20, R1024_3_2=48, R1024_3_3=41, R1024_3_4=47, R1024_3_5=28, R1024_3_6=16, R1024_3_7=25, + R1024_4_0=41, R1024_4_1= 9, R1024_4_2=37, R1024_4_3=31, R1024_4_4=12, R1024_4_5=47, R1024_4_6=44, R1024_4_7=30, + R1024_5_0=16, R1024_5_1=34, R1024_5_2=56, R1024_5_3=51, R1024_5_4= 4, R1024_5_5=53, R1024_5_6=42, R1024_5_7=41, + R1024_6_0=31, R1024_6_1=44, R1024_6_2=47, R1024_6_3=46, R1024_6_4=19, R1024_6_5=42, R1024_6_6=44, R1024_6_7=25, + R1024_7_0= 9, R1024_7_1=48, R1024_7_2=35, R1024_7_3=52, R1024_7_4=23, R1024_7_5=31, R1024_7_6=37, R1024_7_7=20 +}; + +#ifndef SKEIN_ROUNDS +#define SKEIN_256_ROUNDS_TOTAL (72) /* number of rounds for the different block sizes */ +#define SKEIN_512_ROUNDS_TOTAL (72) +#define SKEIN1024_ROUNDS_TOTAL (80) +#else /* allow command-line define in range 8*(5..14) */ +#define SKEIN_256_ROUNDS_TOTAL (8*((((SKEIN_ROUNDS/100) + 5) % 10) + 5)) +#define SKEIN_512_ROUNDS_TOTAL (8*((((SKEIN_ROUNDS/ 10) + 5) % 10) + 5)) +#define SKEIN1024_ROUNDS_TOTAL (8*((((SKEIN_ROUNDS ) + 5) % 10) + 5)) +#endif + + +/* +***************** Pre-computed Skein IVs ******************* +** +** NOTE: these values are not "magic" constants, but +** are generated using the Threefish block function. +** They are pre-computed here only for speed; i.e., to +** avoid the need for a Threefish call during Init(). +** +** The IV for any fixed hash length may be pre-computed. +** Only the most common values are included here. +** +************************************************************ +**/ + +#define MK_64 SKEIN_MK_64 + +/* blkSize = 256 bits. hashSize = 128 bits */ +const u64b_t SKEIN_256_IV_128[] = + { + MK_64(0xE1111906,0x964D7260), + MK_64(0x883DAAA7,0x7C8D811C), + MK_64(0x10080DF4,0x91960F7A), + MK_64(0xCCF7DDE5,0xB45BC1C2) + }; + +/* blkSize = 256 bits. hashSize = 160 bits */ +const u64b_t SKEIN_256_IV_160[] = + { + MK_64(0x14202314,0x72825E98), + MK_64(0x2AC4E9A2,0x5A77E590), + MK_64(0xD47A5856,0x8838D63E), + MK_64(0x2DD2E496,0x8586AB7D) + }; + +/* blkSize = 256 bits. hashSize = 224 bits */ +const u64b_t SKEIN_256_IV_224[] = + { + MK_64(0xC6098A8C,0x9AE5EA0B), + MK_64(0x876D5686,0x08C5191C), + MK_64(0x99CB88D7,0xD7F53884), + MK_64(0x384BDDB1,0xAEDDB5DE) + }; + +/* blkSize = 256 bits. hashSize = 256 bits */ +const u64b_t SKEIN_256_IV_256[] = + { + MK_64(0xFC9DA860,0xD048B449), + MK_64(0x2FCA6647,0x9FA7D833), + MK_64(0xB33BC389,0x6656840F), + MK_64(0x6A54E920,0xFDE8DA69) + }; + +/* blkSize = 512 bits. hashSize = 128 bits */ +const u64b_t SKEIN_512_IV_128[] = + { + MK_64(0xA8BC7BF3,0x6FBF9F52), + MK_64(0x1E9872CE,0xBD1AF0AA), + MK_64(0x309B1790,0xB32190D3), + MK_64(0xBCFBB854,0x3F94805C), + MK_64(0x0DA61BCD,0x6E31B11B), + MK_64(0x1A18EBEA,0xD46A32E3), + MK_64(0xA2CC5B18,0xCE84AA82), + MK_64(0x6982AB28,0x9D46982D) + }; + +/* blkSize = 512 bits. hashSize = 160 bits */ +const u64b_t SKEIN_512_IV_160[] = + { + MK_64(0x28B81A2A,0xE013BD91), + MK_64(0xC2F11668,0xB5BDF78F), + MK_64(0x1760D8F3,0xF6A56F12), + MK_64(0x4FB74758,0x8239904F), + MK_64(0x21EDE07F,0x7EAF5056), + MK_64(0xD908922E,0x63ED70B8), + MK_64(0xB8EC76FF,0xECCB52FA), + MK_64(0x01A47BB8,0xA3F27A6E) + }; + +/* blkSize = 512 bits. hashSize = 224 bits */ +const u64b_t SKEIN_512_IV_224[] = + { + MK_64(0xCCD06162,0x48677224), + MK_64(0xCBA65CF3,0xA92339EF), + MK_64(0x8CCD69D6,0x52FF4B64), + MK_64(0x398AED7B,0x3AB890B4), + MK_64(0x0F59D1B1,0x457D2BD0), + MK_64(0x6776FE65,0x75D4EB3D), + MK_64(0x99FBC70E,0x997413E9), + MK_64(0x9E2CFCCF,0xE1C41EF7) + }; + +/* blkSize = 512 bits. hashSize = 256 bits */ +const u64b_t SKEIN_512_IV_256[] = + { + MK_64(0xCCD044A1,0x2FDB3E13), + MK_64(0xE8359030,0x1A79A9EB), + MK_64(0x55AEA061,0x4F816E6F), + MK_64(0x2A2767A4,0xAE9B94DB), + MK_64(0xEC06025E,0x74DD7683), + MK_64(0xE7A436CD,0xC4746251), + MK_64(0xC36FBAF9,0x393AD185), + MK_64(0x3EEDBA18,0x33EDFC13) + }; + +/* blkSize = 512 bits. hashSize = 384 bits */ +const u64b_t SKEIN_512_IV_384[] = + { + MK_64(0xA3F6C6BF,0x3A75EF5F), + MK_64(0xB0FEF9CC,0xFD84FAA4), + MK_64(0x9D77DD66,0x3D770CFE), + MK_64(0xD798CBF3,0xB468FDDA), + MK_64(0x1BC4A666,0x8A0E4465), + MK_64(0x7ED7D434,0xE5807407), + MK_64(0x548FC1AC,0xD4EC44D6), + MK_64(0x266E1754,0x6AA18FF8) + }; + +/* blkSize = 512 bits. hashSize = 512 bits */ +const u64b_t SKEIN_512_IV_512[] = + { + MK_64(0x4903ADFF,0x749C51CE), + MK_64(0x0D95DE39,0x9746DF03), + MK_64(0x8FD19341,0x27C79BCE), + MK_64(0x9A255629,0xFF352CB1), + MK_64(0x5DB62599,0xDF6CA7B0), + MK_64(0xEABE394C,0xA9D5C3F4), + MK_64(0x991112C7,0x1A75B523), + MK_64(0xAE18A40B,0x660FCC33) + }; + +/* blkSize = 1024 bits. hashSize = 384 bits */ +const u64b_t SKEIN1024_IV_384[] = + { + MK_64(0x5102B6B8,0xC1894A35), + MK_64(0xFEEBC9E3,0xFE8AF11A), + MK_64(0x0C807F06,0xE32BED71), + MK_64(0x60C13A52,0xB41A91F6), + MK_64(0x9716D35D,0xD4917C38), + MK_64(0xE780DF12,0x6FD31D3A), + MK_64(0x797846B6,0xC898303A), + MK_64(0xB172C2A8,0xB3572A3B), + MK_64(0xC9BC8203,0xA6104A6C), + MK_64(0x65909338,0xD75624F4), + MK_64(0x94BCC568,0x4B3F81A0), + MK_64(0x3EBBF51E,0x10ECFD46), + MK_64(0x2DF50F0B,0xEEB08542), + MK_64(0x3B5A6530,0x0DBC6516), + MK_64(0x484B9CD2,0x167BBCE1), + MK_64(0x2D136947,0xD4CBAFEA) + }; + +/* blkSize = 1024 bits. hashSize = 512 bits */ +const u64b_t SKEIN1024_IV_512[] = + { + MK_64(0xCAEC0E5D,0x7C1B1B18), + MK_64(0xA01B0E04,0x5F03E802), + MK_64(0x33840451,0xED912885), + MK_64(0x374AFB04,0xEAEC2E1C), + MK_64(0xDF25A0E2,0x813581F7), + MK_64(0xE4004093,0x8B12F9D2), + MK_64(0xA662D539,0xC2ED39B6), + MK_64(0xFA8B85CF,0x45D8C75A), + MK_64(0x8316ED8E,0x29EDE796), + MK_64(0x053289C0,0x2E9F91B8), + MK_64(0xC3F8EF1D,0x6D518B73), + MK_64(0xBDCEC3C4,0xD5EF332E), + MK_64(0x549A7E52,0x22974487), + MK_64(0x67070872,0x5B749816), + MK_64(0xB9CD28FB,0xF0581BD1), + MK_64(0x0E2940B8,0x15804974) + }; + +/* blkSize = 1024 bits. hashSize = 1024 bits */ +const u64b_t SKEIN1024_IV_1024[] = + { + MK_64(0xD593DA07,0x41E72355), + MK_64(0x15B5E511,0xAC73E00C), + MK_64(0x5180E5AE,0xBAF2C4F0), + MK_64(0x03BD41D3,0xFCBCAFAF), + MK_64(0x1CAEC6FD,0x1983A898), + MK_64(0x6E510B8B,0xCDD0589F), + MK_64(0x77E2BDFD,0xC6394ADA), + MK_64(0xC11E1DB5,0x24DCB0A3), + MK_64(0xD6D14AF9,0xC6329AB5), + MK_64(0x6A9B0BFC,0x6EB67E0D), + MK_64(0x9243C60D,0xCCFF1332), + MK_64(0x1A1F1DDE,0x743F02D4), + MK_64(0x0996753C,0x10ED0BB8), + MK_64(0x6572DD22,0xF2B4969A), + MK_64(0x61FD3062,0xD00A579A), + MK_64(0x1DE0536E,0x8682E539) + }; + + +#ifndef SKEIN_USE_ASM +#define SKEIN_USE_ASM (0) /* default is all C code (no ASM) */ +#endif + +#ifndef SKEIN_LOOP +#define SKEIN_LOOP 001 /* default: unroll 256 and 512, but not 1024 */ +#endif + +#define BLK_BITS (WCNT*64) /* some useful definitions for code here */ +#define KW_TWK_BASE (0) +#define KW_KEY_BASE (3) +#define ks (kw + KW_KEY_BASE) +#define ts (kw + KW_TWK_BASE) + +#ifdef SKEIN_DEBUG +#define DebugSaveTweak(ctx) { ctx->h.T[0] = ts[0]; ctx->h.T[1] = ts[1]; } +#else +#define DebugSaveTweak(ctx) +#endif + +/***************************** Skein_256 ******************************/ +#if !(SKEIN_USE_ASM & 256) +static void Skein_256_Process_Block(Skein_256_Ctxt_t *ctx,const u08b_t *blkPtr,size_t blkCnt,size_t byteCntAdd) + { /* do it in C */ + enum + { + WCNT = SKEIN_256_STATE_WORDS + }; +#undef RCNT +#define RCNT (SKEIN_256_ROUNDS_TOTAL/8) + +#ifdef SKEIN_LOOP /* configure how much to unroll the loop */ +#define SKEIN_UNROLL_256 (((SKEIN_LOOP)/100)%10) +#else +#define SKEIN_UNROLL_256 (0) +#endif + +#if SKEIN_UNROLL_256 +#if (RCNT % SKEIN_UNROLL_256) +#error "Invalid SKEIN_UNROLL_256" /* sanity check on unroll count */ +#endif + size_t r; + u64b_t kw[WCNT+4+RCNT*2]; /* key schedule words : chaining vars + tweak + "rotation"*/ +#else + u64b_t kw[WCNT+4]; /* key schedule words : chaining vars + tweak */ +#endif + u64b_t X0,X1,X2,X3; /* local copy of context vars, for speed */ + u64b_t w [WCNT]; /* local copy of input block */ +#ifdef SKEIN_DEBUG + const u64b_t *Xptr[4]; /* use for debugging (help compiler put Xn in registers) */ + Xptr[0] = &X0; Xptr[1] = &X1; Xptr[2] = &X2; Xptr[3] = &X3; +#endif + Skein_assert(blkCnt != 0); /* never call with blkCnt == 0! */ + ts[0] = ctx->h.T[0]; + ts[1] = ctx->h.T[1]; + do { + /* this implementation only supports 2**64 input bytes (no carry out here) */ + ts[0] += byteCntAdd; /* update processed length */ + + /* precompute the key schedule for this block */ + ks[0] = ctx->X[0]; + ks[1] = ctx->X[1]; + ks[2] = ctx->X[2]; + ks[3] = ctx->X[3]; + ks[4] = ks[0] ^ ks[1] ^ ks[2] ^ ks[3] ^ SKEIN_KS_PARITY; + + ts[2] = ts[0] ^ ts[1]; + + Skein_Get64_LSB_First(w,blkPtr,WCNT); /* get input block in little-endian format */ + DebugSaveTweak(ctx); + Skein_Show_Block(BLK_BITS,&ctx->h,ctx->X,blkPtr,w,ks,ts); + + X0 = w[0] + ks[0]; /* do the first full key injection */ + X1 = w[1] + ks[1] + ts[0]; + X2 = w[2] + ks[2] + ts[1]; + X3 = w[3] + ks[3]; + + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INITIAL,Xptr); /* show starting state values */ + + blkPtr += SKEIN_256_BLOCK_BYTES; + + /* run the rounds */ + +#define Round256(p0,p1,p2,p3,ROT,rNum) \ + X##p0 += X##p1; X##p1 = RotL_64(X##p1,ROT##_0); X##p1 ^= X##p0; \ + X##p2 += X##p3; X##p3 = RotL_64(X##p3,ROT##_1); X##p3 ^= X##p2; \ + +#if SKEIN_UNROLL_256 == 0 +#define R256(p0,p1,p2,p3,ROT,rNum) /* fully unrolled */ \ + Round256(p0,p1,p2,p3,ROT,rNum) \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,rNum,Xptr); + +#define I256(R) \ + X0 += ks[((R)+1) % 5]; /* inject the key schedule value */ \ + X1 += ks[((R)+2) % 5] + ts[((R)+1) % 3]; \ + X2 += ks[((R)+3) % 5] + ts[((R)+2) % 3]; \ + X3 += ks[((R)+4) % 5] + (R)+1; \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); +#else /* looping version */ +#define R256(p0,p1,p2,p3,ROT,rNum) \ + Round256(p0,p1,p2,p3,ROT,rNum) \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,4*(r-1)+rNum,Xptr); + +#define I256(R) \ + X0 += ks[r+(R)+0]; /* inject the key schedule value */ \ + X1 += ks[r+(R)+1] + ts[r+(R)+0]; \ + X2 += ks[r+(R)+2] + ts[r+(R)+1]; \ + X3 += ks[r+(R)+3] + r+(R) ; \ + ks[r + (R)+4 ] = ks[r+(R)-1]; /* rotate key schedule */\ + ts[r + (R)+2 ] = ts[r+(R)-1]; \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); + + for (r=1;r < 2*RCNT;r+=2*SKEIN_UNROLL_256) /* loop thru it */ +#endif + { +#define R256_8_rounds(R) \ + R256(0,1,2,3,R_256_0,8*(R) + 1); \ + R256(0,3,2,1,R_256_1,8*(R) + 2); \ + R256(0,1,2,3,R_256_2,8*(R) + 3); \ + R256(0,3,2,1,R_256_3,8*(R) + 4); \ + I256(2*(R)); \ + R256(0,1,2,3,R_256_4,8*(R) + 5); \ + R256(0,3,2,1,R_256_5,8*(R) + 6); \ + R256(0,1,2,3,R_256_6,8*(R) + 7); \ + R256(0,3,2,1,R_256_7,8*(R) + 8); \ + I256(2*(R)+1); + + R256_8_rounds( 0); + +#define R256_Unroll_R(NN) ((SKEIN_UNROLL_256 == 0 && SKEIN_256_ROUNDS_TOTAL/8 > (NN)) || (SKEIN_UNROLL_256 > (NN))) + + #if R256_Unroll_R( 1) + R256_8_rounds( 1); + #endif + #if R256_Unroll_R( 2) + R256_8_rounds( 2); + #endif + #if R256_Unroll_R( 3) + R256_8_rounds( 3); + #endif + #if R256_Unroll_R( 4) + R256_8_rounds( 4); + #endif + #if R256_Unroll_R( 5) + R256_8_rounds( 5); + #endif + #if R256_Unroll_R( 6) + R256_8_rounds( 6); + #endif + #if R256_Unroll_R( 7) + R256_8_rounds( 7); + #endif + #if R256_Unroll_R( 8) + R256_8_rounds( 8); + #endif + #if R256_Unroll_R( 9) + R256_8_rounds( 9); + #endif + #if R256_Unroll_R(10) + R256_8_rounds(10); + #endif + #if R256_Unroll_R(11) + R256_8_rounds(11); + #endif + #if R256_Unroll_R(12) + R256_8_rounds(12); + #endif + #if R256_Unroll_R(13) + R256_8_rounds(13); + #endif + #if R256_Unroll_R(14) + R256_8_rounds(14); + #endif + #if (SKEIN_UNROLL_256 > 14) +#error "need more unrolling in Skein_256_Process_Block" + #endif + } + /* do the final "feedforward" xor, update context chaining vars */ + ctx->X[0] = X0 ^ w[0]; + ctx->X[1] = X1 ^ w[1]; + ctx->X[2] = X2 ^ w[2]; + ctx->X[3] = X3 ^ w[3]; + + Skein_Show_Round(BLK_BITS,&ctx->h,SKEIN_RND_FEED_FWD,ctx->X); + + ts[1] &= ~SKEIN_T1_FLAG_FIRST; + } + while (--blkCnt); + ctx->h.T[0] = ts[0]; + ctx->h.T[1] = ts[1]; + } + +#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) +static size_t Skein_256_Process_Block_CodeSize(void) + { + return ((u08b_t *) Skein_256_Process_Block_CodeSize) - + ((u08b_t *) Skein_256_Process_Block); + } +static uint_t Skein_256_Unroll_Cnt(void) + { + return SKEIN_UNROLL_256; + } +#endif +#endif + +/***************************** Skein_512 ******************************/ +#if !(SKEIN_USE_ASM & 512) +static void Skein_512_Process_Block(Skein_512_Ctxt_t *ctx,const u08b_t *blkPtr,size_t blkCnt,size_t byteCntAdd) + { /* do it in C */ + enum + { + WCNT = SKEIN_512_STATE_WORDS + }; +#undef RCNT +#define RCNT (SKEIN_512_ROUNDS_TOTAL/8) + +#ifdef SKEIN_LOOP /* configure how much to unroll the loop */ +#define SKEIN_UNROLL_512 (((SKEIN_LOOP)/10)%10) +#else +#define SKEIN_UNROLL_512 (0) +#endif + +#if SKEIN_UNROLL_512 +#if (RCNT % SKEIN_UNROLL_512) +#error "Invalid SKEIN_UNROLL_512" /* sanity check on unroll count */ +#endif + size_t r; + u64b_t kw[WCNT+4+RCNT*2]; /* key schedule words : chaining vars + tweak + "rotation"*/ +#else + u64b_t kw[WCNT+4]; /* key schedule words : chaining vars + tweak */ +#endif + u64b_t X0,X1,X2,X3,X4,X5,X6,X7; /* local copy of vars, for speed */ + u64b_t w [WCNT]; /* local copy of input block */ +#ifdef SKEIN_DEBUG + const u64b_t *Xptr[8]; /* use for debugging (help compiler put Xn in registers) */ + Xptr[0] = &X0; Xptr[1] = &X1; Xptr[2] = &X2; Xptr[3] = &X3; + Xptr[4] = &X4; Xptr[5] = &X5; Xptr[6] = &X6; Xptr[7] = &X7; +#endif + + Skein_assert(blkCnt != 0); /* never call with blkCnt == 0! */ + ts[0] = ctx->h.T[0]; + ts[1] = ctx->h.T[1]; + do { + /* this implementation only supports 2**64 input bytes (no carry out here) */ + ts[0] += byteCntAdd; /* update processed length */ + + /* precompute the key schedule for this block */ + ks[0] = ctx->X[0]; + ks[1] = ctx->X[1]; + ks[2] = ctx->X[2]; + ks[3] = ctx->X[3]; + ks[4] = ctx->X[4]; + ks[5] = ctx->X[5]; + ks[6] = ctx->X[6]; + ks[7] = ctx->X[7]; + ks[8] = ks[0] ^ ks[1] ^ ks[2] ^ ks[3] ^ + ks[4] ^ ks[5] ^ ks[6] ^ ks[7] ^ SKEIN_KS_PARITY; + + ts[2] = ts[0] ^ ts[1]; + + Skein_Get64_LSB_First(w,blkPtr,WCNT); /* get input block in little-endian format */ + DebugSaveTweak(ctx); + Skein_Show_Block(BLK_BITS,&ctx->h,ctx->X,blkPtr,w,ks,ts); + + X0 = w[0] + ks[0]; /* do the first full key injection */ + X1 = w[1] + ks[1]; + X2 = w[2] + ks[2]; + X3 = w[3] + ks[3]; + X4 = w[4] + ks[4]; + X5 = w[5] + ks[5] + ts[0]; + X6 = w[6] + ks[6] + ts[1]; + X7 = w[7] + ks[7]; + + blkPtr += SKEIN_512_BLOCK_BYTES; + + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INITIAL,Xptr); + /* run the rounds */ +#define Round512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \ + X##p0 += X##p1; X##p1 = RotL_64(X##p1,ROT##_0); X##p1 ^= X##p0; \ + X##p2 += X##p3; X##p3 = RotL_64(X##p3,ROT##_1); X##p3 ^= X##p2; \ + X##p4 += X##p5; X##p5 = RotL_64(X##p5,ROT##_2); X##p5 ^= X##p4; \ + X##p6 += X##p7; X##p7 = RotL_64(X##p7,ROT##_3); X##p7 ^= X##p6; \ + +#if SKEIN_UNROLL_512 == 0 +#define R512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) /* unrolled */ \ + Round512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,rNum,Xptr); + +#define I512(R) \ + X0 += ks[((R)+1) % 9]; /* inject the key schedule value */ \ + X1 += ks[((R)+2) % 9]; \ + X2 += ks[((R)+3) % 9]; \ + X3 += ks[((R)+4) % 9]; \ + X4 += ks[((R)+5) % 9]; \ + X5 += ks[((R)+6) % 9] + ts[((R)+1) % 3]; \ + X6 += ks[((R)+7) % 9] + ts[((R)+2) % 3]; \ + X7 += ks[((R)+8) % 9] + (R)+1; \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); +#else /* looping version */ +#define R512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \ + Round512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,4*(r-1)+rNum,Xptr); + +#define I512(R) \ + X0 += ks[r+(R)+0]; /* inject the key schedule value */ \ + X1 += ks[r+(R)+1]; \ + X2 += ks[r+(R)+2]; \ + X3 += ks[r+(R)+3]; \ + X4 += ks[r+(R)+4]; \ + X5 += ks[r+(R)+5] + ts[r+(R)+0]; \ + X6 += ks[r+(R)+6] + ts[r+(R)+1]; \ + X7 += ks[r+(R)+7] + r+(R) ; \ + ks[r + (R)+8] = ks[r+(R)-1]; /* rotate key schedule */ \ + ts[r + (R)+2] = ts[r+(R)-1]; \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); + + for (r=1;r < 2*RCNT;r+=2*SKEIN_UNROLL_512) /* loop thru it */ +#endif /* end of looped code definitions */ + { +#define R512_8_rounds(R) /* do 8 full rounds */ \ + R512(0,1,2,3,4,5,6,7,R_512_0,8*(R)+ 1); \ + R512(2,1,4,7,6,5,0,3,R_512_1,8*(R)+ 2); \ + R512(4,1,6,3,0,5,2,7,R_512_2,8*(R)+ 3); \ + R512(6,1,0,7,2,5,4,3,R_512_3,8*(R)+ 4); \ + I512(2*(R)); \ + R512(0,1,2,3,4,5,6,7,R_512_4,8*(R)+ 5); \ + R512(2,1,4,7,6,5,0,3,R_512_5,8*(R)+ 6); \ + R512(4,1,6,3,0,5,2,7,R_512_6,8*(R)+ 7); \ + R512(6,1,0,7,2,5,4,3,R_512_7,8*(R)+ 8); \ + I512(2*(R)+1); /* and key injection */ + + R512_8_rounds( 0); + +#define R512_Unroll_R(NN) ((SKEIN_UNROLL_512 == 0 && SKEIN_512_ROUNDS_TOTAL/8 > (NN)) || (SKEIN_UNROLL_512 > (NN))) + + #if R512_Unroll_R( 1) + R512_8_rounds( 1); + #endif + #if R512_Unroll_R( 2) + R512_8_rounds( 2); + #endif + #if R512_Unroll_R( 3) + R512_8_rounds( 3); + #endif + #if R512_Unroll_R( 4) + R512_8_rounds( 4); + #endif + #if R512_Unroll_R( 5) + R512_8_rounds( 5); + #endif + #if R512_Unroll_R( 6) + R512_8_rounds( 6); + #endif + #if R512_Unroll_R( 7) + R512_8_rounds( 7); + #endif + #if R512_Unroll_R( 8) + R512_8_rounds( 8); + #endif + #if R512_Unroll_R( 9) + R512_8_rounds( 9); + #endif + #if R512_Unroll_R(10) + R512_8_rounds(10); + #endif + #if R512_Unroll_R(11) + R512_8_rounds(11); + #endif + #if R512_Unroll_R(12) + R512_8_rounds(12); + #endif + #if R512_Unroll_R(13) + R512_8_rounds(13); + #endif + #if R512_Unroll_R(14) + R512_8_rounds(14); + #endif + #if (SKEIN_UNROLL_512 > 14) +#error "need more unrolling in Skein_512_Process_Block" + #endif + } + + /* do the final "feedforward" xor, update context chaining vars */ + ctx->X[0] = X0 ^ w[0]; + ctx->X[1] = X1 ^ w[1]; + ctx->X[2] = X2 ^ w[2]; + ctx->X[3] = X3 ^ w[3]; + ctx->X[4] = X4 ^ w[4]; + ctx->X[5] = X5 ^ w[5]; + ctx->X[6] = X6 ^ w[6]; + ctx->X[7] = X7 ^ w[7]; + Skein_Show_Round(BLK_BITS,&ctx->h,SKEIN_RND_FEED_FWD,ctx->X); + + ts[1] &= ~SKEIN_T1_FLAG_FIRST; + } + while (--blkCnt); + ctx->h.T[0] = ts[0]; + ctx->h.T[1] = ts[1]; + } + +#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) +static size_t Skein_512_Process_Block_CodeSize(void) + { + return ((u08b_t *) Skein_512_Process_Block_CodeSize) - + ((u08b_t *) Skein_512_Process_Block); + } +static uint_t Skein_512_Unroll_Cnt(void) + { + return SKEIN_UNROLL_512; + } +#endif +#endif + +/***************************** Skein1024 ******************************/ +#if !(SKEIN_USE_ASM & 1024) +static void Skein1024_Process_Block(Skein1024_Ctxt_t *ctx,const u08b_t *blkPtr,size_t blkCnt,size_t byteCntAdd) + { /* do it in C, always looping (unrolled is bigger AND slower!) */ + enum + { + WCNT = SKEIN1024_STATE_WORDS + }; +#undef RCNT +#define RCNT (SKEIN1024_ROUNDS_TOTAL/8) + +#ifdef SKEIN_LOOP /* configure how much to unroll the loop */ +#define SKEIN_UNROLL_1024 ((SKEIN_LOOP)%10) +#else +#define SKEIN_UNROLL_1024 (0) +#endif + +#if (SKEIN_UNROLL_1024 != 0) +#if (RCNT % SKEIN_UNROLL_1024) +#error "Invalid SKEIN_UNROLL_1024" /* sanity check on unroll count */ +#endif + size_t r; + u64b_t kw[WCNT+4+RCNT*2]; /* key schedule words : chaining vars + tweak + "rotation"*/ +#else + u64b_t kw[WCNT+4]; /* key schedule words : chaining vars + tweak */ +#endif + + u64b_t X00,X01,X02,X03,X04,X05,X06,X07, /* local copy of vars, for speed */ + X08,X09,X10,X11,X12,X13,X14,X15; + u64b_t w [WCNT]; /* local copy of input block */ +#ifdef SKEIN_DEBUG + const u64b_t *Xptr[16]; /* use for debugging (help compiler put Xn in registers) */ + Xptr[ 0] = &X00; Xptr[ 1] = &X01; Xptr[ 2] = &X02; Xptr[ 3] = &X03; + Xptr[ 4] = &X04; Xptr[ 5] = &X05; Xptr[ 6] = &X06; Xptr[ 7] = &X07; + Xptr[ 8] = &X08; Xptr[ 9] = &X09; Xptr[10] = &X10; Xptr[11] = &X11; + Xptr[12] = &X12; Xptr[13] = &X13; Xptr[14] = &X14; Xptr[15] = &X15; +#endif + + Skein_assert(blkCnt != 0); /* never call with blkCnt == 0! */ + ts[0] = ctx->h.T[0]; + ts[1] = ctx->h.T[1]; + do { + /* this implementation only supports 2**64 input bytes (no carry out here) */ + ts[0] += byteCntAdd; /* update processed length */ + + /* precompute the key schedule for this block */ + ks[ 0] = ctx->X[ 0]; + ks[ 1] = ctx->X[ 1]; + ks[ 2] = ctx->X[ 2]; + ks[ 3] = ctx->X[ 3]; + ks[ 4] = ctx->X[ 4]; + ks[ 5] = ctx->X[ 5]; + ks[ 6] = ctx->X[ 6]; + ks[ 7] = ctx->X[ 7]; + ks[ 8] = ctx->X[ 8]; + ks[ 9] = ctx->X[ 9]; + ks[10] = ctx->X[10]; + ks[11] = ctx->X[11]; + ks[12] = ctx->X[12]; + ks[13] = ctx->X[13]; + ks[14] = ctx->X[14]; + ks[15] = ctx->X[15]; + ks[16] = ks[ 0] ^ ks[ 1] ^ ks[ 2] ^ ks[ 3] ^ + ks[ 4] ^ ks[ 5] ^ ks[ 6] ^ ks[ 7] ^ + ks[ 8] ^ ks[ 9] ^ ks[10] ^ ks[11] ^ + ks[12] ^ ks[13] ^ ks[14] ^ ks[15] ^ SKEIN_KS_PARITY; + + ts[2] = ts[0] ^ ts[1]; + + Skein_Get64_LSB_First(w,blkPtr,WCNT); /* get input block in little-endian format */ + DebugSaveTweak(ctx); + Skein_Show_Block(BLK_BITS,&ctx->h,ctx->X,blkPtr,w,ks,ts); + + X00 = w[ 0] + ks[ 0]; /* do the first full key injection */ + X01 = w[ 1] + ks[ 1]; + X02 = w[ 2] + ks[ 2]; + X03 = w[ 3] + ks[ 3]; + X04 = w[ 4] + ks[ 4]; + X05 = w[ 5] + ks[ 5]; + X06 = w[ 6] + ks[ 6]; + X07 = w[ 7] + ks[ 7]; + X08 = w[ 8] + ks[ 8]; + X09 = w[ 9] + ks[ 9]; + X10 = w[10] + ks[10]; + X11 = w[11] + ks[11]; + X12 = w[12] + ks[12]; + X13 = w[13] + ks[13] + ts[0]; + X14 = w[14] + ks[14] + ts[1]; + X15 = w[15] + ks[15]; + + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INITIAL,Xptr); + +#define Round1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rNum) \ + X##p0 += X##p1; X##p1 = RotL_64(X##p1,ROT##_0); X##p1 ^= X##p0; \ + X##p2 += X##p3; X##p3 = RotL_64(X##p3,ROT##_1); X##p3 ^= X##p2; \ + X##p4 += X##p5; X##p5 = RotL_64(X##p5,ROT##_2); X##p5 ^= X##p4; \ + X##p6 += X##p7; X##p7 = RotL_64(X##p7,ROT##_3); X##p7 ^= X##p6; \ + X##p8 += X##p9; X##p9 = RotL_64(X##p9,ROT##_4); X##p9 ^= X##p8; \ + X##pA += X##pB; X##pB = RotL_64(X##pB,ROT##_5); X##pB ^= X##pA; \ + X##pC += X##pD; X##pD = RotL_64(X##pD,ROT##_6); X##pD ^= X##pC; \ + X##pE += X##pF; X##pF = RotL_64(X##pF,ROT##_7); X##pF ^= X##pE; \ + +#if SKEIN_UNROLL_1024 == 0 +#define R1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rn) \ + Round1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rn) \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,rn,Xptr); + +#define I1024(R) \ + X00 += ks[((R)+ 1) % 17]; /* inject the key schedule value */ \ + X01 += ks[((R)+ 2) % 17]; \ + X02 += ks[((R)+ 3) % 17]; \ + X03 += ks[((R)+ 4) % 17]; \ + X04 += ks[((R)+ 5) % 17]; \ + X05 += ks[((R)+ 6) % 17]; \ + X06 += ks[((R)+ 7) % 17]; \ + X07 += ks[((R)+ 8) % 17]; \ + X08 += ks[((R)+ 9) % 17]; \ + X09 += ks[((R)+10) % 17]; \ + X10 += ks[((R)+11) % 17]; \ + X11 += ks[((R)+12) % 17]; \ + X12 += ks[((R)+13) % 17]; \ + X13 += ks[((R)+14) % 17] + ts[((R)+1) % 3]; \ + X14 += ks[((R)+15) % 17] + ts[((R)+2) % 3]; \ + X15 += ks[((R)+16) % 17] + (R)+1; \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); +#else /* looping version */ +#define R1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rn) \ + Round1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rn) \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,4*(r-1)+rn,Xptr); + +#define I1024(R) \ + X00 += ks[r+(R)+ 0]; /* inject the key schedule value */ \ + X01 += ks[r+(R)+ 1]; \ + X02 += ks[r+(R)+ 2]; \ + X03 += ks[r+(R)+ 3]; \ + X04 += ks[r+(R)+ 4]; \ + X05 += ks[r+(R)+ 5]; \ + X06 += ks[r+(R)+ 6]; \ + X07 += ks[r+(R)+ 7]; \ + X08 += ks[r+(R)+ 8]; \ + X09 += ks[r+(R)+ 9]; \ + X10 += ks[r+(R)+10]; \ + X11 += ks[r+(R)+11]; \ + X12 += ks[r+(R)+12]; \ + X13 += ks[r+(R)+13] + ts[r+(R)+0]; \ + X14 += ks[r+(R)+14] + ts[r+(R)+1]; \ + X15 += ks[r+(R)+15] + r+(R) ; \ + ks[r + (R)+16] = ks[r+(R)-1]; /* rotate key schedule */ \ + ts[r + (R)+ 2] = ts[r+(R)-1]; \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); + + for (r=1;r <= 2*RCNT;r+=2*SKEIN_UNROLL_1024) /* loop thru it */ +#endif + { +#define R1024_8_rounds(R) /* do 8 full rounds */ \ + R1024(00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,R1024_0,8*(R) + 1); \ + R1024(00,09,02,13,06,11,04,15,10,07,12,03,14,05,08,01,R1024_1,8*(R) + 2); \ + R1024(00,07,02,05,04,03,06,01,12,15,14,13,08,11,10,09,R1024_2,8*(R) + 3); \ + R1024(00,15,02,11,06,13,04,09,14,01,08,05,10,03,12,07,R1024_3,8*(R) + 4); \ + I1024(2*(R)); \ + R1024(00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,R1024_4,8*(R) + 5); \ + R1024(00,09,02,13,06,11,04,15,10,07,12,03,14,05,08,01,R1024_5,8*(R) + 6); \ + R1024(00,07,02,05,04,03,06,01,12,15,14,13,08,11,10,09,R1024_6,8*(R) + 7); \ + R1024(00,15,02,11,06,13,04,09,14,01,08,05,10,03,12,07,R1024_7,8*(R) + 8); \ + I1024(2*(R)+1); + + R1024_8_rounds( 0); + +#define R1024_Unroll_R(NN) ((SKEIN_UNROLL_1024 == 0 && SKEIN1024_ROUNDS_TOTAL/8 > (NN)) || (SKEIN_UNROLL_1024 > (NN))) + + #if R1024_Unroll_R( 1) + R1024_8_rounds( 1); + #endif + #if R1024_Unroll_R( 2) + R1024_8_rounds( 2); + #endif + #if R1024_Unroll_R( 3) + R1024_8_rounds( 3); + #endif + #if R1024_Unroll_R( 4) + R1024_8_rounds( 4); + #endif + #if R1024_Unroll_R( 5) + R1024_8_rounds( 5); + #endif + #if R1024_Unroll_R( 6) + R1024_8_rounds( 6); + #endif + #if R1024_Unroll_R( 7) + R1024_8_rounds( 7); + #endif + #if R1024_Unroll_R( 8) + R1024_8_rounds( 8); + #endif + #if R1024_Unroll_R( 9) + R1024_8_rounds( 9); + #endif + #if R1024_Unroll_R(10) + R1024_8_rounds(10); + #endif + #if R1024_Unroll_R(11) + R1024_8_rounds(11); + #endif + #if R1024_Unroll_R(12) + R1024_8_rounds(12); + #endif + #if R1024_Unroll_R(13) + R1024_8_rounds(13); + #endif + #if R1024_Unroll_R(14) + R1024_8_rounds(14); + #endif + #if (SKEIN_UNROLL_1024 > 14) +#error "need more unrolling in Skein_1024_Process_Block" + #endif + } + /* do the final "feedforward" xor, update context chaining vars */ + + ctx->X[ 0] = X00 ^ w[ 0]; + ctx->X[ 1] = X01 ^ w[ 1]; + ctx->X[ 2] = X02 ^ w[ 2]; + ctx->X[ 3] = X03 ^ w[ 3]; + ctx->X[ 4] = X04 ^ w[ 4]; + ctx->X[ 5] = X05 ^ w[ 5]; + ctx->X[ 6] = X06 ^ w[ 6]; + ctx->X[ 7] = X07 ^ w[ 7]; + ctx->X[ 8] = X08 ^ w[ 8]; + ctx->X[ 9] = X09 ^ w[ 9]; + ctx->X[10] = X10 ^ w[10]; + ctx->X[11] = X11 ^ w[11]; + ctx->X[12] = X12 ^ w[12]; + ctx->X[13] = X13 ^ w[13]; + ctx->X[14] = X14 ^ w[14]; + ctx->X[15] = X15 ^ w[15]; + + Skein_Show_Round(BLK_BITS,&ctx->h,SKEIN_RND_FEED_FWD,ctx->X); + + ts[1] &= ~SKEIN_T1_FLAG_FIRST; + blkPtr += SKEIN1024_BLOCK_BYTES; + } + while (--blkCnt); + ctx->h.T[0] = ts[0]; + ctx->h.T[1] = ts[1]; + } + +#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) +static size_t Skein1024_Process_Block_CodeSize(void) + { + return ((u08b_t *) Skein1024_Process_Block_CodeSize) - + ((u08b_t *) Skein1024_Process_Block); + } +static uint_t Skein1024_Unroll_Cnt(void) + { + return SKEIN_UNROLL_1024; + } +#endif +#endif + + +#if 0 +/*****************************************************************/ +/* 256-bit Skein */ +/*****************************************************************/ + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* init the context for a straight hashing operation */ +static int Skein_256_Init(Skein_256_Ctxt_t *ctx, size_t hashBitLen) + { + union + { + u08b_t b[SKEIN_256_STATE_BYTES]; + u64b_t w[SKEIN_256_STATE_WORDS]; + } cfg; /* config block */ + + Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); + ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ + + switch (hashBitLen) + { /* use pre-computed values, where available */ +#ifndef SKEIN_NO_PRECOMP + case 256: memcpy(ctx->X,SKEIN_256_IV_256,sizeof(ctx->X)); break; + case 224: memcpy(ctx->X,SKEIN_256_IV_224,sizeof(ctx->X)); break; + case 160: memcpy(ctx->X,SKEIN_256_IV_160,sizeof(ctx->X)); break; + case 128: memcpy(ctx->X,SKEIN_256_IV_128,sizeof(ctx->X)); break; +#endif + default: + /* here if there is no precomputed IV value available */ + /* build/process the config block, type == CONFIG (could be precomputed) */ + Skein_Start_New_Type(ctx,CFG_FINAL); /* set tweaks: T0=0; T1=CFG | FINAL */ + + cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); /* set the schema, version */ + cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ + cfg.w[2] = Skein_Swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL); + memset(&cfg.w[3],0,sizeof(cfg) - 3*sizeof(cfg.w[0])); /* zero pad config block */ + + /* compute the initial chaining values from config block */ + memset(ctx->X,0,sizeof(ctx->X)); /* zero the chaining variables */ + Skein_256_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); + break; + } + /* The chaining vars ctx->X are now initialized for the given hashBitLen. */ + /* Set up to process the data message portion of the hash (default) */ + Skein_Start_New_Type(ctx,MSG); /* T0=0, T1= MSG type */ + + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* init the context for a MAC and/or tree hash operation */ +/* [identical to Skein_256_Init() when keyBytes == 0 && treeInfo == SKEIN_CFG_TREE_INFO_SEQUENTIAL] */ +static int Skein_256_InitExt(Skein_256_Ctxt_t *ctx,size_t hashBitLen,u64b_t treeInfo, const u08b_t *key, size_t keyBytes) + { + union + { + u08b_t b[SKEIN_256_STATE_BYTES]; + u64b_t w[SKEIN_256_STATE_WORDS]; + } cfg; /* config block */ + + Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); + Skein_Assert(keyBytes == 0 || key != NULL,SKEIN_FAIL); + + /* compute the initial chaining values ctx->X[], based on key */ + if (keyBytes == 0) /* is there a key? */ + { + memset(ctx->X,0,sizeof(ctx->X)); /* no key: use all zeroes as key for config block */ + } + else /* here to pre-process a key */ + { + Skein_assert(sizeof(cfg.b) >= sizeof(ctx->X)); + /* do a mini-Init right here */ + ctx->h.hashBitLen=8*sizeof(ctx->X); /* set output hash bit count = state size */ + Skein_Start_New_Type(ctx,KEY); /* set tweaks: T0 = 0; T1 = KEY type */ + memset(ctx->X,0,sizeof(ctx->X)); /* zero the initial chaining variables */ + Skein_256_Update(ctx,key,keyBytes); /* hash the key */ + Skein_256_Final_Pad(ctx,cfg.b); /* put result into cfg.b[] */ + memcpy(ctx->X,cfg.b,sizeof(cfg.b)); /* copy over into ctx->X[] */ +#if SKEIN_NEED_SWAP + { + uint_t i; + for (i=0;i<SKEIN_256_STATE_WORDS;i++) /* convert key bytes to context words */ + ctx->X[i] = Skein_Swap64(ctx->X[i]); + } +#endif + } + /* build/process the config block, type == CONFIG (could be precomputed for each key) */ + ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ + Skein_Start_New_Type(ctx,CFG_FINAL); + + memset(&cfg.w,0,sizeof(cfg.w)); /* pre-pad cfg.w[] with zeroes */ + cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); + cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ + cfg.w[2] = Skein_Swap64(treeInfo); /* tree hash config info (or SKEIN_CFG_TREE_INFO_SEQUENTIAL) */ + + Skein_Show_Key(256,&ctx->h,key,keyBytes); + + /* compute the initial chaining values from config block */ + Skein_256_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); + + /* The chaining vars ctx->X are now initialized */ + /* Set up to process the data message portion of the hash (default) */ + ctx->h.bCnt = 0; /* buffer b[] starts out empty */ + Skein_Start_New_Type(ctx,MSG); + + return SKEIN_SUCCESS; + } +#endif + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* process the input bytes */ +static int Skein_256_Update(Skein_256_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt) + { + size_t n; + + Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + /* process full blocks, if any */ + if (msgByteCnt + ctx->h.bCnt > SKEIN_256_BLOCK_BYTES) + { + if (ctx->h.bCnt) /* finish up any buffered message data */ + { + n = SKEIN_256_BLOCK_BYTES - ctx->h.bCnt; /* # bytes free in buffer b[] */ + if (n) + { + Skein_assert(n < msgByteCnt); /* check on our logic here */ + memcpy(&ctx->b[ctx->h.bCnt],msg,n); + msgByteCnt -= n; + msg += n; + ctx->h.bCnt += n; + } + Skein_assert(ctx->h.bCnt == SKEIN_256_BLOCK_BYTES); + Skein_256_Process_Block(ctx,ctx->b,1,SKEIN_256_BLOCK_BYTES); + ctx->h.bCnt = 0; + } + /* now process any remaining full blocks, directly from input message data */ + if (msgByteCnt > SKEIN_256_BLOCK_BYTES) + { + n = (msgByteCnt-1) / SKEIN_256_BLOCK_BYTES; /* number of full blocks to process */ + Skein_256_Process_Block(ctx,msg,n,SKEIN_256_BLOCK_BYTES); + msgByteCnt -= n * SKEIN_256_BLOCK_BYTES; + msg += n * SKEIN_256_BLOCK_BYTES; + } + Skein_assert(ctx->h.bCnt == 0); + } + + /* copy any remaining source message data bytes into b[] */ + if (msgByteCnt) + { + Skein_assert(msgByteCnt + ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES); + memcpy(&ctx->b[ctx->h.bCnt],msg,msgByteCnt); + ctx->h.bCnt += msgByteCnt; + } + + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize the hash computation and output the result */ +static int Skein_256_Final(Skein_256_Ctxt_t *ctx, u08b_t *hashVal) + { + size_t i,n,byteCnt; + u64b_t X[SKEIN_256_STATE_WORDS]; + Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ + if (ctx->h.bCnt < SKEIN_256_BLOCK_BYTES) /* zero pad b[] if necessary */ + memset(&ctx->b[ctx->h.bCnt],0,SKEIN_256_BLOCK_BYTES - ctx->h.bCnt); + + Skein_256_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ + + /* now output the result */ + byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ + + /* run Threefish in "counter mode" to generate output */ + memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ + memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ + for (i=0;i*SKEIN_256_BLOCK_BYTES < byteCnt;i++) + { + ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ + Skein_Start_New_Type(ctx,OUT_FINAL); + Skein_256_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ + n = byteCnt - i*SKEIN_256_BLOCK_BYTES; /* number of output bytes left to go */ + if (n >= SKEIN_256_BLOCK_BYTES) + n = SKEIN_256_BLOCK_BYTES; + Skein_Put64_LSB_First(hashVal+i*SKEIN_256_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ + Skein_Show_Final(256,&ctx->h,n,hashVal+i*SKEIN_256_BLOCK_BYTES); + memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ + } + return SKEIN_SUCCESS; + } + +#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) +static size_t Skein_256_API_CodeSize(void) + { + return ((u08b_t *) Skein_256_API_CodeSize) - + ((u08b_t *) Skein_256_Init); + } +#endif + +/*****************************************************************/ +/* 512-bit Skein */ +/*****************************************************************/ + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* init the context for a straight hashing operation */ +static int Skein_512_Init(Skein_512_Ctxt_t *ctx, size_t hashBitLen) + { + union + { + u08b_t b[SKEIN_512_STATE_BYTES]; + u64b_t w[SKEIN_512_STATE_WORDS]; + } cfg; /* config block */ + + Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); + ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ + + switch (hashBitLen) + { /* use pre-computed values, where available */ +#ifndef SKEIN_NO_PRECOMP + case 512: memcpy(ctx->X,SKEIN_512_IV_512,sizeof(ctx->X)); break; + case 384: memcpy(ctx->X,SKEIN_512_IV_384,sizeof(ctx->X)); break; + case 256: memcpy(ctx->X,SKEIN_512_IV_256,sizeof(ctx->X)); break; + case 224: memcpy(ctx->X,SKEIN_512_IV_224,sizeof(ctx->X)); break; +#endif + default: + /* here if there is no precomputed IV value available */ + /* build/process the config block, type == CONFIG (could be precomputed) */ + Skein_Start_New_Type(ctx,CFG_FINAL); /* set tweaks: T0=0; T1=CFG | FINAL */ + + cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); /* set the schema, version */ + cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ + cfg.w[2] = Skein_Swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL); + memset(&cfg.w[3],0,sizeof(cfg) - 3*sizeof(cfg.w[0])); /* zero pad config block */ + + /* compute the initial chaining values from config block */ + memset(ctx->X,0,sizeof(ctx->X)); /* zero the chaining variables */ + Skein_512_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); + break; + } + + /* The chaining vars ctx->X are now initialized for the given hashBitLen. */ + /* Set up to process the data message portion of the hash (default) */ + Skein_Start_New_Type(ctx,MSG); /* T0=0, T1= MSG type */ + + return SKEIN_SUCCESS; + } + +#if 0 +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* init the context for a MAC and/or tree hash operation */ +/* [identical to Skein_512_Init() when keyBytes == 0 && treeInfo == SKEIN_CFG_TREE_INFO_SEQUENTIAL] */ +static int Skein_512_InitExt(Skein_512_Ctxt_t *ctx,size_t hashBitLen,u64b_t treeInfo, const u08b_t *key, size_t keyBytes) + { + union + { + u08b_t b[SKEIN_512_STATE_BYTES]; + u64b_t w[SKEIN_512_STATE_WORDS]; + } cfg; /* config block */ + + Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); + Skein_Assert(keyBytes == 0 || key != NULL,SKEIN_FAIL); + + /* compute the initial chaining values ctx->X[], based on key */ + if (keyBytes == 0) /* is there a key? */ + { + memset(ctx->X,0,sizeof(ctx->X)); /* no key: use all zeroes as key for config block */ + } + else /* here to pre-process a key */ + { + Skein_assert(sizeof(cfg.b) >= sizeof(ctx->X)); + /* do a mini-Init right here */ + ctx->h.hashBitLen=8*sizeof(ctx->X); /* set output hash bit count = state size */ + Skein_Start_New_Type(ctx,KEY); /* set tweaks: T0 = 0; T1 = KEY type */ + memset(ctx->X,0,sizeof(ctx->X)); /* zero the initial chaining variables */ + Skein_512_Update(ctx,key,keyBytes); /* hash the key */ + Skein_512_Final_Pad(ctx,cfg.b); /* put result into cfg.b[] */ + memcpy(ctx->X,cfg.b,sizeof(cfg.b)); /* copy over into ctx->X[] */ +#if SKEIN_NEED_SWAP + { + uint_t i; + for (i=0;i<SKEIN_512_STATE_WORDS;i++) /* convert key bytes to context words */ + ctx->X[i] = Skein_Swap64(ctx->X[i]); + } +#endif + } + /* build/process the config block, type == CONFIG (could be precomputed for each key) */ + ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ + Skein_Start_New_Type(ctx,CFG_FINAL); + + memset(&cfg.w,0,sizeof(cfg.w)); /* pre-pad cfg.w[] with zeroes */ + cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); + cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ + cfg.w[2] = Skein_Swap64(treeInfo); /* tree hash config info (or SKEIN_CFG_TREE_INFO_SEQUENTIAL) */ + + Skein_Show_Key(512,&ctx->h,key,keyBytes); + + /* compute the initial chaining values from config block */ + Skein_512_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); + + /* The chaining vars ctx->X are now initialized */ + /* Set up to process the data message portion of the hash (default) */ + ctx->h.bCnt = 0; /* buffer b[] starts out empty */ + Skein_Start_New_Type(ctx,MSG); + + return SKEIN_SUCCESS; + } +#endif + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* process the input bytes */ +static int Skein_512_Update(Skein_512_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt) + { + size_t n; + + Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + /* process full blocks, if any */ + if (msgByteCnt + ctx->h.bCnt > SKEIN_512_BLOCK_BYTES) + { + if (ctx->h.bCnt) /* finish up any buffered message data */ + { + n = SKEIN_512_BLOCK_BYTES - ctx->h.bCnt; /* # bytes free in buffer b[] */ + if (n) + { + Skein_assert(n < msgByteCnt); /* check on our logic here */ + memcpy(&ctx->b[ctx->h.bCnt],msg,n); + msgByteCnt -= n; + msg += n; + ctx->h.bCnt += n; + } + Skein_assert(ctx->h.bCnt == SKEIN_512_BLOCK_BYTES); + Skein_512_Process_Block(ctx,ctx->b,1,SKEIN_512_BLOCK_BYTES); + ctx->h.bCnt = 0; + } + /* now process any remaining full blocks, directly from input message data */ + if (msgByteCnt > SKEIN_512_BLOCK_BYTES) + { + n = (msgByteCnt-1) / SKEIN_512_BLOCK_BYTES; /* number of full blocks to process */ + Skein_512_Process_Block(ctx,msg,n,SKEIN_512_BLOCK_BYTES); + msgByteCnt -= n * SKEIN_512_BLOCK_BYTES; + msg += n * SKEIN_512_BLOCK_BYTES; + } + Skein_assert(ctx->h.bCnt == 0); + } + + /* copy any remaining source message data bytes into b[] */ + if (msgByteCnt) + { + Skein_assert(msgByteCnt + ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES); + memcpy(&ctx->b[ctx->h.bCnt],msg,msgByteCnt); + ctx->h.bCnt += msgByteCnt; + } + + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize the hash computation and output the result */ +static int Skein_512_Final(Skein_512_Ctxt_t *ctx, u08b_t *hashVal) + { + size_t i,n,byteCnt; + u64b_t X[SKEIN_512_STATE_WORDS]; + Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ + if (ctx->h.bCnt < SKEIN_512_BLOCK_BYTES) /* zero pad b[] if necessary */ + memset(&ctx->b[ctx->h.bCnt],0,SKEIN_512_BLOCK_BYTES - ctx->h.bCnt); + + Skein_512_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ + + /* now output the result */ + byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ + + /* run Threefish in "counter mode" to generate output */ + memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ + memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ + for (i=0;i*SKEIN_512_BLOCK_BYTES < byteCnt;i++) + { + ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ + Skein_Start_New_Type(ctx,OUT_FINAL); + Skein_512_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ + n = byteCnt - i*SKEIN_512_BLOCK_BYTES; /* number of output bytes left to go */ + if (n >= SKEIN_512_BLOCK_BYTES) + n = SKEIN_512_BLOCK_BYTES; + Skein_Put64_LSB_First(hashVal+i*SKEIN_512_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ + Skein_Show_Final(512,&ctx->h,n,hashVal+i*SKEIN_512_BLOCK_BYTES); + memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ + } + return SKEIN_SUCCESS; + } + +#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) +static size_t Skein_512_API_CodeSize(void) + { + return ((u08b_t *) Skein_512_API_CodeSize) - + ((u08b_t *) Skein_512_Init); + } +#endif + +/*****************************************************************/ +/* 1024-bit Skein */ +/*****************************************************************/ +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* init the context for a straight hashing operation */ +static int Skein1024_Init(Skein1024_Ctxt_t *ctx, size_t hashBitLen) + { + union + { + u08b_t b[SKEIN1024_STATE_BYTES]; + u64b_t w[SKEIN1024_STATE_WORDS]; + } cfg; /* config block */ + + Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); + ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ + + switch (hashBitLen) + { /* use pre-computed values, where available */ +#ifndef SKEIN_NO_PRECOMP + case 512: memcpy(ctx->X,SKEIN1024_IV_512 ,sizeof(ctx->X)); break; + case 384: memcpy(ctx->X,SKEIN1024_IV_384 ,sizeof(ctx->X)); break; + case 1024: memcpy(ctx->X,SKEIN1024_IV_1024,sizeof(ctx->X)); break; +#endif + default: + /* here if there is no precomputed IV value available */ + /* build/process the config block, type == CONFIG (could be precomputed) */ + Skein_Start_New_Type(ctx,CFG_FINAL); /* set tweaks: T0=0; T1=CFG | FINAL */ + + cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); /* set the schema, version */ + cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ + cfg.w[2] = Skein_Swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL); + memset(&cfg.w[3],0,sizeof(cfg) - 3*sizeof(cfg.w[0])); /* zero pad config block */ + + /* compute the initial chaining values from config block */ + memset(ctx->X,0,sizeof(ctx->X)); /* zero the chaining variables */ + Skein1024_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); + break; + } + + /* The chaining vars ctx->X are now initialized for the given hashBitLen. */ + /* Set up to process the data message portion of the hash (default) */ + Skein_Start_New_Type(ctx,MSG); /* T0=0, T1= MSG type */ + + return SKEIN_SUCCESS; + } + +#if 0 +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* init the context for a MAC and/or tree hash operation */ +/* [identical to Skein1024_Init() when keyBytes == 0 && treeInfo == SKEIN_CFG_TREE_INFO_SEQUENTIAL] */ +static int Skein1024_InitExt(Skein1024_Ctxt_t *ctx,size_t hashBitLen,u64b_t treeInfo, const u08b_t *key, size_t keyBytes) + { + union + { + u08b_t b[SKEIN1024_STATE_BYTES]; + u64b_t w[SKEIN1024_STATE_WORDS]; + } cfg; /* config block */ + + Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); + Skein_Assert(keyBytes == 0 || key != NULL,SKEIN_FAIL); + + /* compute the initial chaining values ctx->X[], based on key */ + if (keyBytes == 0) /* is there a key? */ + { + memset(ctx->X,0,sizeof(ctx->X)); /* no key: use all zeroes as key for config block */ + } + else /* here to pre-process a key */ + { + Skein_assert(sizeof(cfg.b) >= sizeof(ctx->X)); + /* do a mini-Init right here */ + ctx->h.hashBitLen=8*sizeof(ctx->X); /* set output hash bit count = state size */ + Skein_Start_New_Type(ctx,KEY); /* set tweaks: T0 = 0; T1 = KEY type */ + memset(ctx->X,0,sizeof(ctx->X)); /* zero the initial chaining variables */ + Skein1024_Update(ctx,key,keyBytes); /* hash the key */ + Skein1024_Final_Pad(ctx,cfg.b); /* put result into cfg.b[] */ + memcpy(ctx->X,cfg.b,sizeof(cfg.b)); /* copy over into ctx->X[] */ +#if SKEIN_NEED_SWAP + { + uint_t i; + for (i=0;i<SKEIN1024_STATE_WORDS;i++) /* convert key bytes to context words */ + ctx->X[i] = Skein_Swap64(ctx->X[i]); + } +#endif + } + /* build/process the config block, type == CONFIG (could be precomputed for each key) */ + ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ + Skein_Start_New_Type(ctx,CFG_FINAL); + + memset(&cfg.w,0,sizeof(cfg.w)); /* pre-pad cfg.w[] with zeroes */ + cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); + cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ + cfg.w[2] = Skein_Swap64(treeInfo); /* tree hash config info (or SKEIN_CFG_TREE_INFO_SEQUENTIAL) */ + + Skein_Show_Key(1024,&ctx->h,key,keyBytes); + + /* compute the initial chaining values from config block */ + Skein1024_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); + + /* The chaining vars ctx->X are now initialized */ + /* Set up to process the data message portion of the hash (default) */ + ctx->h.bCnt = 0; /* buffer b[] starts out empty */ + Skein_Start_New_Type(ctx,MSG); + + return SKEIN_SUCCESS; + } +#endif + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* process the input bytes */ +static int Skein1024_Update(Skein1024_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt) + { + size_t n; + + Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + /* process full blocks, if any */ + if (msgByteCnt + ctx->h.bCnt > SKEIN1024_BLOCK_BYTES) + { + if (ctx->h.bCnt) /* finish up any buffered message data */ + { + n = SKEIN1024_BLOCK_BYTES - ctx->h.bCnt; /* # bytes free in buffer b[] */ + if (n) + { + Skein_assert(n < msgByteCnt); /* check on our logic here */ + memcpy(&ctx->b[ctx->h.bCnt],msg,n); + msgByteCnt -= n; + msg += n; + ctx->h.bCnt += n; + } + Skein_assert(ctx->h.bCnt == SKEIN1024_BLOCK_BYTES); + Skein1024_Process_Block(ctx,ctx->b,1,SKEIN1024_BLOCK_BYTES); + ctx->h.bCnt = 0; + } + /* now process any remaining full blocks, directly from input message data */ + if (msgByteCnt > SKEIN1024_BLOCK_BYTES) + { + n = (msgByteCnt-1) / SKEIN1024_BLOCK_BYTES; /* number of full blocks to process */ + Skein1024_Process_Block(ctx,msg,n,SKEIN1024_BLOCK_BYTES); + msgByteCnt -= n * SKEIN1024_BLOCK_BYTES; + msg += n * SKEIN1024_BLOCK_BYTES; + } + Skein_assert(ctx->h.bCnt == 0); + } + + /* copy any remaining source message data bytes into b[] */ + if (msgByteCnt) + { + Skein_assert(msgByteCnt + ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES); + memcpy(&ctx->b[ctx->h.bCnt],msg,msgByteCnt); + ctx->h.bCnt += msgByteCnt; + } + + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize the hash computation and output the result */ +static int Skein1024_Final(Skein1024_Ctxt_t *ctx, u08b_t *hashVal) + { + size_t i,n,byteCnt; + u64b_t X[SKEIN1024_STATE_WORDS]; + Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ + if (ctx->h.bCnt < SKEIN1024_BLOCK_BYTES) /* zero pad b[] if necessary */ + memset(&ctx->b[ctx->h.bCnt],0,SKEIN1024_BLOCK_BYTES - ctx->h.bCnt); + + Skein1024_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ + + /* now output the result */ + byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ + + /* run Threefish in "counter mode" to generate output */ + memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ + memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ + for (i=0;i*SKEIN1024_BLOCK_BYTES < byteCnt;i++) + { + ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ + Skein_Start_New_Type(ctx,OUT_FINAL); + Skein1024_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ + n = byteCnt - i*SKEIN1024_BLOCK_BYTES; /* number of output bytes left to go */ + if (n >= SKEIN1024_BLOCK_BYTES) + n = SKEIN1024_BLOCK_BYTES; + Skein_Put64_LSB_First(hashVal+i*SKEIN1024_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ + Skein_Show_Final(1024,&ctx->h,n,hashVal+i*SKEIN1024_BLOCK_BYTES); + memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ + } + return SKEIN_SUCCESS; + } + +#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) +static size_t Skein1024_API_CodeSize(void) + { + return ((u08b_t *) Skein1024_API_CodeSize) - + ((u08b_t *) Skein1024_Init); + } +#endif + +/**************** Functions to support MAC/tree hashing ***************/ +/* (this code is identical for Optimized and Reference versions) */ + +#if 0 +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize the hash computation and output the block, no OUTPUT stage */ +static int Skein_256_Final_Pad(Skein_256_Ctxt_t *ctx, u08b_t *hashVal) + { + Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ + if (ctx->h.bCnt < SKEIN_256_BLOCK_BYTES) /* zero pad b[] if necessary */ + memset(&ctx->b[ctx->h.bCnt],0,SKEIN_256_BLOCK_BYTES - ctx->h.bCnt); + Skein_256_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ + + Skein_Put64_LSB_First(hashVal,ctx->X,SKEIN_256_BLOCK_BYTES); /* "output" the state bytes */ + + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize the hash computation and output the block, no OUTPUT stage */ +static int Skein_512_Final_Pad(Skein_512_Ctxt_t *ctx, u08b_t *hashVal) + { + Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ + if (ctx->h.bCnt < SKEIN_512_BLOCK_BYTES) /* zero pad b[] if necessary */ + memset(&ctx->b[ctx->h.bCnt],0,SKEIN_512_BLOCK_BYTES - ctx->h.bCnt); + Skein_512_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ + + Skein_Put64_LSB_First(hashVal,ctx->X,SKEIN_512_BLOCK_BYTES); /* "output" the state bytes */ + + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize the hash computation and output the block, no OUTPUT stage */ +static int Skein1024_Final_Pad(Skein1024_Ctxt_t *ctx, u08b_t *hashVal) + { + Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ + if (ctx->h.bCnt < SKEIN1024_BLOCK_BYTES) /* zero pad b[] if necessary */ + memset(&ctx->b[ctx->h.bCnt],0,SKEIN1024_BLOCK_BYTES - ctx->h.bCnt); + Skein1024_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ + + Skein_Put64_LSB_First(hashVal,ctx->X,SKEIN1024_BLOCK_BYTES); /* "output" the state bytes */ + + return SKEIN_SUCCESS; + } + + +#if SKEIN_TREE_HASH +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* just do the OUTPUT stage */ +static int Skein_256_Output(Skein_256_Ctxt_t *ctx, u08b_t *hashVal) + { + size_t i,n,byteCnt; + u64b_t X[SKEIN_256_STATE_WORDS]; + Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + /* now output the result */ + byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ + + /* run Threefish in "counter mode" to generate output */ + memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ + memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ + for (i=0;i*SKEIN_256_BLOCK_BYTES < byteCnt;i++) + { + ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ + Skein_Start_New_Type(ctx,OUT_FINAL); + Skein_256_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ + n = byteCnt - i*SKEIN_256_BLOCK_BYTES; /* number of output bytes left to go */ + if (n >= SKEIN_256_BLOCK_BYTES) + n = SKEIN_256_BLOCK_BYTES; + Skein_Put64_LSB_First(hashVal+i*SKEIN_256_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ + Skein_Show_Final(256,&ctx->h,n,hashVal+i*SKEIN_256_BLOCK_BYTES); + memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ + } + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* just do the OUTPUT stage */ +static int Skein_512_Output(Skein_512_Ctxt_t *ctx, u08b_t *hashVal) + { + size_t i,n,byteCnt; + u64b_t X[SKEIN_512_STATE_WORDS]; + Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + /* now output the result */ + byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ + + /* run Threefish in "counter mode" to generate output */ + memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ + memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ + for (i=0;i*SKEIN_512_BLOCK_BYTES < byteCnt;i++) + { + ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ + Skein_Start_New_Type(ctx,OUT_FINAL); + Skein_512_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ + n = byteCnt - i*SKEIN_512_BLOCK_BYTES; /* number of output bytes left to go */ + if (n >= SKEIN_512_BLOCK_BYTES) + n = SKEIN_512_BLOCK_BYTES; + Skein_Put64_LSB_First(hashVal+i*SKEIN_512_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ + Skein_Show_Final(256,&ctx->h,n,hashVal+i*SKEIN_512_BLOCK_BYTES); + memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ + } + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* just do the OUTPUT stage */ +static int Skein1024_Output(Skein1024_Ctxt_t *ctx, u08b_t *hashVal) + { + size_t i,n,byteCnt; + u64b_t X[SKEIN1024_STATE_WORDS]; + Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + /* now output the result */ + byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ + + /* run Threefish in "counter mode" to generate output */ + memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ + memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ + for (i=0;i*SKEIN1024_BLOCK_BYTES < byteCnt;i++) + { + ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ + Skein_Start_New_Type(ctx,OUT_FINAL); + Skein1024_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ + n = byteCnt - i*SKEIN1024_BLOCK_BYTES; /* number of output bytes left to go */ + if (n >= SKEIN1024_BLOCK_BYTES) + n = SKEIN1024_BLOCK_BYTES; + Skein_Put64_LSB_First(hashVal+i*SKEIN1024_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ + Skein_Show_Final(256,&ctx->h,n,hashVal+i*SKEIN1024_BLOCK_BYTES); + memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ + } + return SKEIN_SUCCESS; + } +#endif +#endif + +typedef struct +{ + uint_t statebits; /* 256, 512, or 1024 */ + union + { + Skein_Ctxt_Hdr_t h; /* common header "overlay" */ + Skein_256_Ctxt_t ctx_256; + Skein_512_Ctxt_t ctx_512; + Skein1024_Ctxt_t ctx1024; + } u; +} +hashState; + +/* "incremental" hashing API */ +static HashReturn Init (hashState *state, int hashbitlen); +static HashReturn Update(hashState *state, const BitSequence *data, DataLength databitlen); +static HashReturn Final (hashState *state, BitSequence *hashval); + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* select the context size and init the context */ +static HashReturn Init(hashState *state, int hashbitlen) +{ +#if SKEIN_256_NIST_MAX_HASH_BITS + if (hashbitlen <= SKEIN_256_NIST_MAX_HASHBITS) + { + Skein_Assert(hashbitlen > 0,BAD_HASHLEN); + state->statebits = 64*SKEIN_256_STATE_WORDS; + return Skein_256_Init(&state->u.ctx_256,(size_t) hashbitlen); + } +#endif + if (hashbitlen <= SKEIN_512_NIST_MAX_HASHBITS) + { + state->statebits = 64*SKEIN_512_STATE_WORDS; + return Skein_512_Init(&state->u.ctx_512,(size_t) hashbitlen); + } + else + { + state->statebits = 64*SKEIN1024_STATE_WORDS; + return Skein1024_Init(&state->u.ctx1024,(size_t) hashbitlen); + } +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* process data to be hashed */ +static HashReturn Update(hashState *state, const BitSequence *data, DataLength databitlen) +{ + /* only the final Update() call is allowed do partial bytes, else assert an error */ + Skein_Assert((state->u.h.T[1] & SKEIN_T1_FLAG_BIT_PAD) == 0 || databitlen == 0, SKEIN_FAIL); + + Skein_Assert(state->statebits % 256 == 0 && (state->statebits-256) < 1024,SKEIN_FAIL); + if ((databitlen & 7) == 0) /* partial bytes? */ + { + switch ((state->statebits >> 8) & 3) + { + case 2: return Skein_512_Update(&state->u.ctx_512,data,databitlen >> 3); + case 1: return Skein_256_Update(&state->u.ctx_256,data,databitlen >> 3); + case 0: return Skein1024_Update(&state->u.ctx1024,data,databitlen >> 3); + default: return SKEIN_FAIL; + } + } + else + { /* handle partial final byte */ + size_t bCnt = (databitlen >> 3) + 1; /* number of bytes to handle (nonzero here!) */ + u08b_t b,mask; + + mask = (u08b_t) (1u << (7 - (databitlen & 7))); /* partial byte bit mask */ + b = (u08b_t) ((data[bCnt-1] & (0-mask)) | mask); /* apply bit padding on final byte */ + + switch ((state->statebits >> 8) & 3) + { + case 2: Skein_512_Update(&state->u.ctx_512,data,bCnt-1); /* process all but the final byte */ + Skein_512_Update(&state->u.ctx_512,&b , 1 ); /* process the (masked) partial byte */ + break; + case 1: Skein_256_Update(&state->u.ctx_256,data,bCnt-1); /* process all but the final byte */ + Skein_256_Update(&state->u.ctx_256,&b , 1 ); /* process the (masked) partial byte */ + break; + case 0: Skein1024_Update(&state->u.ctx1024,data,bCnt-1); /* process all but the final byte */ + Skein1024_Update(&state->u.ctx1024,&b , 1 ); /* process the (masked) partial byte */ + break; + default: return SKEIN_FAIL; + } + Skein_Set_Bit_Pad_Flag(state->u.h); /* set tweak flag for the final call */ + + return SKEIN_SUCCESS; + } +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize hash computation and output the result (hashbitlen bits) */ +static HashReturn Final(hashState *state, BitSequence *hashval) +{ + Skein_Assert(state->statebits % 256 == 0 && (state->statebits-256) < 1024,FAIL); + switch ((state->statebits >> 8) & 3) + { + case 2: return Skein_512_Final(&state->u.ctx_512,hashval); + case 1: return Skein_256_Final(&state->u.ctx_256,hashval); + case 0: return Skein1024_Final(&state->u.ctx1024,hashval); + default: return SKEIN_FAIL; + } +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* all-in-one hash function */ +HashReturn skein_hash(int hashbitlen, const BitSequence *data, /* all-in-one call */ + DataLength databitlen,BitSequence *hashval) +{ + hashState state; + HashReturn r = Init(&state,hashbitlen); + if (r == SKEIN_SUCCESS) + { /* these calls do not fail when called properly */ + r = Update(&state,data,databitlen); + Final(&state,hashval); + } + return r; +} diff --git a/src/crypto/skein.h b/src/crypto/skein.h new file mode 100644 index 000000000..5c9cc5518 --- /dev/null +++ b/src/crypto/skein.h @@ -0,0 +1,47 @@ +#ifndef _SKEIN_H_ +#define _SKEIN_H_ 1 +/************************************************************************** +** +** Interface declarations and internal definitions for Skein hashing. +** +** Source code author: Doug Whiting, 2008. +** +** This algorithm and source code is released to the public domain. +** +*************************************************************************** +** +** The following compile-time switches may be defined to control some +** tradeoffs between speed, code size, error checking, and security. +** +** The "default" note explains what happens when the switch is not defined. +** +** SKEIN_DEBUG -- make callouts from inside Skein code +** to examine/display intermediate values. +** [default: no callouts (no overhead)] +** +** SKEIN_ERR_CHECK -- how error checking is handled inside Skein +** code. If not defined, most error checking +** is disabled (for performance). Otherwise, +** the switch value is interpreted as: +** 0: use assert() to flag errors +** 1: return SKEIN_FAIL to flag errors +** +***************************************************************************/ +#include "skein_port.h" /* get platform-specific definitions */ + +typedef enum +{ + SKEIN_SUCCESS = 0, /* return codes from Skein calls */ + SKEIN_FAIL = 1, + SKEIN_BAD_HASHLEN = 2 +} +HashReturn; + +typedef size_t DataLength; /* bit count type */ +typedef u08b_t BitSequence; /* bit stream type */ + +/* "all-in-one" call */ +HashReturn skein_hash(int hashbitlen, const BitSequence *data, + DataLength databitlen, BitSequence *hashval); + +#endif /* ifndef _SKEIN_H_ */ diff --git a/src/crypto/skein_port.h b/src/crypto/skein_port.h new file mode 100644 index 000000000..bd47065ad --- /dev/null +++ b/src/crypto/skein_port.h @@ -0,0 +1,190 @@ +#ifndef _SKEIN_PORT_H_ +#define _SKEIN_PORT_H_ + +#include <limits.h> +#include <stdint.h> + +#ifndef RETURN_VALUES +# define RETURN_VALUES +# if defined( DLL_EXPORT ) +# if defined( _MSC_VER ) || defined ( __INTEL_COMPILER ) +# define VOID_RETURN __declspec( dllexport ) void __stdcall +# define INT_RETURN __declspec( dllexport ) int __stdcall +# elif defined( __GNUC__ ) +# define VOID_RETURN __declspec( __dllexport__ ) void +# define INT_RETURN __declspec( __dllexport__ ) int +# else +# error Use of the DLL is only available on the Microsoft, Intel and GCC compilers +# endif +# elif defined( DLL_IMPORT ) +# if defined( _MSC_VER ) || defined ( __INTEL_COMPILER ) +# define VOID_RETURN __declspec( dllimport ) void __stdcall +# define INT_RETURN __declspec( dllimport ) int __stdcall +# elif defined( __GNUC__ ) +# define VOID_RETURN __declspec( __dllimport__ ) void +# define INT_RETURN __declspec( __dllimport__ ) int +# else +# error Use of the DLL is only available on the Microsoft, Intel and GCC compilers +# endif +# elif defined( __WATCOMC__ ) +# define VOID_RETURN void __cdecl +# define INT_RETURN int __cdecl +# else +# define VOID_RETURN void +# define INT_RETURN int +# endif +#endif + +/* These defines are used to declare buffers in a way that allows + faster operations on longer variables to be used. In all these + defines 'size' must be a power of 2 and >= 8 + + dec_unit_type(size,x) declares a variable 'x' of length + 'size' bits + + dec_bufr_type(size,bsize,x) declares a buffer 'x' of length 'bsize' + bytes defined as an array of variables + each of 'size' bits (bsize must be a + multiple of size / 8) + + ptr_cast(x,size) casts a pointer to a pointer to a + varaiable of length 'size' bits +*/ + +#define ui_type(size) uint##size##_t +#define dec_unit_type(size,x) typedef ui_type(size) x +#define dec_bufr_type(size,bsize,x) typedef ui_type(size) x[bsize / (size >> 3)] +#define ptr_cast(x,size) ((ui_type(size)*)(x)) + +typedef unsigned int uint_t; /* native unsigned integer */ +typedef uint8_t u08b_t; /* 8-bit unsigned integer */ +typedef uint64_t u64b_t; /* 64-bit unsigned integer */ + +#ifndef RotL_64 +#define RotL_64(x,N) (((x) << (N)) | ((x) >> (64-(N)))) +#endif + +/* + * Skein is "natively" little-endian (unlike SHA-xxx), for optimal + * performance on x86 CPUs. The Skein code requires the following + * definitions for dealing with endianness: + * + * SKEIN_NEED_SWAP: 0 for little-endian, 1 for big-endian + * Skein_Put64_LSB_First + * Skein_Get64_LSB_First + * Skein_Swap64 + * + * If SKEIN_NEED_SWAP is defined at compile time, it is used here + * along with the portable versions of Put64/Get64/Swap64, which + * are slow in general. + * + * Otherwise, an "auto-detect" of endianness is attempted below. + * If the default handling doesn't work well, the user may insert + * platform-specific code instead (e.g., for big-endian CPUs). + * + */ +#ifndef SKEIN_NEED_SWAP /* compile-time "override" for endianness? */ + + +#include "common/int-util.h" + +#define IS_BIG_ENDIAN 4321 /* byte 0 is most significant (mc68k) */ +#define IS_LITTLE_ENDIAN 1234 /* byte 0 is least significant (i386) */ + +#if BYTE_ORDER == LITTLE_ENDIAN +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif + +#if BYTE_ORDER == BIG_ENDIAN +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#endif + +/* special handler for IA64, which may be either endianness (?) */ +/* here we assume little-endian, but this may need to be changed */ +#if defined(__ia64) || defined(__ia64__) || defined(_M_IA64) +# define PLATFORM_MUST_ALIGN (1) +#ifndef PLATFORM_BYTE_ORDER +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif +#endif + +#ifndef PLATFORM_MUST_ALIGN +# define PLATFORM_MUST_ALIGN (0) +#endif + + +#if PLATFORM_BYTE_ORDER == IS_BIG_ENDIAN + /* here for big-endian CPUs */ +#define SKEIN_NEED_SWAP (1) +#elif PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN + /* here for x86 and x86-64 CPUs (and other detected little-endian CPUs) */ +#define SKEIN_NEED_SWAP (0) +#if PLATFORM_MUST_ALIGN == 0 /* ok to use "fast" versions? */ +#define Skein_Put64_LSB_First(dst08,src64,bCnt) memcpy(dst08,src64,bCnt) +#define Skein_Get64_LSB_First(dst64,src08,wCnt) memcpy(dst64,src08,8*(wCnt)) +#endif +#else +#error "Skein needs endianness setting!" +#endif + +#endif /* ifndef SKEIN_NEED_SWAP */ + +/* + ****************************************************************** + * Provide any definitions still needed. + ****************************************************************** + */ +#ifndef Skein_Swap64 /* swap for big-endian, nop for little-endian */ +#if SKEIN_NEED_SWAP +#define Skein_Swap64(w64) \ + ( (( ((u64b_t)(w64)) & 0xFF) << 56) | \ + (((((u64b_t)(w64)) >> 8) & 0xFF) << 48) | \ + (((((u64b_t)(w64)) >>16) & 0xFF) << 40) | \ + (((((u64b_t)(w64)) >>24) & 0xFF) << 32) | \ + (((((u64b_t)(w64)) >>32) & 0xFF) << 24) | \ + (((((u64b_t)(w64)) >>40) & 0xFF) << 16) | \ + (((((u64b_t)(w64)) >>48) & 0xFF) << 8) | \ + (((((u64b_t)(w64)) >>56) & 0xFF) ) ) +#else +#define Skein_Swap64(w64) (w64) +#endif +#endif /* ifndef Skein_Swap64 */ + + +#ifndef Skein_Put64_LSB_First +void Skein_Put64_LSB_First(u08b_t *dst,const u64b_t *src,size_t bCnt) +#ifdef SKEIN_PORT_CODE /* instantiate the function code here? */ + { /* this version is fully portable (big-endian or little-endian), but slow */ + size_t n; + + for (n=0;n<bCnt;n++) + dst[n] = (u08b_t) (src[n>>3] >> (8*(n&7))); + } +#else + ; /* output only the function prototype */ +#endif +#endif /* ifndef Skein_Put64_LSB_First */ + + +#ifndef Skein_Get64_LSB_First +void Skein_Get64_LSB_First(u64b_t *dst,const u08b_t *src,size_t wCnt) +#ifdef SKEIN_PORT_CODE /* instantiate the function code here? */ + { /* this version is fully portable (big-endian or little-endian), but slow */ + size_t n; + + for (n=0;n<8*wCnt;n+=8) + dst[n/8] = (((u64b_t) src[n ]) ) + + (((u64b_t) src[n+1]) << 8) + + (((u64b_t) src[n+2]) << 16) + + (((u64b_t) src[n+3]) << 24) + + (((u64b_t) src[n+4]) << 32) + + (((u64b_t) src[n+5]) << 40) + + (((u64b_t) src[n+6]) << 48) + + (((u64b_t) src[n+7]) << 56) ; + } +#else + ; /* output only the function prototype */ +#endif +#endif /* ifndef Skein_Get64_LSB_First */ + +#endif /* ifndef _SKEIN_PORT_H_ */ diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c new file mode 100644 index 000000000..c7264bd96 --- /dev/null +++ b/src/crypto/slow-hash.c @@ -0,0 +1,153 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <assert.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#include "common/int-util.h" +#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 +}; + +#define MEMORY (1 << 21) /* 2 MiB */ +#define ITER (1 << 20) +#define AES_BLOCK_SIZE 16 +#define AES_KEY_SIZE 32 /*16*/ +#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); } + +static void mul(const uint8_t* a, const uint8_t* b, uint8_t* res) { + uint64_t a0, b0; + uint64_t hi, lo; + + 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); +} + +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); +} + +static void copy_block(uint8_t* dst, const uint8_t* src) { + memcpy(dst, src, AES_BLOCK_SIZE); +} + +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 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]; + } +} + +#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) + +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); + } + 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]); + } + } + 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); +} diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c new file mode 100644 index 000000000..a2b0eeaa5 --- /dev/null +++ b/src/crypto/tree-hash.c @@ -0,0 +1,40 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <alloca.h> +#include <assert.h> +#include <stddef.h> +#include <string.h> + +#include "hash-ops.h" + +void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash) { + assert(count > 0); + if (count == 1) { + memcpy(root_hash, hashes, HASH_SIZE); + } else if (count == 2) { + cn_fast_hash(hashes, 2 * HASH_SIZE, root_hash); + } else { + size_t i, j; + size_t cnt = count - 1; + char (*ints)[HASH_SIZE]; + for (i = 1; i < sizeof(size_t); i <<= 1) { + cnt |= cnt >> i; + } + cnt &= ~(cnt >> 1); + ints = alloca(cnt * HASH_SIZE); + memcpy(ints, hashes, (2 * cnt - count) * HASH_SIZE); + for (i = 2 * cnt - count, j = 2 * cnt - count; j < cnt; i += 2, ++j) { + cn_fast_hash(hashes[i], 64, ints[j]); + } + assert(i == count); + while (cnt > 2) { + cnt >>= 1; + for (i = 0, j = 0; j < cnt; i += 2, ++j) { + cn_fast_hash(ints[i], 64, ints[j]); + } + } + cn_fast_hash(ints[0], 64, root_hash); + } +} diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h new file mode 100644 index 000000000..55efb1508 --- /dev/null +++ b/src/cryptonote_config.h @@ -0,0 +1,82 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#define CRYPTONOTE_MAX_BLOCK_NUMBER 500000000 +#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 6 // addresses start with "2" +#define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW 10 +#define CURRENT_TRANSACTION_VERSION 1 +#define CURRENT_BLOCK_MAJOR_VERSION 1 +#define CURRENT_BLOCK_MINOR_VERSION 0 +#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2 + +#define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW 60 + +// MONEY_SUPPLY - total number coins to be generated +#define MONEY_SUPPLY ((uint64_t)(-1)) +// COIN - number of smallest units in one coin +#define COIN ((uint64_t)1000000000000) // pow(10, 12) +#define DEFAULT_FEE ((uint64_t)1000000) // pow(10, 6) + +#define CRYPTONOTE_REWARD_BLOCKS_WINDOW 100 +#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE 10000 //size of block (bytes) after which reward for block calculated using block size +#define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE 600 +#define CRYPTONOTE_DISPLAY_DECIMAL_POINT 8 + + +#define ORPHANED_BLOCKS_MAX_COUNT 100 + + +#define DIFFICULTY_TARGET 120 // seconds +#define DIFFICULTY_WINDOW 720 // blocks +#define DIFFICULTY_LAG 15 // !!! +#define DIFFICULTY_CUT 60 // timestamps to cut after sorting +#define DIFFICULTY_BLOCKS_COUNT DIFFICULTY_WINDOW + DIFFICULTY_LAG + + +#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS DIFFICULTY_TARGET * CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS +#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS 1 + + +#define DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN DIFFICULTY_TARGET //just alias + + +#define BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT 10000 //by default, blocks ids count in synchronizing +#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 P2P_DEFAULT_PORT 8080 +#define RPC_DEFAULT_PORT 8081 +#define COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT 1000 + +#define P2P_LOCAL_WHITE_PEERLIST_LIMIT 1000 +#define P2P_LOCAL_GRAY_PEERLIST_LIMIT 5000 + +#define P2P_DEFAULT_CONNECTIONS_COUNT 8 +#define P2P_DEFAULT_HANDSHAKE_INTERVAL 60 //secondes +#define P2P_DEFAULT_PACKET_MAX_SIZE 50000000 //50000000 bytes maximum packet size +#define P2P_DEFAULT_PEERS_IN_HANDSHAKE 250 +#define P2P_DEFAULT_CONNECTION_TIMEOUT 5000 //5 seconds +#define P2P_DEFAULT_PING_CONNECTION_TIMEOUT 2000 //2 seconds +#define P2P_DEFAULT_INVOKE_TIMEOUT 20*1000 //2 minutes +#define P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT 5000 //5 seconds +#define P2P_STAT_TRUSTED_PUB_KEY "8f80f9a5a434a9f1510d13336228debfee9c918ce505efe225d8c94d045fa115" +#define P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT 70 + +#define ALLOW_DEBUG_COMMANDS + +#define CRYPTONOTE_NAME "bytecoin" +#define CRYPTONOTE_POOLDATA_FILENAME "poolstate.bin" +#define CRYPTONOTE_BLOCKCHAINDATA_FILENAME "blockchain.bin" +#define CRYPTONOTE_BLOCKCHAINDATA_TEMP_FILENAME "blockchain.bin.tmp" +#define P2P_NET_DATA_FILENAME "p2pstate.bin" +#define MINER_CONFIG_FILE_NAME "miner_conf.json" + + + diff --git a/src/cryptonote_core/account.cpp b/src/cryptonote_core/account.cpp new file mode 100644 index 000000000..ba39b9b77 --- /dev/null +++ b/src/cryptonote_core/account.cpp @@ -0,0 +1,50 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <boost/archive/binary_oarchive.hpp> +#include <boost/archive/binary_iarchive.hpp> +#include <fstream> + +#include "include_base_utils.h" +#include "account.h" +#include "warnings.h" +#include "crypto/crypto.h" +#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_core/cryptonote_format_utils.h" +using namespace std; + +DISABLE_VS_WARNINGS(4244 4345) + + namespace cryptonote +{ + //----------------------------------------------------------------- + account_base::account_base() + { + set_null(); + } + //----------------------------------------------------------------- + void account_base::set_null() + { + m_keys = account_keys(); + } + //----------------------------------------------------------------- + void account_base::generate() + { + 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); + m_creation_timestamp = time(NULL); + } + //----------------------------------------------------------------- + const account_keys& account_base::get_keys() const + { + return m_keys; + } + //----------------------------------------------------------------- + std::string account_base::get_public_address_str() + { + //TODO: change this code into base 58 + return get_account_address_as_str(m_keys.m_account_address); + } + //----------------------------------------------------------------- +} diff --git a/src/cryptonote_core/account.h b/src/cryptonote_core/account.h new file mode 100644 index 000000000..8b525da97 --- /dev/null +++ b/src/cryptonote_core/account.h @@ -0,0 +1,61 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include "cryptonote_core/cryptonote_basic.h" +#include "crypto/crypto.h" +#include "serialization/keyvalue_serialization.h" + +namespace cryptonote +{ + + struct account_keys + { + account_public_address m_account_address; + crypto::secret_key m_spend_secret_key; + crypto::secret_key m_view_secret_key; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(m_account_address) + KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_spend_secret_key) + KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_view_secret_key) + END_KV_SERIALIZE_MAP() + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + class account_base + { + public: + account_base(); + void generate(); + const account_keys& get_keys() const; + std::string get_public_address_str(); + + uint64_t get_createtime() const { return m_creation_timestamp; } + void set_createtime(uint64_t val) { m_creation_timestamp = val; } + + bool load(const std::string& file_path); + bool store(const std::string& file_path); + + template <class t_archive> + inline void serialize(t_archive &a, const unsigned int /*ver*/) + { + a & m_keys; + a & m_creation_timestamp; + } + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(m_keys) + KV_SERIALIZE(m_creation_timestamp) + END_KV_SERIALIZE_MAP() + + private: + void set_null(); + account_keys m_keys; + uint64_t m_creation_timestamp; + }; +} diff --git a/src/cryptonote_core/account_boost_serialization.h b/src/cryptonote_core/account_boost_serialization.h new file mode 100644 index 000000000..9cc36d14a --- /dev/null +++ b/src/cryptonote_core/account_boost_serialization.h @@ -0,0 +1,31 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include "account.h" +#include "cryptonote_core/cryptonote_boost_serialization.h" + +//namespace cryptonote { +namespace boost +{ + namespace serialization + { + template <class Archive> + inline void serialize(Archive &a, cryptonote::account_keys &x, const boost::serialization::version_type ver) + { + a & x.m_account_address; + a & x.m_spend_secret_key; + a & x.m_view_secret_key; + } + + template <class Archive> + inline void serialize(Archive &a, cryptonote::account_public_address &x, const boost::serialization::version_type ver) + { + a & x.m_spend_public_key; + a & x.m_view_public_key; + } + + } +} diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp new file mode 100644 index 000000000..1ec186652 --- /dev/null +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -0,0 +1,1633 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <algorithm> +#include <cstdio> +#include <boost/archive/binary_oarchive.hpp> +#include <boost/archive/binary_iarchive.hpp> + +#include "include_base_utils.h" +#include "cryptonote_basic_impl.h" +#include "blockchain_storage.h" +#include "cryptonote_format_utils.h" +#include "cryptonote_boost_serialization.h" +#include "blockchain_storage_boost_serialization.h" +#include "cryptonote_config.h" +#include "miner.h" +#include "misc_language.h" +#include "profile_tools.h" +#include "file_io_utils.h" +#include "common/boost_serialization_helper.h" +#include "warnings.h" +#include "crypto/hash.h" +//#include "serialization/json_archive.h" + +using namespace std; +using namespace epee; +using namespace cryptonote; + +DISABLE_VS_WARNINGS(4267) + +//------------------------------------------------------------------ +bool blockchain_storage::have_tx(const crypto::hash &id) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + return m_transactions.find(id) != m_transactions.end(); +} +//------------------------------------------------------------------ +bool blockchain_storage::have_tx_keyimg_as_spent(const crypto::key_image &key_im) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + return m_spent_keys.find(key_im) != m_spent_keys.end(); +} +//------------------------------------------------------------------ +transaction *blockchain_storage::get_tx(const crypto::hash &id) +{ + auto it = m_transactions.find(id); + if (it == m_transactions.end()) + return NULL; + + return &it->second.tx; +} +//------------------------------------------------------------------ +uint64_t blockchain_storage::get_current_blockchain_height() +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + return m_blocks.size(); +} +//------------------------------------------------------------------ +bool blockchain_storage::init(const std::string& config_folder) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + m_config_folder = config_folder; + LOG_PRINT_L0("Loading blockchain..."); + const std::string filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_FILENAME; + if(!tools::unserialize_obj_from_file(*this, filename)) + { + const std::string temp_filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_TEMP_FILENAME; + if(tools::unserialize_obj_from_file(*this, temp_filename)) + { + LOG_PRINT_L0("Blockchain storage loaded from temporary file"); + std::error_code ec = tools::replace_file(temp_filename, filename); + if (ec) + { + LOG_ERROR("Failed to rename blockchain data file " << temp_filename << " to " << filename << ": " << ec.message() << ':' << ec.value()); + return false; + } + } + else + { + LOG_PRINT_L0("Blockchain storage file not found, generating genesis block."); + block bl = boost::value_initialized<block>(); + block_verification_context bvc = boost::value_initialized<block_verification_context>(); + generate_genesis_block(bl); + add_new_block(bl, bvc); + CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed && bvc.m_added_to_main_chain, false, "Failed to add genesis block to blockchain"); + } + } + if(!m_blocks.size()) + { + LOG_PRINT_L0("Blockchain not loaded, generating genesis block."); + block bl = boost::value_initialized<block>(); + block_verification_context bvc = boost::value_initialized<block_verification_context>(); + generate_genesis_block(bl); + add_new_block(bl, bvc); + CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed, false, "Failed to add genesis block to blockchain"); + } + uint64_t timestamp_diff = time(NULL) - m_blocks.back().bl.timestamp; + if(!m_blocks.back().bl.timestamp) + timestamp_diff = time(NULL) - 1341378000; + LOG_PRINT_GREEN("Blockchain initialized. last block: " << m_blocks.size()-1 << ", " << misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block(), LOG_LEVEL_0); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::store_blockchain() +{ + LOG_PRINT_L0("Storing blockchain..."); + if (!tools::create_directories_if_necessary(m_config_folder)) + { + LOG_PRINT_L0("Failed to create data directory: " << m_config_folder); + return false; + } + + const std::string temp_filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_TEMP_FILENAME; + // There is a chance that temp_filename and filename are hardlinks to the same file + std::remove(temp_filename.c_str()); + if(!tools::serialize_obj_to_file(*this, temp_filename)) + { + //achtung! + LOG_ERROR("Failed to save blockchain data to file: " << temp_filename); + return false; + } + const std::string filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_FILENAME; + std::error_code ec = tools::replace_file(temp_filename, filename); + if (ec) + { + LOG_ERROR("Failed to rename blockchain data file " << temp_filename << " to " << filename << ": " << ec.message() << ':' << ec.value()); + return false; + } + LOG_PRINT_L0("Blockchain stored OK."); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::deinit() +{ + return store_blockchain(); +} +//------------------------------------------------------------------ +bool blockchain_storage::pop_block_from_blockchain() +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + CHECK_AND_ASSERT_MES(m_blocks.size() > 1, false, "pop_block_from_blockchain: can't pop from blockchain with size = " << m_blocks.size()); + size_t h = m_blocks.size()-1; + block_extended_info& bei = m_blocks[h]; + //crypto::hash id = get_block_hash(bei.bl); + bool r = purge_block_data_from_blockchain(bei.bl, bei.bl.tx_hashes.size()); + CHECK_AND_ASSERT_MES(r, false, "Failed to purge_block_data_from_blockchain for block " << get_block_hash(bei.bl) << " on height " << h); + + //remove from index + auto bl_ind = m_blocks_index.find(get_block_hash(bei.bl)); + CHECK_AND_ASSERT_MES(bl_ind != m_blocks_index.end(), false, "pop_block_from_blockchain: blockchain id not found in index"); + m_blocks_index.erase(bl_ind); + //pop block from core + m_blocks.pop_back(); + m_tx_pool.on_blockchain_dec(m_blocks.size()-1, get_tail_id()); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::reset_and_set_genesis_block(const block& b) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + m_transactions.clear(); + m_spent_keys.clear(); + m_blocks.clear(); + m_blocks_index.clear(); + m_alternative_chains.clear(); + m_outputs.clear(); + + block_verification_context bvc = boost::value_initialized<block_verification_context>(); + add_new_block(b, bvc); + return bvc.m_added_to_main_chain && !bvc.m_verifivation_failed; +} +//------------------------------------------------------------------ +bool blockchain_storage::purge_transaction_keyimages_from_blockchain(const transaction& tx, bool strict_check) +{ + struct purge_transaction_visitor: public boost::static_visitor<bool> + { + key_images_container& m_spent_keys; + bool m_strict_check; + purge_transaction_visitor(key_images_container& spent_keys, bool strict_check):m_spent_keys(spent_keys), m_strict_check(strict_check){} + + bool operator()(const txin_to_key& inp) const + { + //const crypto::key_image& ki = inp.k_image; + auto r = m_spent_keys.find(inp.k_image); + if(r != m_spent_keys.end()) + { + m_spent_keys.erase(r); + }else + { + CHECK_AND_ASSERT_MES(!m_strict_check, false, "purge_block_data_from_blockchain: key image in transaction not found"); + } + return true; + } + bool operator()(const txin_gen& inp) const + { + return true; + } + bool operator()(const txin_to_script& tx) const + { + return false; + } + + bool operator()(const txin_to_scripthash& tx) const + { + return false; + } + }; + + BOOST_FOREACH(const txin_v& in, tx.vin) + { + bool r = boost::apply_visitor(purge_transaction_visitor(m_spent_keys, strict_check), in); + CHECK_AND_ASSERT_MES(!strict_check || r, false, "failed to process purge_transaction_visitor"); + } + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::purge_transaction_from_blockchain(const crypto::hash& tx_id) +{ + auto tx_index_it = m_transactions.find(tx_id); + CHECK_AND_ASSERT_MES(tx_index_it != m_transactions.end(), false, "purge_block_data_from_blockchain: transaction not found in blockchain index!!"); + transaction& tx = tx_index_it->second.tx; + + purge_transaction_keyimages_from_blockchain(tx, true); + + if(!is_coinbase(tx)) + { + cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); + bool r = m_tx_pool.add_tx(tx, tvc, true); + CHECK_AND_ASSERT_MES(r, false, "purge_block_data_from_blockchain: failed to add transaction to transaction pool"); + } + + bool res = pop_transaction_from_global_index(tx, tx_id); + m_transactions.erase(tx_index_it); + LOG_PRINT_L1("Removed transaction from blockchain history:" << tx_id << ENDL); + return res; +} +//------------------------------------------------------------------ +bool blockchain_storage::purge_block_data_from_blockchain(const block& bl, size_t processed_tx_count) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + bool res = true; + CHECK_AND_ASSERT_MES(processed_tx_count <= bl.tx_hashes.size(), false, "wrong processed_tx_count in purge_block_data_from_blockchain"); + for(size_t count = 0; count != processed_tx_count; count++) + { + res = purge_transaction_from_blockchain(bl.tx_hashes[(processed_tx_count -1)- count]) && res; + } + + res = purge_transaction_from_blockchain(get_transaction_hash(bl.miner_tx)) && res; + + return res; +} +//------------------------------------------------------------------ +crypto::hash blockchain_storage::get_tail_id(uint64_t& height) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + height = get_current_blockchain_height()-1; + return get_tail_id(); +} +//------------------------------------------------------------------ +crypto::hash blockchain_storage::get_tail_id() +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + crypto::hash id = null_hash; + if(m_blocks.size()) + { + get_block_hash(m_blocks.back().bl, id); + } + return id; +} +//------------------------------------------------------------------ +bool blockchain_storage::get_short_chain_history(std::list<crypto::hash>& ids) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + size_t i = 0; + size_t current_multiplier = 1; + size_t sz = m_blocks.size(); + if(!sz) + return true; + size_t current_back_offset = 1; + bool genesis_included = false; + while(current_back_offset < sz) + { + ids.push_back(get_block_hash(m_blocks[sz-current_back_offset].bl)); + if(sz-current_back_offset == 0) + genesis_included = true; + if(i < 10) + { + ++current_back_offset; + }else + { + current_back_offset += current_multiplier *= 2; + } + ++i; + } + if(!genesis_included) + ids.push_back(get_block_hash(m_blocks[0].bl)); + + return true; +} +//------------------------------------------------------------------ +crypto::hash blockchain_storage::get_block_id_by_height(uint64_t height) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if(height >= m_blocks.size()) + return null_hash; + + return get_block_hash(m_blocks[height].bl); +} +//------------------------------------------------------------------ +bool blockchain_storage::get_block_by_hash(const crypto::hash &h, block &blk) { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + // try to find block in main chain + blocks_by_id_index::const_iterator it = m_blocks_index.find(h); + if (m_blocks_index.end() != it) { + blk = m_blocks[it->second].bl; + return true; + } + + // try to find block in alternative chain + blocks_ext_by_hash::const_iterator it_alt = m_alternative_chains.find(h); + if (m_alternative_chains.end() != it_alt) { + blk = it_alt->second.bl; + return true; + } + + return false; +} +//------------------------------------------------------------------ +void blockchain_storage::get_all_known_block_ids(std::list<crypto::hash> &main, std::list<crypto::hash> &alt, std::list<crypto::hash> &invalid) { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + BOOST_FOREACH(blocks_by_id_index::value_type &v, m_blocks_index) + main.push_back(v.first); + + BOOST_FOREACH(blocks_ext_by_hash::value_type &v, m_alternative_chains) + alt.push_back(v.first); + + BOOST_FOREACH(blocks_ext_by_hash::value_type &v, m_invalid_blocks) + invalid.push_back(v.first); +} +//------------------------------------------------------------------ +difficulty_type blockchain_storage::get_difficulty_for_next_block() +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::vector<uint64_t> timestamps; + std::vector<difficulty_type> commulative_difficulties; + size_t offset = m_blocks.size() - std::min(m_blocks.size(), static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT)); + if(!offset) + ++offset;//skip genesis block + for(; offset < m_blocks.size(); offset++) + { + timestamps.push_back(m_blocks[offset].bl.timestamp); + commulative_difficulties.push_back(m_blocks[offset].cumulative_difficulty); + } + return next_difficulty(timestamps, commulative_difficulties); +} +//------------------------------------------------------------------ +bool blockchain_storage::rollback_blockchain_switching(std::list<block>& original_chain, size_t rollback_height) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + //remove failed subchain + for(size_t i = m_blocks.size()-1; i >=rollback_height; i--) + { + bool r = pop_block_from_blockchain(); + CHECK_AND_ASSERT_MES(r, false, "PANIC!!! failed to remove block while chain switching during the rollback!"); + } + //return back original chain + BOOST_FOREACH(auto& bl, original_chain) + { + block_verification_context bvc = boost::value_initialized<block_verification_context>(); + bool r = handle_block_to_main_chain(bl, bvc); + CHECK_AND_ASSERT_MES(r && bvc.m_added_to_main_chain, false, "PANIC!!! failed to add (again) block while chain switching during the rollback!"); + } + + LOG_PRINT_L0("Rollback success."); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_chain) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + CHECK_AND_ASSERT_MES(alt_chain.size(), false, "switch_to_alternative_blockchain: empty chain passed"); + + size_t split_height = alt_chain.front()->second.height; + CHECK_AND_ASSERT_MES(m_blocks.size() > split_height, false, "switch_to_alternative_blockchain: blockchain size is lower than split height"); + + //disconnecting old chain + std::list<block> disconnected_chain; + for(size_t i = m_blocks.size()-1; i >=split_height; i--) + { + block b = m_blocks[i].bl; + bool r = pop_block_from_blockchain(); + CHECK_AND_ASSERT_MES(r, false, "failed to remove block on chain switching"); + disconnected_chain.push_front(b); + } + + //connecting new alternative chain + for(auto alt_ch_iter = alt_chain.begin(); alt_ch_iter != alt_chain.end(); alt_ch_iter++) + { + auto ch_ent = *alt_ch_iter; + block_verification_context bvc = boost::value_initialized<block_verification_context>(); + bool r = handle_block_to_main_chain(ch_ent->second.bl, bvc); + if(!r || !bvc.m_added_to_main_chain) + { + LOG_PRINT_L0("Failed to switch to alternative blockchain"); + rollback_blockchain_switching(disconnected_chain, split_height); + add_block_as_invalid(ch_ent->second, get_block_hash(ch_ent->second.bl)); + LOG_PRINT_L0("The block was inserted as invalid while connecting new alternative chain, block_id: " << get_block_hash(ch_ent->second.bl)); + m_alternative_chains.erase(ch_ent); + + for(auto alt_ch_to_orph_iter = ++alt_ch_iter; alt_ch_to_orph_iter != alt_chain.end(); alt_ch_to_orph_iter++) + { + //block_verification_context bvc = boost::value_initialized<block_verification_context>(); + add_block_as_invalid((*alt_ch_iter)->second, (*alt_ch_iter)->first); + m_alternative_chains.erase(*alt_ch_to_orph_iter); + } + return false; + } + } + + //pushing old chain as alternative chain + BOOST_FOREACH(auto& old_ch_ent, 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) + { + LOG_ERROR("Failed to push ex-main chain blocks to alternative chain "); + rollback_blockchain_switching(disconnected_chain, split_height); + return false; + } + } + + //removing all_chain entries from alternative chain + BOOST_FOREACH(auto ch_ent, alt_chain) + { + m_alternative_chains.erase(ch_ent); + } + + LOG_PRINT_GREEN("REORGANIZE SUCCESS! on height: " << split_height << ", new blockchain size: " << m_blocks.size(), LOG_LEVEL_0); + return true; +} +//------------------------------------------------------------------ +difficulty_type blockchain_storage::get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::iterator>& alt_chain, block_extended_info& bei) +{ + std::vector<uint64_t> timestamps; + std::vector<difficulty_type> commulative_difficulties; + if(alt_chain.size()< DIFFICULTY_BLOCKS_COUNT) + { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front()->second.height : bei.height; + size_t main_chain_count = DIFFICULTY_BLOCKS_COUNT - std::min(static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT), alt_chain.size()); + main_chain_count = std::min(main_chain_count, main_chain_stop_offset); + size_t main_chain_start_offset = main_chain_stop_offset - main_chain_count; + + if(!main_chain_start_offset) + ++main_chain_start_offset; //skip genesis block + for(; main_chain_start_offset < main_chain_stop_offset; ++main_chain_start_offset) + { + timestamps.push_back(m_blocks[main_chain_start_offset].bl.timestamp); + commulative_difficulties.push_back(m_blocks[main_chain_start_offset].cumulative_difficulty); + } + + CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= DIFFICULTY_BLOCKS_COUNT, false, "Internal error, alt_chain.size()["<< alt_chain.size() + << "] + vtimestampsec.size()[" << timestamps.size() << "] NOT <= DIFFICULTY_WINDOW[]" << DIFFICULTY_BLOCKS_COUNT ); + BOOST_FOREACH(auto it, alt_chain) + { + timestamps.push_back(it->second.bl.timestamp); + commulative_difficulties.push_back(it->second.cumulative_difficulty); + } + }else + { + timestamps.resize(std::min(alt_chain.size(), static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT))); + commulative_difficulties.resize(std::min(alt_chain.size(), static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT))); + size_t count = 0; + size_t max_i = timestamps.size()-1; + BOOST_REVERSE_FOREACH(auto it, alt_chain) + { + timestamps[max_i - count] = it->second.bl.timestamp; + commulative_difficulties[max_i - count] = it->second.cumulative_difficulty; + count++; + if(count >= DIFFICULTY_BLOCKS_COUNT) + break; + } + } + return next_difficulty(timestamps, commulative_difficulties); +} +//------------------------------------------------------------------ +bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t height) +{ + CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 1, false, "coinbase transaction in the block has no inputs"); + CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(txin_gen), false, "coinbase transaction in the block has the wrong type"); + if(boost::get<txin_gen>(b.miner_tx.vin[0]).height != height) + { + LOG_PRINT_RED_L0("The miner transaction in block has invalid height: " << boost::get<txin_gen>(b.miner_tx.vin[0]).height << ", expected: " << height); + return false; + } + CHECK_AND_ASSERT_MES(b.miner_tx.unlock_time == height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, + false, + "coinbase transaction transaction have wrong unlock time=" << b.miner_tx.unlock_time << ", expected " << height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW); + + //check outs overflow + if(!check_outs_overflow(b.miner_tx)) + { + LOG_PRINT_RED_L0("miner transaction have money overflow in block " << get_block_hash(b)); + return false; + } + + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins) +{ + //validate reward + uint64_t money_in_use = 0; + BOOST_FOREACH(auto& o, b.miner_tx.vout) + money_in_use += o.amount; + + std::vector<size_t> last_blocks_sizes; + get_last_n_blocks_sizes(last_blocks_sizes, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + bool block_too_big = false; + base_reward = get_block_reward(last_blocks_sizes, cumulative_block_size, block_too_big, already_generated_coins); + if(block_too_big) + { + LOG_PRINT_L0("block size " << cumulative_block_size << " is bigger than allowed for this blockchain"); + return false; + } + if(base_reward + fee < money_in_use) + { + LOG_ERROR("coinbase transaction spend too much money (" << print_money(money_in_use) << "). Block reward is " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")"); + return false; + } + if(base_reward + fee != money_in_use) + { + LOG_ERROR("coinbase transaction doesn't use full amount of block reward: spent: " + << print_money(money_in_use) << ", block reward " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")"); + return false; + } + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::get_backward_blocks_sizes(size_t from_height, std::vector<size_t>& sz, size_t count) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + CHECK_AND_ASSERT_MES(from_height < m_blocks.size(), false, "Internal error: get_backward_blocks_sizes called with from_height=" << from_height << ", blockchain height = " << m_blocks.size()); + + size_t start_offset = (from_height+1) - std::min((from_height+1), count); + for(size_t i = start_offset; i != from_height+1; i++) + sz.push_back(m_blocks[i].block_cumulative_size); + + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if(!m_blocks.size()) + return true; + return get_backward_blocks_sizes(m_blocks.size() -1, sz, count); +} +//------------------------------------------------------------------ +uint64_t blockchain_storage::get_current_comulative_blocksize_limit() +{ + return m_current_block_comul_sz_limit; +} +//------------------------------------------------------------------ +bool blockchain_storage::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) +{ + size_t txs_cumulative_size = 0; + uint64_t fee = 0; + size_t comul_sz_limit = 0; + std::vector<size_t> sz; + + CRITICAL_REGION_BEGIN(m_blockchain_lock); + get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + b.major_version = CURRENT_BLOCK_MAJOR_VERSION; + b.minor_version = CURRENT_BLOCK_MINOR_VERSION; + b.prev_id = get_tail_id(); + b.timestamp = time(NULL); + height = m_blocks.size(); + diffic = get_difficulty_for_next_block(); + CHECK_AND_ASSERT_MES(diffic, false, "difficulty owverhead."); + + comul_sz_limit = m_current_block_comul_sz_limit - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + + CRITICAL_REGION_END(); + + m_tx_pool.fill_block_template(b, txs_cumulative_size, comul_sz_limit, fee); + + /* + two-phase miner transaction generation: we don't know exact block size until we prepare block, but we don't know reward until we know + block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size + */ + //make blocks coin-base tx looks close to real coinbase tx to get truthful blob size + bool r = construct_miner_tx(height, m_blocks.back().already_generated_coins, miner_address, b.miner_tx, fee, sz, txs_cumulative_size, ex_nonce, 11); + CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance"); +#ifdef _DEBUG + std::list<size_t> try_val; + try_val.push_back(get_object_blobsize(b.miner_tx)); +#endif + + size_t cumulative_size = txs_cumulative_size + get_object_blobsize(b.miner_tx); + size_t try_count = 0; + for(; try_count != 10; ++try_count) + { + r = construct_miner_tx(height, m_blocks.back().already_generated_coins, miner_address, b.miner_tx, fee, sz, cumulative_size, ex_nonce, 11); +#ifdef _DEBUG + try_val.push_back(get_object_blobsize(b.miner_tx)); +#endif + + CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, second chance"); + size_t coinbase_blob_size = get_object_blobsize(b.miner_tx); + if(coinbase_blob_size > cumulative_size - txs_cumulative_size ) + { + cumulative_size = txs_cumulative_size + coinbase_blob_size; + continue; + }else + { + if(coinbase_blob_size < cumulative_size - txs_cumulative_size ) + { + size_t delta = cumulative_size - txs_cumulative_size - coinbase_blob_size; + b.miner_tx.extra.insert(b.miner_tx.extra.end(), delta, 0); + //here could be 1 byte difference, because of extra field counter is varint, and it can become from 1-byte len to 2-bytes len. + if(cumulative_size != txs_cumulative_size + get_object_blobsize(b.miner_tx)) + { + CHECK_AND_ASSERT_MES(cumulative_size + 1== txs_cumulative_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " + 1 is not equal txs_cumulative_size=" << txs_cumulative_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx) ); + b.miner_tx.extra.resize(b.miner_tx.extra.size() - 1 ); + if(cumulative_size != txs_cumulative_size + get_object_blobsize(b.miner_tx)) + {//fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size + LOG_PRINT_RED("Miner tx creation have no luck with delta_extra size = " << delta << " and " << delta - 1 , LOG_LEVEL_2); + cumulative_size += delta + 1; + continue; + } + LOG_PRINT_GREEN("Setting extra for block: " << b.miner_tx.extra.size() << ", try_count=" << try_count, LOG_LEVEL_1); + } + } + CHECK_AND_ASSERT_MES(cumulative_size == txs_cumulative_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " is not equal txs_cumulative_size=" << txs_cumulative_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx) ); + return true; + } + } + LOG_ERROR("Failed to create_block_template with " << try_count << " tries"); + return false; +} +//------------------------------------------------------------------ +bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, std::vector<uint64_t>& timestamps) +{ + if(timestamps.size() >= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW) + return true; + + size_t need_elements = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - timestamps.size(); + CHECK_AND_ASSERT_MES(start_top_height < m_blocks.size(), false, "internal error: passed start_height = " << start_top_height << " not less then m_blocks.size()=" << m_blocks.size()); + size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements:0; + do + { + timestamps.push_back(m_blocks[start_top_height].bl.timestamp); + if(start_top_height == 0) + break; + --start_top_height; + }while(start_top_height != stop_offset); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + //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); + auto it_prev = m_alternative_chains.find(b.prev_id); + if(it_prev != m_alternative_chains.end() || it_main_prev != m_blocks_index.end()) + { + //we have new block in alternative chain + + //build alternative subchain, front -> mainchain, back -> alternative head + blocks_ext_by_hash::iterator alt_it = it_prev; //m_alternative_chains.find() + std::list<blocks_ext_by_hash::iterator> alt_chain; + std::vector<uint64_t> timestamps; + while(alt_it != m_alternative_chains.end()) + { + alt_chain.push_front(alt_it); + timestamps.push_back(alt_it->second.bl.timestamp); + alt_it = m_alternative_chains.find(alt_it->second.bl.prev_id); + } + + if(alt_chain.size()) + { + //make sure that it has right connection to main chain + CHECK_AND_ASSERT_MES(m_blocks.size() > alt_chain.front()->second.height, false, "main blockchain wrong height"); + crypto::hash h = null_hash; + get_block_hash(m_blocks[alt_chain.front()->second.height - 1].bl, h); + CHECK_AND_ASSERT_MES(h == alt_chain.front()->second.bl.prev_id, false, "alternative chain have wrong connection to main chain"); + complete_timestamps_vector(alt_chain.front()->second.height - 1, timestamps); + }else + { + CHECK_AND_ASSERT_MES(it_main_prev != m_blocks_index.end(), false, "internal error: broken imperative condition it_main_prev != m_blocks_index.end()"); + complete_timestamps_vector(it_main_prev->second, timestamps); + } + //check timestamp correct + if(!check_block_timestamp(timestamps, b)) + { + LOG_PRINT_RED_L0("Block with id: " << id + << ENDL << " for alternative chain, have invalid timestamp: " << b.timestamp); + //add_block_as_invalid(b, id);//do not add blocks to invalid storage before proof of work check was passed + bvc.m_verifivation_failed = true; + return false; + } + + 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; + 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)) + { + 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; + } + } + + if(!prevalidate_miner_transaction(b, bei.height)) + { + LOG_PRINT_RED_L0("Block with id: " << string_tools::pod_to_hex(id) + << " (as alternative) have wrong miner transaction."); + bvc.m_verifivation_failed = true; + return false; + + } + + bei.cumulative_difficulty = alt_chain.size() ? it_prev->second.cumulative_difficulty: m_blocks[it_main_prev->second].cumulative_difficulty; + bei.cumulative_difficulty += current_diff; + +#ifdef _DEBUG + auto i_dres = m_alternative_chains.find(id); + CHECK_AND_ASSERT_MES(i_dres == m_alternative_chains.end(), false, "insertion of new alternative block returned as it already exist"); +#endif + 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) + { + //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); + if(r) bvc.m_added_to_main_chain = true; + else bvc.m_verifivation_failed = true; + return r; + } + 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 + bvc.m_marked_as_orphaned = true; + LOG_PRINT_RED_L0("Block recognized as orphaned and rejected, id = " << id); + } + + + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if(start_offset >= m_blocks.size()) + return false; + for(size_t i = start_offset; i < start_offset + count && i < m_blocks.size();i++) + { + blocks.push_back(m_blocks[i].bl); + std::list<crypto::hash> missed_ids; + get_transactions(m_blocks[i].bl.tx_hashes, txs, missed_ids); + CHECK_AND_ASSERT_MES(!missed_ids.size(), false, "have missed transactions in own block in main blockchain"); + } + + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if(start_offset >= m_blocks.size()) + return false; + + for(size_t i = start_offset; i < start_offset + count && i < m_blocks.size();i++) + blocks.push_back(m_blocks[i].bl); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + rsp.current_blockchain_height = get_current_blockchain_height(); + std::list<block> blocks; + get_blocks(arg.blocks, blocks, rsp.missed_ids); + + BOOST_FOREACH(const auto& bl, blocks) + { + std::list<crypto::hash> missed_tx_id; + std::list<transaction> txs; + get_transactions(bl.tx_hashes, txs, rsp.missed_ids); + CHECK_AND_ASSERT_MES(!missed_tx_id.size(), false, "Internal error: have missed missed_tx_id.size()=" << missed_tx_id.size() + << ENDL << "for block id = " << get_block_hash(bl)); + rsp.blocks.push_back(block_complete_entry()); + block_complete_entry& e = rsp.blocks.back(); + //pack block + e.block = t_serializable_object_to_blob(bl); + //pack transactions + BOOST_FOREACH(transaction& tx, txs) + e.txs.push_back(t_serializable_object_to_blob(tx)); + + } + //get another transactions, if need + std::list<transaction> txs; + get_transactions(arg.txs, txs, rsp.missed_ids); + //pack aside transactions + BOOST_FOREACH(const auto& tx, txs) + rsp.txs.push_back(t_serializable_object_to_blob(tx)); + + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::get_alternative_blocks(std::list<block>& blocks) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + BOOST_FOREACH(const auto& alt_bl, m_alternative_chains) + { + blocks.push_back(alt_bl.second.bl); + } + return true; +} +//------------------------------------------------------------------ +size_t blockchain_storage::get_alternative_blocks_count() +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + return m_alternative_chains.size(); +} +//------------------------------------------------------------------ +bool blockchain_storage::add_out_to_get_random_outs(std::vector<std::pair<crypto::hash, size_t> >& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + transactions_container::iterator tx_it = m_transactions.find(amount_outs[i].first); + CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "internal error: transaction with id " << amount_outs[i].first << ENDL << + ", used in mounts global index for amount=" << amount << ": i=" << i << "not found in transactions index"); + CHECK_AND_ASSERT_MES(tx_it->second.tx.vout.size() > amount_outs[i].second, false, "internal error: in global outs index, transaction out index=" + << amount_outs[i].second << " more than transaction outputs = " << tx_it->second.tx.vout.size() << ", for tx id = " << amount_outs[i].first); + transaction& tx = tx_it->second.tx; + CHECK_AND_ASSERT_MES(tx.vout[amount_outs[i].second].target.type() == typeid(txout_to_key), false, "unknown tx out type"); + + //check if transaction is unlocked + if(!is_tx_spendtime_unlocked(tx.unlock_time)) + return false; + + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oen = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry()); + oen.global_amount_index = i; + oen.out_key = boost::get<txout_to_key>(tx.vout[amount_outs[i].second].target).key; + return true; +} +//------------------------------------------------------------------ +size_t blockchain_storage::find_end_of_allowed_index(const std::vector<std::pair<crypto::hash, size_t> >& amount_outs) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if(!amount_outs.size()) + return 0; + size_t i = amount_outs.size(); + do + { + --i; + transactions_container::iterator it = m_transactions.find(amount_outs[i].first); + CHECK_AND_ASSERT_MES(it != m_transactions.end(), 0, "internal error: failed to find transaction from outputs index with tx_id=" << amount_outs[i].first); + if(it->second.m_keeper_block_height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW <= get_current_blockchain_height() ) + return i+1; + } while (i != 0); + return 0; +} +//------------------------------------------------------------------ +bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) +{ + srand(static_cast<unsigned int>(time(NULL))); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + BOOST_FOREACH(uint64_t amount, req.amounts) + { + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs = *res.outs.insert(res.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount()); + result_outs.amount = amount; + auto it = m_outputs.find(amount); + if(it == m_outputs.end()) + { + LOG_ERROR("COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: not outs for amount " << amount << ", wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist"); + continue;//actually this is strange situation, wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist + } + std::vector<std::pair<crypto::hash, size_t> >& amount_outs = it->second; + //it is not good idea to use top fresh outs, because it increases possibility of transaction canceling on split + //lets find upper bound of not fresh outs + size_t up_index_limit = find_end_of_allowed_index(amount_outs); + CHECK_AND_ASSERT_MES(up_index_limit <= amount_outs.size(), false, "internal error: find_end_of_allowed_index returned wrong index=" << up_index_limit << ", with amount_outs.size = " << amount_outs.size()); + if(amount_outs.size() > req.outs_count) + { + std::set<size_t> used; + size_t try_count = 0; + for(uint64_t j = 0; j != req.outs_count && try_count < up_index_limit;) + { + size_t i = rand()%up_index_limit; + if(used.count(i)) + continue; + bool added = add_out_to_get_random_outs(amount_outs, result_outs, amount, i); + used.insert(i); + if(added) + ++j; + ++try_count; + } + }else + { + for(size_t i = 0; i != up_index_limit; i++) + add_out_to_get_random_outs(amount_outs, result_outs, amount, i); + } + } + return true; +} +//------------------------------------------------------------------ +//bool blockchain_storage::get_outs_for_amounts(uint64_t amount, std::vector<std::pair<crypto::hash, size_t> >& keys, std::map<crypto::hash, transaction>& txs) +//{ +// auto it = m_outputs.find(amount); +// if(it == m_outputs.end()) +// return false; +// keys = it->second; +// typedef std::pair<crypto::hash, size_t> pair; +// BOOST_FOREACH(pair& pr, keys) +// { +// auto it = m_transactions.find(pr.first); +// CHECK_AND_ASSERT_MES(it != m_transactions.end(), false, "internal error: transaction with id " << pr.first << " not found in internal index, but have refference for amount " << amount); +// txs[pr.first] = it->second.tx; +// } +// return true; +//} +//------------------------------------------------------------------ +bool blockchain_storage::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, uint64_t& starter_offset) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + if(!qblock_ids.size() /*|| !req.m_total_height*/) + { + LOG_ERROR("Client sent wrong NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << qblock_ids.size() << /*", m_height=" << req.m_total_height <<*/ ", dropping connection"); + return false; + } + //check genesis match + if(qblock_ids.back() != get_block_hash(m_blocks[0].bl)) + { + LOG_ERROR("Client sent wrong NOTIFY_REQUEST_CHAIN: genesis block missmatch: " << ENDL << "id: " + << qblock_ids.back() << ", " << ENDL << "expected: " << get_block_hash(m_blocks[0].bl) + << "," << ENDL << " dropping connection"); + return false; + } + + /* Figure out what blocks we should request to get state_normal */ + size_t i = 0; + auto bl_it = qblock_ids.begin(); + auto block_index_it = m_blocks_index.find(*bl_it); + for(; bl_it != qblock_ids.end(); bl_it++, i++) + { + block_index_it = m_blocks_index.find(*bl_it); + if(block_index_it != m_blocks_index.end()) + break; + } + + if(bl_it == qblock_ids.end()) + { + LOG_ERROR("Internal error handling connection, can't find split point"); + return false; + } + + if(block_index_it == m_blocks_index.end()) + { + //this should NEVER happen, but, dose of paranoia in such cases is not too bad + LOG_ERROR("Internal error handling connection, can't find split point"); + return false; + } + + //we start to put block ids INCLUDING last known id, just to make other side be sure + starter_offset = block_index_it->second; + return true; +} +//------------------------------------------------------------------ +uint64_t blockchain_storage::block_difficulty(size_t i) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + CHECK_AND_ASSERT_MES(i < m_blocks.size(), false, "wrong block index i = " << i << " at blockchain_storage::block_difficulty()"); + if(i == 0) + return m_blocks[i].cumulative_difficulty; + + return m_blocks[i].cumulative_difficulty - m_blocks[i-1].cumulative_difficulty; +} +//------------------------------------------------------------------ +void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_index) +{ + std::stringstream ss; + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if(start_index >=m_blocks.size()) + { + LOG_PRINT_L0("Wrong starter index set: " << start_index << ", expected max index " << m_blocks.size()-1); + return; + } + + for(size_t i = start_index; i != m_blocks.size() && i != end_index; i++) + { + ss << "height " << i << ", timastamp " << m_blocks[i].bl.timestamp << ", cumul_dif " << m_blocks[i].cumulative_difficulty << ", cumul_size " << m_blocks[i].block_cumulative_size + << "\nid\t\t" << get_block_hash(m_blocks[i].bl) + << "\ndifficulty\t\t" << block_difficulty(i) << ", nonce " << m_blocks[i].bl.nonce << ", tx_count " << m_blocks[i].bl.tx_hashes.size() << ENDL; + } + LOG_PRINT_L0("Current blockchain:" << ENDL << ss.str()); +} +//------------------------------------------------------------------ +void blockchain_storage::print_blockchain_index() +{ + std::stringstream ss; + CRITICAL_REGION_LOCAL(m_blockchain_lock); + BOOST_FOREACH(const blocks_by_id_index::value_type& v, m_blocks_index) + ss << "id\t\t" << v.first << " height" << v.second << ENDL << ""; + + LOG_PRINT_L0("Current blockchain index:" << ENDL << ss.str()); +} +//------------------------------------------------------------------ +void blockchain_storage::print_blockchain_outs(const std::string& file) +{ + std::stringstream ss; + CRITICAL_REGION_LOCAL(m_blockchain_lock); + BOOST_FOREACH(const outputs_container::value_type& v, m_outputs) + { + const std::vector<std::pair<crypto::hash, size_t> >& vals = v.second; + if(vals.size()) + { + ss << "amount: " << v.first << ENDL; + for(size_t i = 0; i != vals.size(); i++) + ss << "\t" << vals[i].first << ": " << vals[i].second << ENDL; + } + } + if(file_io_utils::save_string_to_file(file, ss.str())) + { + LOG_PRINT_L0("Current outputs index writen to file: " << file); + }else + { + LOG_PRINT_L0("Failed to write current outputs index to file: " << file); + } +} +//------------------------------------------------------------------ +bool blockchain_storage::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if(!find_blockchain_supplement(qblock_ids, resp.start_height)) + return false; + + resp.total_height = get_current_blockchain_height(); + size_t count = 0; + for(size_t i = resp.start_height; i != m_blocks.size() && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++) + resp.m_block_ids.push_back(get_block_hash(m_blocks[i].bl)); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if(!find_blockchain_supplement(qblock_ids, start_height)) + return false; + + total_height = get_current_blockchain_height(); + size_t count = 0; + for(size_t i = start_height; i != m_blocks.size() && count < max_count; i++, count++) + { + blocks.resize(blocks.size()+1); + blocks.back().first = m_blocks[i].bl; + std::list<crypto::hash> mis; + get_transactions(m_blocks[i].bl.tx_hashes, blocks.back().second, mis); + CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found"); + } + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::add_block_as_invalid(const block& bl, const crypto::hash& h) +{ + block_extended_info bei = AUTO_VAL_INIT(bei); + bei.bl = bl; + return add_block_as_invalid(bei, h); +} +//------------------------------------------------------------------ +bool blockchain_storage::add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + auto i_res = m_invalid_blocks.insert(std::map<crypto::hash, block_extended_info>::value_type(h, bei)); + CHECK_AND_ASSERT_MES(i_res.second, false, "at insertion invalid by tx returned status existed"); + LOG_PRINT_L0("BLOCK ADDED AS INVALID: " << h << ENDL << ", prev_id=" << bei.bl.prev_id << ", m_invalid_blocks count=" << m_invalid_blocks.size()); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::have_block(const crypto::hash& id) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if(m_blocks_index.count(id)) + return true; + if(m_alternative_chains.count(id)) + return true; + /*if(m_orphaned_blocks.get<by_id>().count(id)) + return true;*/ + + /*if(m_orphaned_by_tx.count(id)) + return true;*/ + if(m_invalid_blocks.count(id)) + return true; + + return false; +} +//------------------------------------------------------------------ +bool blockchain_storage::handle_block_to_main_chain(const block& bl, block_verification_context& bvc) +{ + crypto::hash id = get_block_hash(bl); + return handle_block_to_main_chain(bl, id, bvc); +} +//------------------------------------------------------------------ +bool blockchain_storage::push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector<uint64_t>& global_indexes) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + size_t i = 0; + BOOST_FOREACH(const auto& ot, tx.vout) + { + outputs_container::mapped_type& amount_index = m_outputs[ot.amount]; + amount_index.push_back(std::pair<crypto::hash, size_t>(tx_id, i)); + global_indexes.push_back(amount_index.size()-1); + ++i; + } + return true; +} +//------------------------------------------------------------------ +size_t blockchain_storage::get_total_transactions() +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + return m_transactions.size(); +} +//------------------------------------------------------------------ +bool blockchain_storage::get_outs(uint64_t amount, std::list<crypto::public_key>& pkeys) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + auto it = m_outputs.find(amount); + if(it == m_outputs.end()) + return true; + + BOOST_FOREACH(const auto& out_entry, it->second) + { + auto tx_it = m_transactions.find(out_entry.first); + CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "transactions outs global index consistency broken: wrong tx id in index"); + CHECK_AND_ASSERT_MES(tx_it->second.tx.vout.size() > out_entry.second, false, "transactions outs global index consistency broken: index in tx_outx more then size"); + CHECK_AND_ASSERT_MES(tx_it->second.tx.vout[out_entry.second].target.type() == typeid(txout_to_key), false, "transactions outs global index consistency broken: index in tx_outx more then size"); + pkeys.push_back(boost::get<txout_to_key>(tx_it->second.tx.vout[out_entry.second].target).key); + } + + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + size_t i = tx.vout.size()-1; + BOOST_REVERSE_FOREACH(const auto& ot, tx.vout) + { + auto it = m_outputs.find(ot.amount); + CHECK_AND_ASSERT_MES(it != m_outputs.end(), false, "transactions outs global index consistency broken"); + CHECK_AND_ASSERT_MES(it->second.size(), false, "transactions outs global index: empty index for amount: " << ot.amount); + CHECK_AND_ASSERT_MES(it->second.back().first == tx_id , false, "transactions outs global index consistency broken: tx id missmatch"); + CHECK_AND_ASSERT_MES(it->second.back().second == i, false, "transactions outs global index consistency broken: in transaction index missmatch"); + it->second.pop_back(); + --i; + } + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + struct add_transaction_input_visitor: public boost::static_visitor<bool> + { + key_images_container& m_spent_keys; + const crypto::hash& m_tx_id; + const crypto::hash& m_bl_id; + add_transaction_input_visitor(key_images_container& spent_keys, const crypto::hash& tx_id, const crypto::hash& bl_id):m_spent_keys(spent_keys), m_tx_id(tx_id), m_bl_id(bl_id) + {} + bool operator()(const txin_to_key& in) const + { + const crypto::key_image& ki = in.k_image; + auto r = m_spent_keys.insert(ki); + if(!r.second) + { + //double spend detected + LOG_PRINT_L0("tx with id: " << m_tx_id << " in block id: " << m_bl_id << " have input marked as spent with key image: " << ki << ", block declined"); + return false; + } + return true; + } + + bool operator()(const txin_gen& tx) const{return true;} + bool operator()(const txin_to_script& tx) const{return false;} + bool operator()(const txin_to_scripthash& tx) const{return false;} + }; + + BOOST_FOREACH(const txin_v& in, tx.vin) + { + if(!boost::apply_visitor(add_transaction_input_visitor(m_spent_keys, tx_id, bl_id), in)) + { + LOG_ERROR("critical internal error: add_transaction_input_visitor failed. but here key_images should be shecked"); + purge_transaction_keyimages_from_blockchain(tx, false); + return false; + } + } + transaction_chain_entry ch_e; + ch_e.m_keeper_block_height = bl_height; + ch_e.tx = tx; + auto i_r = m_transactions.insert(std::pair<crypto::hash, transaction_chain_entry>(tx_id, ch_e)); + if(!i_r.second) + { + LOG_PRINT_L0("tx with id: " << tx_id << " in block id: " << bl_id << " already in blockchain"); + return false; + } + bool r = push_transaction_to_global_outs_index(tx, tx_id, i_r.first->second.m_global_output_indexes); + CHECK_AND_ASSERT_MES(r, false, "failed to return push_transaction_to_global_outs_index tx id " << tx_id); + LOG_PRINT_L2("Added transaction to blockchain history:" << ENDL + << "tx_id: " << tx_id << ENDL + << "inputs: " << tx.vin.size() << ", outs: " << tx.vout.size() << ", spend money: " << print_money(get_outs_money_amount(tx)) << "(fee: " << (is_coinbase(tx) ? "0[coinbase]" : print_money(get_tx_fee(tx))) << ")"); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + auto it = m_transactions.find(tx_id); + if(it == m_transactions.end()) + { + LOG_PRINT_RED_L0("warning: get_tx_outputs_gindexs failed to find transaction with id = " << tx_id); + return false; + } + + CHECK_AND_ASSERT_MES(it->second.m_global_output_indexes.size(), false, "internal error: global indexes for transaction " << tx_id << " is empty"); + indexs = it->second.m_global_output_indexes; + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + bool res = check_tx_inputs(tx, &max_used_block_height); + if(!res) return false; + CHECK_AND_ASSERT_MES(max_used_block_height < m_blocks.size(), false, "internal error: max used block index=" << max_used_block_height << " is not less then blockchain size = " << m_blocks.size()); + get_block_hash(m_blocks[max_used_block_height].bl, max_used_block_id); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::have_tx_keyimges_as_spent(const transaction &tx) +{ + BOOST_FOREACH(const txin_v& in, tx.vin) + { + CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, in_to_key, true); + if(have_tx_keyimg_as_spent(in_to_key.k_image)) + return true; + } + return false; +} +//------------------------------------------------------------------ +bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height) +{ + crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx); + return check_tx_inputs(tx, tx_prefix_hash, pmax_used_block_height); +} +//------------------------------------------------------------------ +bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height) +{ + size_t sig_index = 0; + if(pmax_used_block_height) + *pmax_used_block_height = 0; + + BOOST_FOREACH(const auto& txin, tx.vin) + { + CHECK_AND_ASSERT_MES(txin.type() == typeid(txin_to_key), false, "wrong type id in tx input at blockchain_storage::check_tx_inputs"); + const txin_to_key& in_to_key = boost::get<txin_to_key>(txin); + + CHECK_AND_ASSERT_MES(in_to_key.key_offsets.size(), false, "empty in_to_key.key_offsets in transaction with id " << get_transaction_hash(tx)); + + if(have_tx_keyimg_as_spent(in_to_key.k_image)) + { + LOG_PRINT_L1("Key image already spent in blockchain: " << string_tools::pod_to_hex(in_to_key.k_image)); + return false; + } + + CHECK_AND_ASSERT_MES(sig_index < tx.signatures.size(), false, "wrong transaction: not signature entry for input with index= " << sig_index); + if(!check_tx_input(in_to_key, tx_prefix_hash, tx.signatures[sig_index], pmax_used_block_height)) + { + LOG_PRINT_L0("Failed to check ring signature for tx " << get_transaction_hash(tx)); + return false; + } + + sig_index++; + } + + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time) +{ + if(unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER) + { + //interpret as block index + if(get_current_blockchain_height()-1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time) + return true; + else + return false; + }else + { + //interpret as time + uint64_t current_time = static_cast<uint64_t>(time(NULL)); + if(current_time + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS >= unlock_time) + return true; + else + return false; + } + return false; +} +//------------------------------------------------------------------ +bool blockchain_storage::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t* pmax_related_block_height) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + struct outputs_visitor + { + std::vector<const crypto::public_key *>& m_results_collector; + blockchain_storage& m_bch; + outputs_visitor(std::vector<const crypto::public_key *>& results_collector, blockchain_storage& bch):m_results_collector(results_collector), m_bch(bch) + {} + bool handle_output(const transaction& tx, const tx_out& out) + { + //check tx unlock time + if(!m_bch.is_tx_spendtime_unlocked(tx.unlock_time)) + { + LOG_PRINT_L0("One of outputs for one of inputs have wrong tx.unlock_time = " << tx.unlock_time); + return false; + } + + if(out.target.type() != typeid(txout_to_key)) + { + LOG_PRINT_L0("Output have wrong type id, which=" << out.target.which()); + return false; + } + + m_results_collector.push_back(&boost::get<txout_to_key>(out.target).key); + return true; + } + }; + + //check ring signature + std::vector<const crypto::public_key *> output_keys; + outputs_visitor vi(output_keys, *this); + if(!scan_outputkeys_for_indexes(txin, vi, pmax_related_block_height)) + { + LOG_PRINT_L0("Failed to get output keys for tx with amount = " << print_money(txin.amount) << " and count indexes " << txin.key_offsets.size()); + return false; + } + + if(txin.key_offsets.size() != output_keys.size()) + { + LOG_PRINT_L0("Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.key_offsets.size() << " returned wrong keys count " << output_keys.size()); + return false; + } + CHECK_AND_ASSERT_MES(sig.size() == output_keys.size(), false, "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size()); + if(m_is_in_checkpoint_zone) + return true; + return crypto::check_ring_signature(tx_prefix_hash, txin.k_image, output_keys, sig.data()); +} +//------------------------------------------------------------------ +uint64_t blockchain_storage::get_adjusted_time() +{ + //TODO: add collecting median time + return time(NULL); +} +//------------------------------------------------------------------ +bool blockchain_storage::check_block_timestamp_main(const block& b) +{ + if(b.timestamp > get_adjusted_time() + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT) + { + LOG_PRINT_L0("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 2 hours"); + return false; + } + + std::vector<uint64_t> timestamps; + size_t offset = m_blocks.size() <= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW ? 0: m_blocks.size()- BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW; + for(;offset!= m_blocks.size(); ++offset) + timestamps.push_back(m_blocks[offset].bl.timestamp); + + return check_block_timestamp(std::move(timestamps), b); +} +//------------------------------------------------------------------ +bool blockchain_storage::check_block_timestamp(std::vector<uint64_t> timestamps, const block& b) +{ + if(timestamps.size() < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW) + return true; + + uint64_t median_ts = epee::misc_utils::median(timestamps); + + if(b.timestamp < median_ts) + { + LOG_PRINT_L0("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", less than median of last " << BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW << " blocks, " << median_ts); + return false; + } + + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc) +{ + TIME_MEASURE_START(block_processing_time); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if(bl.prev_id != get_tail_id()) + { + LOG_PRINT_L0("Block with id: " << id << ENDL + << "have wrong prev_id: " << bl.prev_id << ENDL + << "expected: " << get_tail_id()); + return false; + } + + if(!check_block_timestamp_main(bl)) + { + LOG_PRINT_L0("Block with id: " << id << ENDL + << "have invalid timestamp: " << bl.timestamp); + //add_block_as_invalid(bl, id);//do not add blocks to invalid storage befor proof of work check was passed + bvc.m_verifivation_failed = true; + return false; + } + + //check proof of work + TIME_MEASURE_START(target_calculating_time); + difficulty_type current_diffic = get_difficulty_for_next_block(); + CHECK_AND_ASSERT_MES(current_diffic, false, "!!!!!!!!! difficulty overhead !!!!!!!!!"); + 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())) + { + proof_of_work = get_block_longhash(bl, m_blocks.size()); + + 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(!m_checkpoints.check_block(get_current_blockchain_height(), id)) + { + LOG_ERROR("CHECKPOINT VALIDATION FAILED"); + bvc.m_verifivation_failed = true; + return false; + } + } + TIME_MEASURE_FINISH(longhash_calculating_time); + + if(!prevalidate_miner_transaction(bl, m_blocks.size())) + { + LOG_PRINT_L0("Block with id: " << id + << " failed to pass prevalidation"); + bvc.m_verifivation_failed = true; + return false; + } + size_t coinbase_blob_size = get_object_blobsize(bl.miner_tx); + size_t cumulative_block_size = coinbase_blob_size; + //process transactions + if(!add_transaction_from_block(bl.miner_tx, get_transaction_hash(bl.miner_tx), id, get_current_blockchain_height())) + { + LOG_PRINT_L0("Block with id: " << id << " failed to add transaction to blockchain storage"); + bvc.m_verifivation_failed = true; + return false; + } + size_t tx_processed_count = 0; + uint64_t fee_summary = 0; + BOOST_FOREACH(const crypto::hash& tx_id, bl.tx_hashes) + { + transaction tx; + size_t blob_size = 0; + uint64_t fee = 0; + if(!m_tx_pool.take_tx(tx_id, tx, blob_size, fee)) + { + LOG_PRINT_L0("Block with id: " << id << "have at least one unknown transaction with id: " << tx_id); + purge_block_data_from_blockchain(bl, tx_processed_count); + //add_block_as_invalid(bl, id); + bvc.m_verifivation_failed = true; + return false; + } + if(!check_tx_inputs(tx)) + { + LOG_PRINT_L0("Block with id: " << id << "have at least one transaction (id: " << tx_id << ") with wrong inputs."); + cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); + bool add_res = m_tx_pool.add_tx(tx, tvc, true); + CHECK_AND_ASSERT_MES2(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool"); + purge_block_data_from_blockchain(bl, tx_processed_count); + add_block_as_invalid(bl, id); + LOG_PRINT_L0("Block with id " << id << " added as invalid becouse of wrong inputs in transactions"); + bvc.m_verifivation_failed = true; + return false; + } + + if(!add_transaction_from_block(tx, tx_id, id, get_current_blockchain_height())) + { + LOG_PRINT_L0("Block with id: " << id << " failed to add transaction to blockchain storage"); + cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); + bool add_res = m_tx_pool.add_tx(tx, tvc, true); + CHECK_AND_ASSERT_MES2(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool"); + purge_block_data_from_blockchain(bl, tx_processed_count); + bvc.m_verifivation_failed = true; + return false; + } + fee_summary += fee; + cumulative_block_size += blob_size; + ++tx_processed_count; + } + uint64_t base_reward = 0; + uint64_t already_generated_coins = m_blocks.size() ? m_blocks.back().already_generated_coins:0; + if(!validate_miner_transaction(bl, cumulative_block_size, fee_summary, base_reward, already_generated_coins)) + { + LOG_PRINT_L0("Block with id: " << id + << " have wrong miner transaction"); + purge_block_data_from_blockchain(bl, tx_processed_count); + bvc.m_verifivation_failed = true; + return false; + } + + + block_extended_info bei = boost::value_initialized<block_extended_info>(); + bei.bl = bl; + bei.block_cumulative_size = cumulative_block_size; + bei.cumulative_difficulty = current_diffic; + bei.already_generated_coins = already_generated_coins + base_reward; + if(m_blocks.size()) + bei.cumulative_difficulty += m_blocks.back().cumulative_difficulty; + + bei.height = m_blocks.size(); + + auto ind_res = m_blocks_index.insert(std::pair<crypto::hash, size_t>(id, bei.height)); + if(!ind_res.second) + { + LOG_ERROR("block with id: " << id << " already in block indexes"); + purge_block_data_from_blockchain(bl, tx_processed_count); + bvc.m_verifivation_failed = true; + return false; + } + + m_blocks.push_back(bei); + update_next_comulative_size_limit(); + TIME_MEASURE_FINISH(block_processing_time); + LOG_PRINT_L1("+++++ BLOCK SUCCESSFULLY ADDED" << ENDL << "id:\t" << id + << ENDL << "PoW:\t" << proof_of_work + << ENDL << "HEIGHT " << bei.height << ", difficulty:\t" << current_diffic + << ENDL << "block reward: " << print_money(fee_summary + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_summary) + << "), coinbase_blob_size: " << coinbase_blob_size << ", cumulative size: " << cumulative_block_size + << ", " << block_processing_time << "("<< target_calculating_time << "/" << longhash_calculating_time << ")ms"); + + bvc.m_added_to_main_chain = true; + /*if(!m_orphanes_reorganize_in_work) + review_orphaned_blocks_with_new_block_id(id, true);*/ + + m_tx_pool.on_blockchain_inc(bei.height, id); + //LOG_PRINT_L0("BLOCK: " << ENDL << "" << dump_obj_as_json(bei.bl)); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::update_next_comulative_size_limit() +{ + std::vector<size_t> sz; + get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + + uint64_t median = misc_utils::median(sz); + if(median <= CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE) + median = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE; + + m_current_block_comul_sz_limit = median*2; + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::add_new_block(const block& bl_, block_verification_context& bvc) +{ + //copy block here to let modify block.target + block bl = bl_; + crypto::hash id = get_block_hash(bl); + CRITICAL_REGION_LOCAL(m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process + CRITICAL_REGION_LOCAL1(m_blockchain_lock); + if(have_block(id)) + { + LOG_PRINT_L3("block with id = " << id << " already exists"); + bvc.m_already_exists = true; + return false; + } + + //check that block refers to chain tail + if(!(bl.prev_id == get_tail_id())) + { + //chain switching or wrong block + bvc.m_added_to_main_chain = false; + return handle_alternative_block(bl, id, bvc); + //never relay alternative blocks + } + + 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 new file mode 100644 index 000000000..4ff5e83ff --- /dev/null +++ b/src/cryptonote_core/blockchain_storage.h @@ -0,0 +1,297 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include <boost/serialization/serialization.hpp> +#include <boost/serialization/version.hpp> +#include <boost/serialization/list.hpp> +#include <boost/multi_index_container.hpp> +#include <boost/multi_index/global_fun.hpp> +#include <boost/multi_index/hashed_index.hpp> +#include <boost/multi_index/member.hpp> +#include <boost/foreach.hpp> +#include <atomic> + +#include "tx_pool.h" +#include "cryptonote_basic.h" +#include "common/util.h" +#include "cryptonote_protocol/cryptonote_protocol_defs.h" +#include "rpc/core_rpc_server_commands_defs.h" +#include "difficulty.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "verification_context.h" +#include "crypto/hash.h" +#include "checkpoints.h" + +namespace cryptonote +{ + + /************************************************************************/ + /* */ + /************************************************************************/ + class blockchain_storage + { + public: + struct transaction_chain_entry + { + transaction tx; + uint64_t m_keeper_block_height; + size_t m_blob_size; + std::vector<uint64_t> m_global_output_indexes; + }; + + struct block_extended_info + { + block bl; + uint64_t height; + size_t block_cumulative_size; + difficulty_type cumulative_difficulty; + uint64_t already_generated_coins; + }; + + blockchain_storage(tx_memory_pool& tx_pool):m_tx_pool(tx_pool), m_current_block_comul_sz_limit(0), m_is_in_checkpoint_zone(false) + {}; + + bool init() { return init(tools::get_default_data_dir()); } + bool init(const std::string& config_folder); + bool deinit(); + + void set_checkpoints(checkpoints&& chk_pts) { m_checkpoints = chk_pts; } + + //bool push_new_block(); + bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs); + bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks); + bool get_alternative_blocks(std::list<block>& blocks); + //bool get_orphaned_blocks(std::list<block>& orphaned_by_prev_id, std::list<block>& orphaned_by_tx); + //size_t get_orphaned_by_prev_blocks_count(); + //size_t get_orphaned_by_tx_blocks_count(); + size_t get_alternative_blocks_count(); + crypto::hash get_block_id_by_height(uint64_t height); + bool get_block_by_hash(const crypto::hash &h, block &blk); + void get_all_known_block_ids(std::list<crypto::hash> &main, std::list<crypto::hash> &alt, std::list<crypto::hash> &invalid); + + template<class archive_t> + void serialize(archive_t & ar, const unsigned int version); + + bool have_tx(const crypto::hash &id); + bool have_tx_keyimges_as_spent(const transaction &tx); + bool have_tx_keyimg_as_spent(const crypto::key_image &key_im); + transaction *get_tx(const crypto::hash &id); + + template<class visitor_t> + bool scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height = NULL); + + uint64_t get_current_blockchain_height(); + crypto::hash get_tail_id(); + crypto::hash get_tail_id(uint64_t& height); + difficulty_type get_difficulty_for_next_block(); + bool add_new_block(const block& bl_, block_verification_context& bvc); + + bool reset_and_set_genesis_block(const block& b); + bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, const blobdata& ex_nonce); + bool have_block(const crypto::hash& id); + size_t get_total_transactions(); + bool get_outs(uint64_t amount, std::list<crypto::public_key>& pkeys); + bool get_short_chain_history(std::list<crypto::hash>& ids); + bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp); + bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, uint64_t& starter_offset); + bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); + //bool get_chain_entry(const NOTIFY_REQUEST_CHAIN_ENTRY::request& req, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp); + bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp); + bool handle_get_objects(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res); + bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res); + //bool get_outs_for_amounts(uint64_t amount, std::vector<std::pair<crypto::hash, size_t> >& keys, std::map<crypto::hash, transaction>& txs); + bool get_backward_blocks_sizes(size_t from_height, std::vector<size_t>& sz, size_t count); + bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs); + bool store_blockchain(); + bool check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t* pmax_related_block_height = NULL); + bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height = NULL); + bool check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height = NULL); + bool check_tx_inputs(const transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id); + uint64_t get_current_comulative_blocksize_limit(); + + template<class t_ids_container, class t_blocks_container, class t_missed_container> + bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) + { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + BOOST_FOREACH(const auto& bl_id, block_ids) + { + auto it = m_blocks_index.find(bl_id); + if(it == m_blocks_index.end()) + 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) + << " have index record with offset="<<it->second<< ", bigger then m_blocks.size()=" << m_blocks.size()); + blocks.push_back(m_blocks[it->second].bl); + } + } + return true; + } + + template<class t_ids_container, class t_tx_container, class t_missed_container> + bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) + { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + BOOST_FOREACH(const auto& tx_id, txs_ids) + { + auto it = m_transactions.find(tx_id); + if(it == m_transactions.end()) + { + transaction tx; + if(!m_tx_pool.get_transaction(tx_id, tx)) + missed_txs.push_back(tx_id); + else + txs.push_back(tx); + } + else + txs.push_back(it->second.tx); + } + return true; + } + //debug functions + void print_blockchain(uint64_t start_index, uint64_t end_index); + void print_blockchain_index(); + void print_blockchain_outs(const std::string& file); + + private: + typedef std::unordered_map<crypto::hash, size_t> blocks_by_id_index; + typedef std::unordered_map<crypto::hash, transaction_chain_entry> transactions_container; + typedef std::unordered_set<crypto::key_image> key_images_container; + typedef std::vector<block_extended_info> blocks_container; + typedef std::unordered_map<crypto::hash, block_extended_info> blocks_ext_by_hash; + typedef std::unordered_map<crypto::hash, block> blocks_by_hash; + 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 + + // main chain + blocks_container m_blocks; // height -> block_extended_info + blocks_by_id_index m_blocks_index; // crypto::hash -> height + transactions_container m_transactions; + key_images_container m_spent_keys; + size_t m_current_block_comul_sz_limit; + + + // all alternative chains + blocks_ext_by_hash m_alternative_chains; // crypto::hash -> block_extended_info + + // some invalid blocks + blocks_ext_by_hash m_invalid_blocks; // crypto::hash -> block_extended_info + outputs_container m_outputs; + + + std::string m_config_folder; + checkpoints m_checkpoints; + std::atomic<bool> m_is_in_checkpoint_zone; + + + + + bool switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_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); + bool purge_transaction_keyimages_from_blockchain(const transaction& tx, bool strict_check); + + bool handle_block_to_main_chain(const block& bl, block_verification_context& bvc); + bool handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc); + bool handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc); + //bool handle_block_as_orphaned(const block& b, const crypto::hash& id, block_verification_context& bvc); + difficulty_type get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::iterator>& alt_chain, block_extended_info& bei); + bool prevalidate_miner_transaction(const block& b, uint64_t height); + bool validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins); + bool validate_transaction(const block& b, uint64_t height, const transaction& tx); + //bool add_block_to_orphaned_by_tx(const block& bl, const crypto::hash& h); + //bool finish_forward_orphaned_chain(std::list< orphans_indexed_container::index<by_id>::type::iterator >& orph_chain); + bool rollback_blockchain_switching(std::list<block>& original_chain, size_t rollback_height); + bool add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height); + bool push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector<uint64_t>& global_indexes); + bool pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id); + //bool review_orphaned_blocks_with_new_block_id(const crypto::hash& id, bool main_chain); + //void review_orphaned_blocks_finisher(); + bool get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count); + bool add_out_to_get_random_outs(std::vector<std::pair<crypto::hash, size_t> >& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i); + bool is_tx_spendtime_unlocked(uint64_t unlock_time); + bool add_block_as_invalid(const block& bl, const crypto::hash& h); + bool add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h); + size_t find_end_of_allowed_index(const std::vector<std::pair<crypto::hash, size_t> >& amount_outs); + uint64_t block_difficulty(size_t i); + bool check_block_timestamp_main(const block& b); + bool check_block_timestamp(std::vector<uint64_t> timestamps, const block& b); + uint64_t get_adjusted_time(); + bool complete_timestamps_vector(uint64_t start_height, std::vector<uint64_t>& timestamps); + bool update_next_comulative_size_limit(); + }; + + + /************************************************************************/ + /* */ + /************************************************************************/ + + #define CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER 11 + + template<class archive_t> + void blockchain_storage::serialize(archive_t & ar, const unsigned int version) + { + if(version < CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER) + return; + CRITICAL_REGION_LOCAL(m_blockchain_lock); + ar & m_blocks; + ar & m_blocks_index; + ar & m_transactions; + ar & m_spent_keys; + ar & m_alternative_chains; + ar & m_outputs; + ar & m_invalid_blocks; + ar & m_current_block_comul_sz_limit; + } + + //------------------------------------------------------------------ + template<class visitor_t> + bool blockchain_storage::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height) + { + + auto it = m_outputs.find(tx_in_to_key.amount); + if(it == m_outputs.end() || !tx_in_to_key.key_offsets.size()) + return false; + + std::vector<uint64_t> absolute_offsets = relative_output_offsets_to_absolute(tx_in_to_key.key_offsets); + + + std::vector<std::pair<crypto::hash, size_t> >& amount_outs_vec = it->second; + size_t count = 0; + BOOST_FOREACH(uint64_t i, absolute_offsets) + { + if(i >= amount_outs_vec.size() ) + { + LOG_PRINT_L0("Wrong index in transaction inputs: " << i << ", expected maximum " << amount_outs_vec.size() - 1); + 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(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])) + { + LOG_PRINT_L0("Failed to handle_output for output no = " << count << ", with absolute offset " << i); + return false; + } + if(count++ == absolute_offsets.size()-1 && pmax_related_block_height) + { + if(*pmax_related_block_height < tx_it->second.m_keeper_block_height) + *pmax_related_block_height = tx_it->second.m_keeper_block_height; + } + } + + return true; + } +} + + + +BOOST_CLASS_VERSION(cryptonote::blockchain_storage, CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER) diff --git a/src/cryptonote_core/blockchain_storage_boost_serialization.h b/src/cryptonote_core/blockchain_storage_boost_serialization.h new file mode 100644 index 000000000..6aa06e87f --- /dev/null +++ b/src/cryptonote_core/blockchain_storage_boost_serialization.h @@ -0,0 +1,33 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +namespace boost +{ + namespace serialization + { + + + template<class archive_t> + void serialize(archive_t & ar, cryptonote::blockchain_storage::transaction_chain_entry& te, const unsigned int version) + { + ar & te.tx; + ar & te.m_keeper_block_height; + ar & te.m_blob_size; + ar & te.m_global_output_indexes; + } + + template<class archive_t> + void serialize(archive_t & ar, cryptonote::blockchain_storage::block_extended_info& ei, const unsigned int version) + { + ar & ei.bl; + ar & ei.height; + ar & ei.cumulative_difficulty; + ar & ei.block_cumulative_size; + ar & ei.already_generated_coins; + } + + } +} diff --git a/src/cryptonote_core/checkpoints.cpp b/src/cryptonote_core/checkpoints.cpp new file mode 100644 index 000000000..54c2f3a6d --- /dev/null +++ b/src/cryptonote_core/checkpoints.cpp @@ -0,0 +1,48 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "include_base_utils.h" +using namespace epee; + +#include "checkpoints.h" + +namespace cryptonote +{ + //--------------------------------------------------------------------------- + checkpoints::checkpoints() + { + } + //--------------------------------------------------------------------------- + bool checkpoints::add_checkpoint(uint64_t height, const std::string& hash_str) + { + crypto::hash h = null_hash; + bool r = epee::string_tools::parse_tpod_from_hex_string(hash_str, h); + CHECK_AND_ASSERT_MES(r, false, "WRONG HASH IN CHECKPOINTS!!!"); + CHECK_AND_ASSERT_MES(0 == m_points.count(height), false, "WRONG HASH IN CHECKPOINTS!!!"); + m_points[height] = h; + return true; + } + //--------------------------------------------------------------------------- + bool checkpoints::is_in_checkpoint_zone(uint64_t height) const + { + return !m_points.empty() && (height <= (--m_points.end())->first); + } + //--------------------------------------------------------------------------- + bool checkpoints::check_block(uint64_t height, const crypto::hash& h) const + { + auto it = m_points.find(height); + if(it == m_points.end()) + return true; + + if(it->second == h) + { + LOG_PRINT_GREEN("CHECKPOINT PASSED FOR HEIGHT " << height << " " << h, LOG_LEVEL_0); + return true; + }else + { + LOG_ERROR("CHECKPOINT FAILED FOR HEIGHT " << height << ". EXPECTED HASH: " << it->second << ", FETCHED HASH: " << h); + return false; + } + } +} diff --git a/src/cryptonote_core/checkpoints.h b/src/cryptonote_core/checkpoints.h new file mode 100644 index 000000000..20014b1c8 --- /dev/null +++ b/src/cryptonote_core/checkpoints.h @@ -0,0 +1,22 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include <map> +#include "cryptonote_basic_impl.h" + + +namespace cryptonote +{ + class checkpoints + { + public: + checkpoints(); + 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; + 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 new file mode 100644 index 000000000..3d539de98 --- /dev/null +++ b/src/cryptonote_core/checkpoints_create.h @@ -0,0 +1,27 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include "checkpoints.h" +#include "misc_log_ex.h" + +#define ADD_CHECKPOINT(h, hash) CHECK_AND_ASSERT(checkpoints.add_checkpoint(h, hash), false); + +namespace cryptonote { + inline bool create_checkpoints(cryptonote::checkpoints& checkpoints) + { + ADD_CHECKPOINT(79000, "cae33204e624faeb64938d80073bb7bbacc27017dc63f36c5c0f313cad455a02"); + ADD_CHECKPOINT(140000, "993059fb6ab92db7d80d406c67a52d9c02d873ca34b6290a12b744c970208772"); + ADD_CHECKPOINT(200000, "a5f74c7542077df6859f48b5b1f9c3741f29df38f91a47e14c94b5696e6c3073"); + ADD_CHECKPOINT(230580, "32bd7cb6c68a599cf2861941f29002a5e203522b9af54f08dfced316f6459103"); + ADD_CHECKPOINT(260000, "f68e70b360ca194f48084da7a7fd8e0251bbb4b5587f787ca65a6f5baf3f5947"); + ADD_CHECKPOINT(300000, "8e80861713f68354760dc10ea6ea79f5f3ff28f39b3f0835a8637463b09d70ff"); + ADD_CHECKPOINT(390285, "e00bdc9bf407aeace2f3109de11889ed25894bf194231d075eddaec838097eb7"); + ADD_CHECKPOINT(417000, "2dc96f8fc4d4a4d76b3ed06722829a7ab09d310584b8ecedc9b578b2c458a69f"); + ADD_CHECKPOINT(427193, "00feabb08f2d5759ed04fd6b799a7513187478696bba2db2af10d4347134e311"); + + return true; + } +} diff --git a/src/cryptonote_core/connection_context.h b/src/cryptonote_core/connection_context.h new file mode 100644 index 000000000..bf13449bc --- /dev/null +++ b/src/cryptonote_core/connection_context.h @@ -0,0 +1,54 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include <unordered_set> +#include <atomic> +#include "net/net_utils_base.h" + + +namespace cryptonote +{ + class my_atomic: public std::atomic<uint32_t> + { + public: + my_atomic() + {}; + my_atomic(const my_atomic& a):std::atomic<uint32_t>(a.load()) + {} + my_atomic& operator= (const my_atomic& a) + { + store(a.load()); + return *this; + } + uint32_t operator++() + { + return std::atomic<uint32_t>::operator++(); + } + uint32_t operator++(int fake) + { + return std::atomic<uint32_t>::operator++(fake); + } + }; + + + struct cryptonote_connection_context: public epee::net_utils::connection_context_base + { + + enum state + { + state_befor_handshake = 0, //default state + state_synchronizing, + state_normal + }; + + state m_state; + std::list<crypto::hash> m_needed_objects; + std::unordered_set<crypto::hash> m_requested_objects; + uint64_t m_remote_blockchain_height; + uint64_t m_last_response_height; + my_atomic m_callback_request_count; //in debug purpose: problem with double callback rise + //size_t m_score; TODO: add score calculations + }; +} diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h new file mode 100644 index 000000000..007e62bbd --- /dev/null +++ b/src/cryptonote_core/cryptonote_basic.h @@ -0,0 +1,347 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <boost/variant.hpp> +#include <boost/functional/hash/hash.hpp> +#include <vector> +#include <cstring> // memcmp +#include <sstream> +#include "serialization/serialization.h" +#include "serialization/variant.h" +#include "serialization/vector.h" +#include "serialization/binary_archive.h" +#include "serialization/json_archive.h" +#include "serialization/debug_archive.h" +#include "serialization/crypto.h" +#include "serialization/keyvalue_serialization.h" // eepe named serialization +#include "string_tools.h" +#include "cryptonote_config.h" +#include "crypto/crypto.h" +#include "crypto/hash.h" +#include "misc_language.h" +#include "tx_extra.h" + + +namespace cryptonote +{ + + const static crypto::hash null_hash = AUTO_VAL_INIT(null_hash); + const static crypto::public_key null_pkey = AUTO_VAL_INIT(null_pkey); + + typedef std::vector<crypto::signature> ring_signature; + + + /* outputs */ + + struct txout_to_script + { + std::vector<crypto::public_key> keys; + std::vector<uint8_t> script; + + BEGIN_SERIALIZE_OBJECT() + FIELD(keys) + FIELD(script) + END_SERIALIZE() + }; + + struct txout_to_scripthash + { + crypto::hash hash; + }; + + struct txout_to_key + { + txout_to_key() { } + txout_to_key(const crypto::public_key &_key) : key(_key) { } + crypto::public_key key; + }; + + + /* inputs */ + + struct txin_gen + { + size_t height; + + BEGIN_SERIALIZE_OBJECT() + VARINT_FIELD(height) + END_SERIALIZE() + }; + + struct txin_to_script + { + crypto::hash prev; + size_t prevout; + std::vector<uint8_t> sigset; + + BEGIN_SERIALIZE_OBJECT() + FIELD(prev) + VARINT_FIELD(prevout) + FIELD(sigset) + END_SERIALIZE() + }; + + struct txin_to_scripthash + { + crypto::hash prev; + size_t prevout; + txout_to_script script; + std::vector<uint8_t> sigset; + + BEGIN_SERIALIZE_OBJECT() + FIELD(prev) + VARINT_FIELD(prevout) + FIELD(script) + FIELD(sigset) + END_SERIALIZE() + }; + + struct txin_to_key + { + uint64_t amount; + std::vector<uint64_t> key_offsets; + crypto::key_image k_image; // double spending protection + + BEGIN_SERIALIZE_OBJECT() + VARINT_FIELD(amount) + FIELD(key_offsets) + FIELD(k_image) + END_SERIALIZE() + }; + + + typedef boost::variant<txin_gen, txin_to_script, txin_to_scripthash, txin_to_key> txin_v; + + typedef boost::variant<txout_to_script, txout_to_scripthash, txout_to_key> txout_target_v; + + //typedef std::pair<uint64_t, txout> out_t; + struct tx_out + { + uint64_t amount; + txout_target_v target; + + BEGIN_SERIALIZE_OBJECT() + VARINT_FIELD(amount) + FIELD(target) + END_SERIALIZE() + + + }; + + class transaction_prefix + { + + public: + // tx information + size_t version; + uint64_t unlock_time; //number of block (or time), used as a limitation like: spend this tx not early then block/time + + std::vector<txin_v> vin; + std::vector<tx_out> vout; + //extra + std::vector<uint8_t> extra; + + BEGIN_SERIALIZE() + VARINT_FIELD(version) + if(CURRENT_TRANSACTION_VERSION < version) return false; + VARINT_FIELD(unlock_time) + FIELD(vin) + FIELD(vout) + FIELD(extra) + END_SERIALIZE() + + + protected: + transaction_prefix(){} + }; + + class transaction: public transaction_prefix + { + public: + std::vector<std::vector<crypto::signature> > signatures; //count signatures always the same as inputs count + + transaction(); + virtual ~transaction(); + void set_null(); + + BEGIN_SERIALIZE_OBJECT() + FIELDS(*static_cast<transaction_prefix *>(this)) + + ar.tag("signatures"); + ar.begin_array(); + PREPARE_CUSTOM_VECTOR_SERIALIZATION(vin.size(), signatures); + bool signatures_not_expected = signatures.empty(); + if (!signatures_not_expected && vin.size() != signatures.size()) + return false; + + for (size_t i = 0; i < vin.size(); ++i) + { + size_t signature_size = get_signature_size(vin[i]); + if (signatures_not_expected) + { + if (0 == signature_size) + continue; + else + return false; + } + + PREPARE_CUSTOM_VECTOR_SERIALIZATION(signature_size, signatures[i]); + if (signature_size != signatures[i].size()) + return false; + + FIELDS(signatures[i]); + + if (vin.size() - i > 1) + ar.delimit_array(); + } + ar.end_array(); + END_SERIALIZE() + + private: + static size_t get_signature_size(const txin_v& tx_in); + }; + + + inline + transaction::transaction() + { + set_null(); + } + + inline + transaction::~transaction() + { + //set_null(); + } + + inline + void transaction::set_null() + { + version = 0; + unlock_time = 0; + vin.clear(); + vout.clear(); + extra.clear(); + signatures.clear(); + } + + inline + size_t transaction::get_signature_size(const txin_v& tx_in) + { + struct txin_signature_size_visitor : public boost::static_visitor<size_t> + { + size_t operator()(const txin_gen& txin) const{return 0;} + size_t operator()(const txin_to_script& txin) const{return 0;} + size_t operator()(const txin_to_scripthash& txin) const{return 0;} + size_t operator()(const txin_to_key& txin) const {return txin.key_offsets.size();} + }; + + return boost::apply_visitor(txin_signature_size_visitor(), tx_in); + } + + + + /************************************************************************/ + /* */ + /************************************************************************/ + struct block_header + { + uint8_t major_version; + uint8_t minor_version; + uint64_t timestamp; + crypto::hash prev_id; + uint32_t nonce; + + BEGIN_SERIALIZE() + VARINT_FIELD(major_version) + if(major_version > CURRENT_BLOCK_MAJOR_VERSION) return false; + VARINT_FIELD(minor_version) + VARINT_FIELD(timestamp) + FIELD(prev_id) + FIELD(nonce) + END_SERIALIZE() + }; + + struct block: public block_header + { + transaction miner_tx; + std::vector<crypto::hash> tx_hashes; + + BEGIN_SERIALIZE_OBJECT() + FIELDS(*static_cast<block_header *>(this)) + FIELD(miner_tx) + FIELD(tx_hashes) + END_SERIALIZE() + }; + + + /************************************************************************/ + /* */ + /************************************************************************/ + struct account_public_address + { + crypto::public_key m_spend_public_key; + crypto::public_key m_view_public_key; + + BEGIN_SERIALIZE_OBJECT() + FIELD(m_spend_public_key) + FIELD(m_view_public_key) + END_SERIALIZE() + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_spend_public_key) + KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_view_public_key) + END_KV_SERIALIZE_MAP() + }; + + struct keypair + { + crypto::public_key pub; + crypto::secret_key sec; + + static inline keypair generate() + { + keypair k; + generate_keys(k.pub, k.sec); + return k; + } + }; + //--------------------------------------------------------------- + +} + +BLOB_SERIALIZER(cryptonote::txout_to_key); +BLOB_SERIALIZER(cryptonote::txout_to_scripthash); + +VARIANT_TAG(binary_archive, cryptonote::txin_gen, 0xff); +VARIANT_TAG(binary_archive, cryptonote::txin_to_script, 0x0); +VARIANT_TAG(binary_archive, cryptonote::txin_to_scripthash, 0x1); +VARIANT_TAG(binary_archive, cryptonote::txin_to_key, 0x2); +VARIANT_TAG(binary_archive, cryptonote::txout_to_script, 0x0); +VARIANT_TAG(binary_archive, cryptonote::txout_to_scripthash, 0x1); +VARIANT_TAG(binary_archive, cryptonote::txout_to_key, 0x2); +VARIANT_TAG(binary_archive, cryptonote::transaction, 0xcc); +VARIANT_TAG(binary_archive, cryptonote::block, 0xbb); + +VARIANT_TAG(json_archive, cryptonote::txin_gen, "gen"); +VARIANT_TAG(json_archive, cryptonote::txin_to_script, "script"); +VARIANT_TAG(json_archive, cryptonote::txin_to_scripthash, "scripthash"); +VARIANT_TAG(json_archive, cryptonote::txin_to_key, "key"); +VARIANT_TAG(json_archive, cryptonote::txout_to_script, "script"); +VARIANT_TAG(json_archive, cryptonote::txout_to_scripthash, "scripthash"); +VARIANT_TAG(json_archive, cryptonote::txout_to_key, "key"); +VARIANT_TAG(json_archive, cryptonote::transaction, "tx"); +VARIANT_TAG(json_archive, cryptonote::block, "block"); + +VARIANT_TAG(debug_archive, cryptonote::txin_gen, "gen"); +VARIANT_TAG(debug_archive, cryptonote::txin_to_script, "script"); +VARIANT_TAG(debug_archive, cryptonote::txin_to_scripthash, "scripthash"); +VARIANT_TAG(debug_archive, cryptonote::txin_to_key, "key"); +VARIANT_TAG(debug_archive, cryptonote::txout_to_script, "script"); +VARIANT_TAG(debug_archive, cryptonote::txout_to_scripthash, "scripthash"); +VARIANT_TAG(debug_archive, cryptonote::txout_to_key, "key"); +VARIANT_TAG(debug_archive, cryptonote::transaction, "tx"); +VARIANT_TAG(debug_archive, cryptonote::block, "block"); diff --git a/src/cryptonote_core/cryptonote_basic_impl.cpp b/src/cryptonote_core/cryptonote_basic_impl.cpp new file mode 100644 index 000000000..b320a3463 --- /dev/null +++ b/src/cryptonote_core/cryptonote_basic_impl.cpp @@ -0,0 +1,193 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "include_base_utils.h" +using namespace epee; + +#include "cryptonote_basic_impl.h" +#include "string_tools.h" +#include "serialization/binary_utils.h" +#include "serialization/vector.h" +#include "cryptonote_format_utils.h" +#include "cryptonote_config.h" +#include "misc_language.h" +#include "common/base58.h" +#include "crypto/hash.h" +#include "common/int-util.h" + +namespace cryptonote { + + /************************************************************************/ + /* Cryptonote helper functions */ + /************************************************************************/ + //----------------------------------------------------------------------------------------------- + size_t get_max_block_size() + { + return CRYPTONOTE_MAX_BLOCK_SIZE; + } + //----------------------------------------------------------------------------------------------- + size_t get_max_tx_size() + { + return CRYPTONOTE_MAX_TX_SIZE; + } + //----------------------------------------------------------------------------------------------- + uint64_t get_block_reward(std::vector<size_t>& last_blocks_sizes, size_t current_block_size, bool& block_too_big, uint64_t already_generated_coins) + { + block_too_big = false; + + uint64_t base_reward = (MONEY_SUPPLY - already_generated_coins) >> 18; + + if(current_block_size < CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE) + return base_reward; + + size_t med_sz = misc_utils::median(last_blocks_sizes); + + //make it soft + if(med_sz < CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE) + med_sz = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE; + + if(current_block_size > med_sz) + { + if(current_block_size > med_sz*2) + { + LOG_PRINT_L0("Block cumulative size is too big: " << current_block_size << ", expected less than " << med_sz*2); + block_too_big = true; + return 0; + } + + assert(med_sz < std::numeric_limits<uint32_t>::max()); + assert(current_block_size < std::numeric_limits<uint32_t>::max()); + + uint64_t product_hi; + uint64_t product_lo = mul128(base_reward, current_block_size * (2 * med_sz - current_block_size), &product_hi); + + uint64_t reward_hi; + uint64_t reward_lo; + div128_32(product_hi, product_lo, static_cast<uint32_t>(med_sz), &reward_hi, &reward_lo); + div128_32(reward_hi, reward_lo, static_cast<uint32_t>(med_sz), &reward_hi, &reward_lo); + assert(0 == reward_hi); + assert(reward_lo < base_reward); + + return reward_lo; + }else + return base_reward; + } + //------------------------------------------------------------------------------------ + uint8_t get_account_address_checksum(const public_address_outer_blob& bl) + { + const unsigned char* pbuf = reinterpret_cast<const unsigned char*>(&bl); + uint8_t summ = 0; + for(size_t i = 0; i!= sizeof(public_address_outer_blob)-1; i++) + summ += pbuf[i]; + + return summ; + } + //----------------------------------------------------------------------- + std::string get_account_address_as_str(const account_public_address& adr) + { + return tools::base58::encode_addr(CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX, t_serializable_object_to_blob(adr)); + } + //----------------------------------------------------------------------- + bool is_coinbase(const transaction& tx) + { + if(tx.vin.size() != 1) + return false; + + if(tx.vin[0].type() != typeid(txin_gen)) + return false; + + return true; + } + //----------------------------------------------------------------------- + bool get_account_address_from_str(account_public_address& adr, const std::string& str) + { + if (2 * sizeof(public_address_outer_blob) != str.size()) + { + blobdata data; + uint64_t prefix; + if (!tools::base58::decode_addr(str, prefix, data)) + { + LOG_PRINT_L0("Invalid address format"); + return false; + } + + if (CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX != prefix) + { + LOG_PRINT_L0("Wrong address prefix: " << prefix << ", expected " << CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX); + return false; + } + + if (!::serialization::parse_binary(data, adr)) + { + LOG_PRINT_L0("Account public address keys can't be parsed"); + return false; + } + + if (!crypto::check_key(adr.m_spend_public_key) || !crypto::check_key(adr.m_view_public_key)) + { + LOG_PRINT_L0("Failed to validate address keys"); + return false; + } + } + else + { + // Old address format + std::string buff; + if(!string_tools::parse_hexstr_to_binbuff(str, buff)) + return false; + + if(buff.size()!=sizeof(public_address_outer_blob)) + { + LOG_PRINT_L0("Wrong public address size: " << buff.size() << ", expected size: " << sizeof(public_address_outer_blob)); + return false; + } + + public_address_outer_blob blob = *reinterpret_cast<const public_address_outer_blob*>(buff.data()); + + + if(blob.m_ver > CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER) + { + LOG_PRINT_L0("Unknown version of public address: " << blob.m_ver << ", expected " << CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER); + return false; + } + + if(blob.check_sum != get_account_address_checksum(blob)) + { + LOG_PRINT_L0("Wrong public address checksum"); + return false; + } + + //we success + adr = blob.m_address; + } + + return true; + } + + bool operator ==(const cryptonote::transaction& a, const cryptonote::transaction& b) { + return cryptonote::get_transaction_hash(a) == cryptonote::get_transaction_hash(b); + } + + bool operator ==(const cryptonote::block& a, const cryptonote::block& b) { + return cryptonote::get_block_hash(a) == cryptonote::get_block_hash(b); + } +} + +//-------------------------------------------------------------------------------- +bool parse_hash256(const std::string str_hash, crypto::hash& hash) +{ + std::string buf; + bool res = epee::string_tools::parse_hexstr_to_binbuff(str_hash, buf); + if (!res || buf.size() != sizeof(crypto::hash)) + { + std::cout << "invalid hash format: <" << str_hash << '>' << std::endl; + return false; + } + else + { + buf.copy(reinterpret_cast<char *>(&hash), sizeof(crypto::hash)); + return true; + } +} diff --git a/src/cryptonote_core/cryptonote_basic_impl.h b/src/cryptonote_core/cryptonote_basic_impl.h new file mode 100644 index 000000000..f56d09a8e --- /dev/null +++ b/src/cryptonote_core/cryptonote_basic_impl.h @@ -0,0 +1,65 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include "cryptonote_basic.h" +#include "crypto/crypto.h" +#include "crypto/hash.h" + + +namespace cryptonote { + /************************************************************************/ + /* */ + /************************************************************************/ + template<class t_array> + struct array_hasher: std::unary_function<t_array&, std::size_t> + { + std::size_t operator()(const t_array& val) const + { + return boost::hash_range(&val.data[0], &val.data[sizeof(val.data)]); + } + }; + + +#pragma pack(push, 1) + struct public_address_outer_blob + { + uint8_t m_ver; + account_public_address m_address; + uint8_t check_sum; + }; +#pragma pack (pop) + + + /************************************************************************/ + /* Cryptonote helper functions */ + /************************************************************************/ + size_t get_max_block_size(); + size_t get_max_tx_size(); + uint64_t get_block_reward(std::vector<size_t>& last_blocks_sizes, size_t current_block_size, bool& block_too_big, uint64_t already_generated_coins); + uint8_t get_account_address_checksum(const public_address_outer_blob& bl); + std::string get_account_address_as_str(const account_public_address& adr); + bool get_account_address_from_str(account_public_address& adr, const std::string& str); + bool is_coinbase(const transaction& tx); + + bool operator ==(const cryptonote::transaction& a, const cryptonote::transaction& b); + bool operator ==(const cryptonote::block& a, const cryptonote::block& b); +} + +template <class T> +std::ostream &print256(std::ostream &o, const T &v) { + return o << "<" << epee::string_tools::pod_to_hex(v) << ">"; +} + +bool parse_hash256(const std::string str_hash, crypto::hash& hash); + +namespace crypto { + inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { return print256(o, v); } + inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) { return print256(o, v); } + inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) { return print256(o, v); } + inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) { return print256(o, v); } + inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) { return print256(o, v); } + inline std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { return print256(o, v); } +} diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h new file mode 100644 index 000000000..80c497844 --- /dev/null +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -0,0 +1,143 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <boost/serialization/vector.hpp> +#include <boost/serialization/utility.hpp> +#include <boost/serialization/variant.hpp> +#include <boost/serialization/set.hpp> +#include <boost/serialization/map.hpp> +#include <boost/foreach.hpp> +#include <boost/serialization/is_bitwise_serializable.hpp> +#include "cryptonote_basic.h" +#include "common/unordered_containers_boost_serialization.h" +#include "crypto/crypto.h" + +//namespace cryptonote { +namespace boost +{ + namespace serialization + { + + //--------------------------------------------------- + template <class Archive> + inline void serialize(Archive &a, crypto::public_key &x, const boost::serialization::version_type ver) + { + a & reinterpret_cast<char (&)[sizeof(crypto::public_key)]>(x); + } + template <class Archive> + inline void serialize(Archive &a, crypto::secret_key &x, const boost::serialization::version_type ver) + { + a & reinterpret_cast<char (&)[sizeof(crypto::secret_key)]>(x); + } + template <class Archive> + inline void serialize(Archive &a, crypto::key_derivation &x, const boost::serialization::version_type ver) + { + a & reinterpret_cast<char (&)[sizeof(crypto::key_derivation)]>(x); + } + template <class Archive> + inline void serialize(Archive &a, crypto::key_image &x, const boost::serialization::version_type ver) + { + a & reinterpret_cast<char (&)[sizeof(crypto::key_image)]>(x); + } + + template <class Archive> + inline void serialize(Archive &a, crypto::signature &x, const boost::serialization::version_type ver) + { + a & reinterpret_cast<char (&)[sizeof(crypto::signature)]>(x); + } + template <class Archive> + inline void serialize(Archive &a, crypto::hash &x, const boost::serialization::version_type ver) + { + a & reinterpret_cast<char (&)[sizeof(crypto::hash)]>(x); + } + + template <class Archive> + inline void serialize(Archive &a, cryptonote::txout_to_script &x, const boost::serialization::version_type ver) + { + a & x.keys; + a & x.script; + } + + + template <class Archive> + inline void serialize(Archive &a, cryptonote::txout_to_key &x, const boost::serialization::version_type ver) + { + a & x.key; + } + + template <class Archive> + inline void serialize(Archive &a, cryptonote::txout_to_scripthash &x, const boost::serialization::version_type ver) + { + a & x.hash; + } + + template <class Archive> + inline void serialize(Archive &a, cryptonote::txin_gen &x, const boost::serialization::version_type ver) + { + a & x.height; + } + + template <class Archive> + inline void serialize(Archive &a, cryptonote::txin_to_script &x, const boost::serialization::version_type ver) + { + a & x.prev; + a & x.prevout; + a & x.sigset; + } + + template <class Archive> + inline void serialize(Archive &a, cryptonote::txin_to_scripthash &x, const boost::serialization::version_type ver) + { + a & x.prev; + a & x.prevout; + a & x.script; + a & x.sigset; + } + + template <class Archive> + inline void serialize(Archive &a, cryptonote::txin_to_key &x, const boost::serialization::version_type ver) + { + a & x.amount; + a & x.key_offsets; + a & x.k_image; + } + + template <class Archive> + inline void serialize(Archive &a, cryptonote::tx_out &x, const boost::serialization::version_type ver) + { + a & x.amount; + a & x.target; + } + + + template <class Archive> + inline void serialize(Archive &a, cryptonote::transaction &x, const boost::serialization::version_type ver) + { + a & x.version; + a & x.unlock_time; + a & x.vin; + a & x.vout; + a & x.extra; + a & x.signatures; + } + + + template <class Archive> + inline void serialize(Archive &a, cryptonote::block &b, const boost::serialization::version_type ver) + { + a & b.major_version; + a & b.minor_version; + a & b.timestamp; + a & b.prev_id; + a & b.nonce; + //------------------ + a & b.miner_tx; + a & b.tx_hashes; + } +} +} + +//} diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp new file mode 100644 index 000000000..d5ab8d65a --- /dev/null +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -0,0 +1,519 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "include_base_utils.h" +using namespace epee; + +#include <boost/foreach.hpp> +#include <unordered_set> +#include "cryptonote_core.h" +#include "common/command_line.h" +#include "common/util.h" +#include "warnings.h" +#include "crypto/crypto.h" +#include "cryptonote_config.h" +#include "cryptonote_format_utils.h" +#include "misc_language.h" + +DISABLE_VS_WARNINGS(4355) + +namespace cryptonote +{ + + //----------------------------------------------------------------------------------------------- + core::core(i_cryptonote_protocol* pprotocol): + m_mempool(m_blockchain_storage), + m_blockchain_storage(m_mempool), + m_miner(this), + m_miner_address(boost::value_initialized<account_public_address>()), + m_starter_message_showed(false) + { + set_cryptonote_protocol(pprotocol); + } + void core::set_cryptonote_protocol(i_cryptonote_protocol* pprotocol) + { + if(pprotocol) + m_pprotocol = pprotocol; + else + m_pprotocol = &m_protocol_stub; + } + //----------------------------------------------------------------------------------- + void core::set_checkpoints(checkpoints&& chk_pts) + { + m_blockchain_storage.set_checkpoints(std::move(chk_pts)); + } + //----------------------------------------------------------------------------------- + void core::init_options(boost::program_options::options_description& /*desc*/) + { + } + //----------------------------------------------------------------------------------------------- + bool core::handle_command_line(const boost::program_options::variables_map& vm) + { + m_config_folder = command_line::get_arg(vm, command_line::arg_data_dir); + return true; + } + //----------------------------------------------------------------------------------------------- + uint64_t core::get_current_blockchain_height() + { + return m_blockchain_storage.get_current_blockchain_height(); + } + //----------------------------------------------------------------------------------------------- + bool core::get_blockchain_top(uint64_t& height, crypto::hash& top_id) + { + top_id = m_blockchain_storage.get_tail_id(height); + return true; + } + //----------------------------------------------------------------------------------------------- + bool core::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) + { + return m_blockchain_storage.get_blocks(start_offset, count, blocks, txs); + } + //----------------------------------------------------------------------------------------------- + bool core::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) + { + return m_blockchain_storage.get_blocks(start_offset, count, blocks); + } //----------------------------------------------------------------------------------------------- + bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& missed_txs) + { + return m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs); + } + //----------------------------------------------------------------------------------------------- + bool core::get_transaction(const crypto::hash &h, transaction &tx) + { + std::vector<crypto::hash> ids; + ids.push_back(h); + std::list<transaction> ltx; + std::list<crypto::hash> missing; + if (m_blockchain_storage.get_transactions(ids, ltx, missing)) + { + if (ltx.size() > 0) + { + tx = *ltx.begin(); + return true; + } + } + + return false; + } + //----------------------------------------------------------------------------------------------- + bool core::get_alternative_blocks(std::list<block>& blocks) + { + return m_blockchain_storage.get_alternative_blocks(blocks); + } + //----------------------------------------------------------------------------------------------- + size_t core::get_alternative_blocks_count() + { + return m_blockchain_storage.get_alternative_blocks_count(); + } + //----------------------------------------------------------------------------------------------- + bool core::init(const boost::program_options::variables_map& vm) + { + bool r = handle_command_line(vm); + + r = m_mempool.init(m_config_folder); + CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); + + r = m_blockchain_storage.init(m_config_folder); + CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); + + r = m_miner.init(vm); + CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); + + return load_state_data(); + } + //----------------------------------------------------------------------------------------------- + bool core::set_genesis_block(const block& b) + { + return m_blockchain_storage.reset_and_set_genesis_block(b); + } + //----------------------------------------------------------------------------------------------- + bool core::load_state_data() + { + // may be some code later + return true; + } + //----------------------------------------------------------------------------------------------- + bool core::deinit() + { + m_miner.stop(); + m_mempool.deinit(); + m_blockchain_storage.deinit(); + return true; + } + //----------------------------------------------------------------------------------------------- + bool core::handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block) + { + tvc = boost::value_initialized<tx_verification_context>(); + //want to process all transactions sequentially + CRITICAL_REGION_LOCAL(m_incoming_tx_lock); + + if(tx_blob.size() > get_max_tx_size()) + { + LOG_PRINT_L0("WRONG TRANSACTION BLOB, too big size " << tx_blob.size() << ", rejected"); + tvc.m_verifivation_failed = true; + return false; + } + + crypto::hash tx_hash = null_hash; + crypto::hash tx_prefixt_hash = null_hash; + transaction tx; + + if(!parse_tx_from_blob(tx, tx_hash, tx_prefixt_hash, tx_blob)) + { + LOG_PRINT_L0("WRONG TRANSACTION BLOB, Failed to parse, rejected"); + tvc.m_verifivation_failed = true; + return false; + } + //std::cout << "!"<< tx.vin.size() << std::endl; + + if(!check_tx_syntax(tx)) + { + LOG_PRINT_L0("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " syntax, rejected"); + tvc.m_verifivation_failed = true; + return false; + } + + if(!check_tx_semantic(tx, keeped_by_block)) + { + LOG_PRINT_L0("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " semantic, rejected"); + tvc.m_verifivation_failed = true; + return false; + } + + bool r = add_new_tx(tx, tx_hash, tx_prefixt_hash, tx_blob.size(), tvc, keeped_by_block); + if(tvc.m_verifivation_failed) + {LOG_PRINT_RED_L0("Transaction verification failed: " << tx_hash);} + else if(tvc.m_verifivation_impossible) + {LOG_PRINT_RED_L0("Transaction verification impossible: " << tx_hash);} + + if(tvc.m_added_to_pool) + LOG_PRINT_L1("tx added: " << tx_hash); + return r; + } + //----------------------------------------------------------------------------------------------- + bool core::get_stat_info(core_stat_info& st_inf) + { + st_inf.mining_speed = m_miner.get_speed(); + st_inf.alternative_blocks = m_blockchain_storage.get_alternative_blocks_count(); + st_inf.blockchain_height = m_blockchain_storage.get_current_blockchain_height(); + st_inf.tx_pool_size = m_mempool.get_transactions_count(); + st_inf.top_block_id_str = epee::string_tools::pod_to_hex(m_blockchain_storage.get_tail_id()); + return true; + } + + //----------------------------------------------------------------------------------------------- + bool core::check_tx_semantic(const transaction& tx, bool keeped_by_block) + { + if(!tx.vin.size()) + { + LOG_PRINT_RED_L0("tx with empty inputs, rejected for tx id= " << get_transaction_hash(tx)); + return false; + } + + if(!check_inputs_types_supported(tx)) + { + LOG_PRINT_RED_L0("unsupported input types for tx id= " << get_transaction_hash(tx)); + return false; + } + + if(!check_outs_valid(tx)) + { + LOG_PRINT_RED_L0("tx with invalid outputs, rejected for tx id= " << get_transaction_hash(tx)); + return false; + } + + if(!check_money_overflow(tx)) + { + LOG_PRINT_RED_L0("tx have money overflow, rejected for tx id= " << get_transaction_hash(tx)); + return false; + } + + boost::uint64_t amount_in = 0; + get_inputs_money_amount(tx, amount_in); + boost::uint64_t amount_out = get_outs_money_amount(tx); + + if(amount_in <= amount_out) + { + LOG_PRINT_RED_L0("tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << get_transaction_hash(tx)); + return false; + } + + if(!keeped_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_comulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE) + { + LOG_PRINT_RED_L0("tx have to big size " << get_object_blobsize(tx) << ", expected not bigger than " << m_blockchain_storage.get_current_comulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); + return false; + } + + //check if tx use different key images + if(!check_tx_inputs_keyimages_diff(tx)) + { + LOG_PRINT_RED_L0("tx have to big size " << get_object_blobsize(tx) << ", expected not bigger than " << m_blockchain_storage.get_current_comulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); + return false; + } + + + return true; + } + //----------------------------------------------------------------------------------------------- + bool core::check_tx_inputs_keyimages_diff(const transaction& tx) + { + std::unordered_set<crypto::key_image> ki; + BOOST_FOREACH(const auto& in, tx.vin) + { + CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false); + if(!ki.insert(tokey_in.k_image).second) + return false; + } + return true; + } + //----------------------------------------------------------------------------------------------- + bool core::add_new_tx(const transaction& tx, tx_verification_context& tvc, bool keeped_by_block) + { + crypto::hash tx_hash = get_transaction_hash(tx); + crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx); + blobdata bl; + t_serializable_object_to_blob(tx, bl); + return add_new_tx(tx, tx_hash, tx_prefix_hash, bl.size(), tvc, keeped_by_block); + } + //----------------------------------------------------------------------------------------------- + size_t core::get_blockchain_total_transactions() + { + return m_blockchain_storage.get_total_transactions(); + } + //----------------------------------------------------------------------------------------------- + bool core::get_outs(uint64_t amount, std::list<crypto::public_key>& pkeys) + { + return m_blockchain_storage.get_outs(amount, pkeys); + } + //----------------------------------------------------------------------------------------------- + bool core::add_new_tx(const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block) + { + if(m_mempool.have_tx(tx_hash)) + { + LOG_PRINT_L2("tx " << tx_hash << "already have transaction in tx_pool"); + return true; + } + + if(m_blockchain_storage.have_tx(tx_hash)) + { + LOG_PRINT_L2("tx " << tx_hash << " already have transaction in blockchain"); + return true; + } + + return m_mempool.add_tx(tx, tx_hash, blob_size, tvc, keeped_by_block); + } + //----------------------------------------------------------------------------------------------- + bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) + { + return m_blockchain_storage.create_block_template(b, adr, diffic, height, ex_nonce); + } + //----------------------------------------------------------------------------------------------- + bool core::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) + { + return m_blockchain_storage.find_blockchain_supplement(qblock_ids, resp); + } + //----------------------------------------------------------------------------------------------- + bool core::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) + { + return m_blockchain_storage.find_blockchain_supplement(qblock_ids, blocks, total_height, start_height, max_count); + } + //----------------------------------------------------------------------------------------------- + void core::print_blockchain(uint64_t start_index, uint64_t end_index) + { + m_blockchain_storage.print_blockchain(start_index, end_index); + } + //----------------------------------------------------------------------------------------------- + void core::print_blockchain_index() + { + m_blockchain_storage.print_blockchain_index(); + } + //----------------------------------------------------------------------------------------------- + void core::print_blockchain_outs(const std::string& file) + { + m_blockchain_storage.print_blockchain_outs(file); + } + //----------------------------------------------------------------------------------------------- + bool core::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) + { + return m_blockchain_storage.get_random_outs_for_amounts(req, res); + } + //----------------------------------------------------------------------------------------------- + bool core::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) + { + return m_blockchain_storage.get_tx_outputs_gindexs(tx_id, indexs); + } + //----------------------------------------------------------------------------------------------- + void core::pause_mine() + { + m_miner.pause(); + } + //----------------------------------------------------------------------------------------------- + void core::resume_mine() + { + m_miner.resume(); + } + //----------------------------------------------------------------------------------------------- + bool core::handle_block_found(block& b) + { + block_verification_context bvc = boost::value_initialized<block_verification_context>(); + m_miner.pause(); + m_blockchain_storage.add_new_block(b, bvc); + //anyway - update miner template + update_miner_block_template(); + m_miner.resume(); + + + CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed, false, "mined block failed verification"); + if(bvc.m_added_to_main_chain) + { + cryptonote_connection_context exclude_context = boost::value_initialized<cryptonote_connection_context>(); + NOTIFY_NEW_BLOCK::request arg = AUTO_VAL_INIT(arg); + arg.hop = 0; + arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height(); + std::list<crypto::hash> missed_txs; + std::list<transaction> txs; + m_blockchain_storage.get_transactions(b.tx_hashes, txs, missed_txs); + if(missed_txs.size() && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b)) + { + LOG_PRINT_L0("Block found but, seems that reorganize just happened after that, do not relay this block"); + return true; + } + CHECK_AND_ASSERT_MES(txs.size() == b.tx_hashes.size() && !missed_txs.size(), false, "cant find some transactions in found block:" << get_block_hash(b) << " txs.size()=" << txs.size() + << ", b.tx_hashes.size()=" << b.tx_hashes.size() << ", missed_txs.size()" << missed_txs.size()); + + block_to_blob(b, arg.b.block); + //pack transactions + BOOST_FOREACH(auto& tx, txs) + arg.b.txs.push_back(t_serializable_object_to_blob(tx)); + + m_pprotocol->relay_block(arg, exclude_context); + } + return bvc.m_added_to_main_chain; + } + //----------------------------------------------------------------------------------------------- + void core::on_synchronized() + { + m_miner.on_synchronized(); + } + bool core::get_backward_blocks_sizes(uint64_t from_height, std::vector<size_t>& sizes, size_t count) + { + return m_blockchain_storage.get_backward_blocks_sizes(from_height, sizes, count); + } + //----------------------------------------------------------------------------------------------- + bool core::add_new_block(const block& b, block_verification_context& bvc) + { + return m_blockchain_storage.add_new_block(b, bvc); + } + //----------------------------------------------------------------------------------------------- + bool core::handle_incoming_block(const blobdata& block_blob, block_verification_context& bvc, bool update_miner_blocktemplate) + { + bvc = boost::value_initialized<block_verification_context>(); + if(block_blob.size() > get_max_block_size()) + { + LOG_PRINT_L0("WRONG BLOCK BLOB, too big size " << block_blob.size() << ", rejected"); + bvc.m_verifivation_failed = true; + return false; + } + + + block b = AUTO_VAL_INIT(b); + if(!parse_and_validate_block_from_blob(block_blob, b)) + { + LOG_PRINT_L0("Failed to parse and validate new block"); + bvc.m_verifivation_failed = true; + return false; + } + add_new_block(b, bvc); + if(update_miner_blocktemplate && bvc.m_added_to_main_chain) + update_miner_block_template(); + return true; + } + //----------------------------------------------------------------------------------------------- + crypto::hash core::get_tail_id() + { + return m_blockchain_storage.get_tail_id(); + } + //----------------------------------------------------------------------------------------------- + size_t core::get_pool_transactions_count() + { + return m_mempool.get_transactions_count(); + } + //----------------------------------------------------------------------------------------------- + bool core::have_block(const crypto::hash& id) + { + return m_blockchain_storage.have_block(id); + } + //----------------------------------------------------------------------------------------------- + bool core::parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob) + { + return parse_and_validate_tx_from_blob(blob, tx, tx_hash, tx_prefix_hash); + } + //----------------------------------------------------------------------------------------------- + bool core::check_tx_syntax(const transaction& tx) + { + return true; + } + //----------------------------------------------------------------------------------------------- + bool core::get_pool_transactions(std::list<transaction>& txs) + { + return m_mempool.get_transactions(txs); + } + //----------------------------------------------------------------------------------------------- + bool core::get_short_chain_history(std::list<crypto::hash>& ids) + { + return m_blockchain_storage.get_short_chain_history(ids); + } + //----------------------------------------------------------------------------------------------- + bool core::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp, cryptonote_connection_context& context) + { + return m_blockchain_storage.handle_get_objects(arg, rsp); + } + //----------------------------------------------------------------------------------------------- + crypto::hash core::get_block_id_by_height(uint64_t height) + { + return m_blockchain_storage.get_block_id_by_height(height); + } + //----------------------------------------------------------------------------------------------- + bool core::get_block_by_hash(const crypto::hash &h, block &blk) { + return m_blockchain_storage.get_block_by_hash(h, blk); + } + //----------------------------------------------------------------------------------------------- + void core::get_all_known_block_ids(std::list<crypto::hash> &main, std::list<crypto::hash> &alt, std::list<crypto::hash> &invalid) { + m_blockchain_storage.get_all_known_block_ids(main, alt, invalid); + } + //----------------------------------------------------------------------------------------------- + std::string core::print_pool(bool short_format) + { + return m_mempool.print_pool(short_format); + } + //----------------------------------------------------------------------------------------------- + bool core::update_miner_block_template() + { + m_miner.on_block_chain_update(); + return true; + } + //----------------------------------------------------------------------------------------------- + bool core::on_idle() + { + if(!m_starter_message_showed) + { + 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 + << ENDL + << "Use \"help\" command to see the list of available commands." << ENDL + << ENDL + << "Note: in case you need to interrupt the process, use \"exit\" command. Otherwise, the current progress won't be saved." << ENDL + << "**********************************************************************"); + m_starter_message_showed = true; + } + + m_store_blockchain_interval.do_call(boost::bind(&blockchain_storage::store_blockchain, &m_blockchain_storage)); + m_miner.on_idle(); + return true; + } + //----------------------------------------------------------------------------------------------- +} diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h new file mode 100644 index 000000000..c298451e8 --- /dev/null +++ b/src/cryptonote_core/cryptonote_core.h @@ -0,0 +1,130 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/variables_map.hpp> + +#include "p2p/net_node_common.h" +#include "cryptonote_protocol/cryptonote_protocol_handler_common.h" +#include "storages/portable_storage_template_helper.h" +#include "tx_pool.h" +#include "blockchain_storage.h" +#include "miner.h" +#include "connection_context.h" +#include "cryptonote_core/cryptonote_stat_info.h" +#include "warnings.h" +#include "crypto/hash.h" + +PUSH_WARNINGS +DISABLE_VS_WARNINGS(4355) + +namespace cryptonote +{ + /************************************************************************/ + /* */ + /************************************************************************/ + class core: public i_miner_handler + { + public: + core(i_cryptonote_protocol* pprotocol); + bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp, cryptonote_connection_context& context); + bool on_idle(); + bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block); + bool handle_incoming_block(const blobdata& block_blob, block_verification_context& bvc, bool update_miner_blocktemplate = true); + i_cryptonote_protocol* get_protocol(){return m_pprotocol;} + + //-------------------- i_miner_handler ----------------------- + virtual bool handle_block_found( block& b); + virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce); + + + miner& get_miner(){return m_miner;} + static void init_options(boost::program_options::options_description& desc); + bool init(const boost::program_options::variables_map& vm); + bool set_genesis_block(const block& b); + bool deinit(); + uint64_t get_current_blockchain_height(); + bool get_blockchain_top(uint64_t& heeight, crypto::hash& top_id); + bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs); + bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks); + template<class t_ids_container, class t_blocks_container, class t_missed_container> + bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) + { + return m_blockchain_storage.get_blocks(block_ids, blocks, missed_bs); + } + crypto::hash get_block_id_by_height(uint64_t height); + bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& missed_txs); + bool get_transaction(const crypto::hash &h, transaction &tx); + bool get_block_by_hash(const crypto::hash &h, block &blk); + void get_all_known_block_ids(std::list<crypto::hash> &main, std::list<crypto::hash> &alt, std::list<crypto::hash> &invalid); + + bool get_alternative_blocks(std::list<block>& blocks); + size_t get_alternative_blocks_count(); + + void set_cryptonote_protocol(i_cryptonote_protocol* pprotocol); + void set_checkpoints(checkpoints&& chk_pts); + + bool get_pool_transactions(std::list<transaction>& txs); + size_t get_pool_transactions_count(); + size_t get_blockchain_total_transactions(); + bool get_outs(uint64_t amount, std::list<crypto::public_key>& pkeys); + bool have_block(const crypto::hash& id); + bool get_short_chain_history(std::list<crypto::hash>& ids); + bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp); + bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); + bool get_stat_info(core_stat_info& st_inf); + bool get_backward_blocks_sizes(uint64_t from_height, std::vector<size_t>& sizes, size_t count); + bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs); + crypto::hash get_tail_id(); + bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res); + void pause_mine(); + void resume_mine(); + blockchain_storage& get_blockchain_storage(){return m_blockchain_storage;} + //debug functions + void print_blockchain(uint64_t start_index, uint64_t end_index); + void print_blockchain_index(); + std::string print_pool(bool short_format); + void print_blockchain_outs(const std::string& file); + void on_synchronized(); + + 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); + bool add_new_block(const block& b, block_verification_context& bvc); + bool load_state_data(); + bool parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob); + + bool check_tx_syntax(const transaction& tx); + //check correct values, amounts and all lightweight checks not related with database + bool check_tx_semantic(const transaction& tx, bool keeped_by_block); + //check if tx already in memory pool or in main blockchain + + bool is_key_image_spent(const crypto::key_image& key_im); + + bool check_tx_ring_signature(const txin_to_key& tx, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig); + bool is_tx_spendtime_unlocked(uint64_t unlock_time); + bool update_miner_block_template(); + bool handle_command_line(const boost::program_options::variables_map& vm); + bool on_update_blocktemplate_interval(); + bool check_tx_inputs_keyimages_diff(const transaction& tx); + + + tx_memory_pool m_mempool; + blockchain_storage m_blockchain_storage; + i_cryptonote_protocol* m_pprotocol; + 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; + friend class tx_validate_inputs; + std::atomic<bool> m_starter_message_showed; + }; +} + +POP_WARNINGS diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp new file mode 100644 index 000000000..fbcadc081 --- /dev/null +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -0,0 +1,710 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "include_base_utils.h" +using namespace epee; + +#include "cryptonote_format_utils.h" +#include <boost/foreach.hpp> +#include "cryptonote_config.h" +#include "miner.h" +#include "crypto/crypto.h" +#include "crypto/hash.h" + +namespace cryptonote +{ + //--------------------------------------------------------------- + void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h) + { + std::ostringstream s; + binary_archive<true> a(s); + ::serialization::serialize(a, const_cast<transaction_prefix&>(tx)); + crypto::cn_fast_hash(s.str().data(), s.str().size(), h); + } + //--------------------------------------------------------------- + crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx) + { + crypto::hash h = null_hash; + get_transaction_prefix_hash(tx, h); + return h; + } + //--------------------------------------------------------------- + bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx) + { + std::stringstream ss; + ss << tx_blob; + binary_archive<false> ba(ss); + bool r = ::serialization::serialize(ba, tx); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); + return true; + } + //--------------------------------------------------------------- + bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash) + { + std::stringstream ss; + ss << tx_blob; + binary_archive<false> ba(ss); + bool r = ::serialization::serialize(ba, tx); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); + //TODO: validate tx + + crypto::cn_fast_hash(tx_blob.data(), tx_blob.size(), tx_hash); + get_transaction_prefix_hash(tx, tx_prefix_hash); + return true; + } + //--------------------------------------------------------------- + bool construct_miner_tx(uint64_t height, uint64_t already_generated_coins, const account_public_address& miner_address, transaction& tx, uint64_t fee, std::vector<size_t>& blocks_sizes, size_t current_block_size, size_t max_outs) + { + return construct_miner_tx(height, already_generated_coins, miner_address, tx, fee, blocks_sizes, current_block_size, blobdata(), max_outs); + } + //--------------------------------------------------------------- + bool construct_miner_tx(uint64_t height, uint64_t already_generated_coins, const account_public_address& miner_address, transaction& tx, uint64_t fee, std::vector<size_t>& blocks_sizes, size_t current_block_size, const blobdata& extra_nonce, size_t max_outs) + { + tx.vin.clear(); + tx.vout.clear(); + tx.extra.clear(); + + keypair txkey = keypair::generate(); + add_tx_pub_key_to_extra(tx, txkey.pub); + if(extra_nonce.size()) + if(!add_tx_extra_nonce(tx, extra_nonce)) + return false; + + txin_gen in; + in.height = height; + + bool block_too_big = false; + uint64_t block_reward = get_block_reward(blocks_sizes, current_block_size, block_too_big, already_generated_coins) + fee; + if(block_too_big) + { + LOG_PRINT_L0("Block is too big"); + return false; + } + + std::vector<size_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); }); + + CHECK_AND_ASSERT_MES(1 <= max_outs, false, "max_out must be non-zero"); + while (max_outs < out_amounts.size()) + { + out_amounts[out_amounts.size() - 2] += out_amounts.back(); + out_amounts.resize(out_amounts.size() - 1); + } + + size_t summary_amounts = 0; + for (size_t no = 0; no < out_amounts.size(); no++) + { + crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);; + crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key); + bool r = crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation); + CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", " << txkey.sec << ")"); + + r = crypto::derive_public_key(derivation, no, miner_address.m_spend_public_key, out_eph_public_key); + CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << no << ", "<< miner_address.m_spend_public_key << ")"); + + txout_to_key tk; + tk.key = out_eph_public_key; + + tx_out out; + summary_amounts += out.amount = out_amounts[no]; + out.target = tk; + tx.vout.push_back(out); + } + + CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward); + + tx.version = CURRENT_TRANSACTION_VERSION; + //lock + tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; + tx.vin.push_back(in); + //LOG_PRINT("MINER_TX generated ok, block_reward=" << print_money(block_reward) << "(" << print_money(block_reward - fee) << "+" << print_money(fee) + // << "), current_block_size=" << current_block_size << ", already_generated_coins=" << already_generated_coins << ", tx_id=" << get_transaction_hash(tx), LOG_LEVEL_2); + return true; + } + //--------------------------------------------------------------- + bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki) + { + crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation); + bool r = crypto::generate_key_derivation(tx_public_key, ack.m_view_secret_key, recv_derivation); + CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")"); + + r = crypto::derive_public_key(recv_derivation, real_output_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub); + CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to derive_public_key(" << recv_derivation << ", " << real_output_index << ", " << ack.m_account_address.m_spend_public_key << ")"); + + crypto::derive_secret_key(recv_derivation, real_output_index, ack.m_spend_secret_key, in_ephemeral.sec); + + crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki); + return true; + } + //--------------------------------------------------------------- + uint64_t power_integral(uint64_t a, uint64_t b) + { + if(b == 0) + return 1; + uint64_t total = a; + for(uint64_t i = 1; i != b; i++) + total *= a; + return total; + } + //--------------------------------------------------------------- + bool parse_amount(uint64_t& amount, const std::string& str_amount_) + { + std::vector<std::string> pars; + std::string str_amount = str_amount_; + boost::algorithm::trim(str_amount); + if(!str_amount.size()) + return false; + if(str_amount[0] == '-') + return false; + + boost::split(pars, str_amount, boost::is_any_of("."), boost::token_compress_on ); + if(pars.size() > 2 || pars.size() < 1) + return false; + uint64_t left = 0; + if(!string_tools::get_xtype_from_string(left, pars[0])) + return false; + amount = left * power_integral(10, CRYPTONOTE_DISPLAY_DECIMAL_POINT); + if(pars.size() == 2) + { + uint64_t right = 0; + if(pars[1].size() > 8 ) + return false; + if(pars[1].size() < 8 ) + pars[1].append(8 - pars[1].size(), '0'); + if(!string_tools::get_xtype_from_string(right, pars[1])) + return false; + amount += right; + } + return true; + } + //--------------------------------------------------------------- + bool get_tx_fee(const transaction& tx, uint64_t & fee) + { + uint64_t amount_in = 0; + uint64_t amount_out = 0; + BOOST_FOREACH(auto& in, tx.vin) + { + CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key), 0, "unexpected type id in transaction"); + amount_in += boost::get<txin_to_key>(in).amount; + } + BOOST_FOREACH(auto& o, tx.vout) + amount_out += o.amount; + + CHECK_AND_ASSERT_MES(amount_in >= amount_out, false, "transaction spend (" <<amount_in << ") more than it has (" << amount_out << ")"); + fee = amount_in - amount_out; + return true; + } + //--------------------------------------------------------------- + uint64_t get_tx_fee(const transaction& tx) + { + uint64_t r = 0; + if(!get_tx_fee(tx, r)) + return 0; + return r; + } + //--------------------------------------------------------------- + crypto::public_key get_tx_pub_key_from_extra(const transaction& tx) + { + crypto::public_key pk = null_pkey; + parse_and_validate_tx_extra(tx, pk); + return pk; + } + //--------------------------------------------------------------- + bool parse_and_validate_tx_extra(const transaction& tx, crypto::public_key& tx_pub_key) + { + tx_pub_key = null_pkey; + bool padding_started = false; //let the padding goes only at the end + bool tx_extra_tag_pubkey_found = false; + bool tx_extra_extra_nonce_found = false; + for(size_t i = 0; i != tx.extra.size();) + { + if(padding_started) + { + CHECK_AND_ASSERT_MES(!tx.extra[i], false, "Failed to parse transaction extra (not 0 after padding) in tx " << get_transaction_hash(tx)); + } + else if(tx.extra[i] == TX_EXTRA_TAG_PUBKEY) + { + CHECK_AND_ASSERT_MES(sizeof(crypto::public_key) <= tx.extra.size()-1-i, false, "Failed to parse transaction extra (TX_EXTRA_TAG_PUBKEY have not enough bytes) in tx " << get_transaction_hash(tx)); + CHECK_AND_ASSERT_MES(!tx_extra_tag_pubkey_found, false, "Failed to parse transaction extra (duplicate TX_EXTRA_TAG_PUBKEY entry) in tx " << get_transaction_hash(tx)); + tx_pub_key = *reinterpret_cast<const crypto::public_key*>(&tx.extra[i+1]); + i += 1 + sizeof(crypto::public_key); + tx_extra_tag_pubkey_found = true; + continue; + }else if(tx.extra[i] == TX_EXTRA_NONCE) + { + //CHECK_AND_ASSERT_MES(is_coinbase(tx), false, "Failed to parse transaction extra (TX_EXTRA_NONCE can be only in coinbase) in tx " << get_transaction_hash(tx)); + CHECK_AND_ASSERT_MES(!tx_extra_extra_nonce_found, false, "Failed to parse transaction extra (duplicate TX_EXTRA_NONCE entry) in tx " << get_transaction_hash(tx)); + CHECK_AND_ASSERT_MES(tx.extra.size()-1-i >= 1, false, "Failed to parse transaction extra (TX_EXTRA_NONCE have not enough bytes) in tx " << get_transaction_hash(tx)); + ++i; + CHECK_AND_ASSERT_MES(tx.extra.size()-1-i >= tx.extra[i], false, "Failed to parse transaction extra (TX_EXTRA_NONCE have wrong bytes counter) in tx " << get_transaction_hash(tx)); + tx_extra_extra_nonce_found = true; + i += tx.extra[i];//actually don't need to extract it now, just skip + } + else if(!tx.extra[i]) + { + padding_started = true; + continue; + } + ++i; + } + return true; + } + //--------------------------------------------------------------- + bool add_tx_pub_key_to_extra(transaction& tx, const crypto::public_key& tx_pub_key) + { + tx.extra.resize(tx.extra.size() + 1 + sizeof(crypto::public_key)); + tx.extra[tx.extra.size() - 1 - sizeof(crypto::public_key)] = TX_EXTRA_TAG_PUBKEY; + *reinterpret_cast<crypto::public_key*>(&tx.extra[tx.extra.size() - sizeof(crypto::public_key)]) = tx_pub_key; + return true; + } + //--------------------------------------------------------------- + bool add_tx_extra_nonce(transaction& tx, const blobdata& extra_nonce) + { + CHECK_AND_ASSERT_MES(extra_nonce.size() <=255, false, "extra nonce could be 255 bytes max"); + size_t start_pos = tx.extra.size(); + tx.extra.resize(tx.extra.size() + 2 + extra_nonce.size()); + //write tag + tx.extra[start_pos] = TX_EXTRA_NONCE; + //write len + ++start_pos; + tx.extra[start_pos] = static_cast<uint8_t>(extra_nonce.size()); + //write data + ++start_pos; + memcpy(&tx.extra[start_pos], extra_nonce.data(), extra_nonce.size()); + return true; + } + //--------------------------------------------------------------- + bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, transaction& tx, uint64_t unlock_time) + { + tx.vin.clear(); + tx.vout.clear(); + tx.signatures.clear(); + tx.extra.clear(); + + tx.version = CURRENT_TRANSACTION_VERSION; + tx.unlock_time = unlock_time; + + keypair txkey = keypair::generate(); + add_tx_pub_key_to_extra(tx, txkey.pub); + + struct input_generation_context_data + { + keypair in_ephemeral; + }; + std::vector<input_generation_context_data> in_contexts; + + + uint64_t summary_inputs_money = 0; + //fill inputs + BOOST_FOREACH(const tx_source_entry& src_entr, sources) + { + if(src_entr.real_output >= src_entr.outputs.size()) + { + LOG_ERROR("real_output index (" << src_entr.real_output << ")bigger than output_keys.size()=" << src_entr.outputs.size()); + return false; + } + summary_inputs_money += src_entr.amount; + + //key_derivation recv_derivation; + in_contexts.push_back(input_generation_context_data()); + keypair& in_ephemeral = in_contexts.back().in_ephemeral; + crypto::key_image img; + if(!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img)) + return false; + + //check that derivated key is equal with real output key + if( !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second) ) + { + LOG_ERROR("derived public key missmatch with output public key! "<< ENDL << "derived_key:" + << string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:" + << string_tools::pod_to_hex(src_entr.outputs[src_entr.real_output].second) ); + return false; + } + + //put key image into tx input + txin_to_key input_to_key; + input_to_key.amount = src_entr.amount; + input_to_key.k_image = img; + + //fill outputs array and use relative offsets + BOOST_FOREACH(const tx_source_entry::output_entry& out_entry, src_entr.outputs) + input_to_key.key_offsets.push_back(out_entry.first); + + input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets); + tx.vin.push_back(input_to_key); + } + + // "Shuffle" outs + std::vector<tx_destination_entry> shuffled_dsts(destinations); + std::sort(shuffled_dsts.begin(), shuffled_dsts.end(), [](const tx_destination_entry& de1, const tx_destination_entry& de2) { return de1.amount < de2.amount; } ); + + uint64_t summary_outs_money = 0; + //fill outputs + size_t output_index = 0; + BOOST_FOREACH(const tx_destination_entry& dst_entr, shuffled_dsts) + { + CHECK_AND_ASSERT_MES(dst_entr.amount > 0, false, "Destination with wrong amount: " << dst_entr.amount); + crypto::key_derivation derivation; + crypto::public_key out_eph_public_key; + bool r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, txkey.sec, derivation); + CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << txkey.sec << ")"); + + r = crypto::derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key); + CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< dst_entr.addr.m_spend_public_key << ")"); + + tx_out out; + out.amount = dst_entr.amount; + txout_to_key tk; + tk.key = out_eph_public_key; + out.target = tk; + tx.vout.push_back(out); + output_index++; + summary_outs_money += dst_entr.amount; + } + + //check money + if(summary_outs_money > summary_inputs_money ) + { + LOG_ERROR("Transaction inputs money ("<< summary_inputs_money << ") less than outputs money (" << summary_outs_money << ")"); + return false; + } + + + //generate ring signatures + crypto::hash tx_prefix_hash; + get_transaction_prefix_hash(tx, tx_prefix_hash); + + std::stringstream ss_ring_s; + size_t i = 0; + BOOST_FOREACH(const tx_source_entry& src_entr, sources) + { + ss_ring_s << "pub_keys:" << ENDL; + std::vector<const crypto::public_key*> keys_ptrs; + BOOST_FOREACH(const tx_source_entry::output_entry& o, src_entr.outputs) + { + keys_ptrs.push_back(&o.second); + ss_ring_s << o.second << ENDL; + } + + tx.signatures.push_back(std::vector<crypto::signature>()); + std::vector<crypto::signature>& sigs = tx.signatures.back(); + sigs.resize(src_entr.outputs.size()); + crypto::generate_ring_signature(tx_prefix_hash, boost::get<txin_to_key>(tx.vin[i]).k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data()); + ss_ring_s << "signatures:" << ENDL; + std::for_each(sigs.begin(), sigs.end(), [&](const crypto::signature& s){ss_ring_s << s << ENDL;}); + ss_ring_s << "prefix_hash:" << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[i].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output; + i++; + } + + LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str() , LOG_LEVEL_3); + + return true; + } + //--------------------------------------------------------------- + bool get_inputs_money_amount(const transaction& tx, uint64_t& money) + { + money = 0; + BOOST_FOREACH(const auto& in, tx.vin) + { + CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false); + money += tokey_in.amount; + } + return true; + } + //--------------------------------------------------------------- + uint64_t get_block_height(const block& b) + { + CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 1, 0, "wrong miner tx in block: " << get_block_hash(b) << ", b.miner_tx.vin.size() != 1"); + CHECKED_GET_SPECIFIC_VARIANT(b.miner_tx.vin[0], const txin_gen, coinbase_in, 0); + return coinbase_in.height; + } + //--------------------------------------------------------------- + bool check_inputs_types_supported(const transaction& tx) + { + BOOST_FOREACH(const auto& in, tx.vin) + { + CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key), false, "wrong variant type: " + << in.type().name() << ", expected " << typeid(txin_to_key).name() + << ", in transaction id=" << get_transaction_hash(tx)); + + } + return true; + } + //----------------------------------------------------------------------------------------------- + bool check_outs_valid(const transaction& tx) + { + BOOST_FOREACH(const tx_out& out, tx.vout) + { + CHECK_AND_ASSERT_MES(out.target.type() == typeid(txout_to_key), false, "wrong variant type: " + << out.target.type().name() << ", expected " << typeid(txout_to_key).name() + << ", in transaction id=" << get_transaction_hash(tx)); + + CHECK_AND_NO_ASSERT_MES(0 < out.amount, false, "zero amount ouput in transaction id=" << get_transaction_hash(tx)); + + if(!check_key(boost::get<txout_to_key>(out.target).key)) + return false; + } + return true; + } + //----------------------------------------------------------------------------------------------- + bool check_money_overflow(const transaction& tx) + { + return check_inputs_overflow(tx) && check_outs_overflow(tx); + } + //--------------------------------------------------------------- + bool check_inputs_overflow(const transaction& tx) + { + uint64_t money = 0; + BOOST_FOREACH(const auto& in, tx.vin) + { + CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false); + if(money > tokey_in.amount + money) + return false; + money += tokey_in.amount; + } + return true; + } + //--------------------------------------------------------------- + bool check_outs_overflow(const transaction& tx) + { + uint64_t money = 0; + BOOST_FOREACH(const auto& o, tx.vout) + { + if(money > o.amount + money) + return false; + money += o.amount; + } + return true; + } + //--------------------------------------------------------------- + uint64_t get_outs_money_amount(const transaction& tx) + { + uint64_t outputs_amount = 0; + BOOST_FOREACH(const auto& o, tx.vout) + outputs_amount += o.amount; + return outputs_amount; + } + //--------------------------------------------------------------- + std::string short_hash_str(const crypto::hash& h) + { + std::string res = string_tools::pod_to_hex(h); + CHECK_AND_ASSERT_MES(res.size() == 64, res, "wrong hash256 with string_tools::pod_to_hex conversion"); + auto erased_pos = res.erase(8, 48); + res.insert(8, "...."); + return res; + } + //--------------------------------------------------------------- + bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, size_t output_index) + { + crypto::key_derivation derivation; + generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); + crypto::public_key pk; + derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk); + return pk == out_key.key; + } + //--------------------------------------------------------------- + bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector<size_t>& outs, uint64_t& money_transfered) + { + return lookup_acc_outs(acc, tx, get_tx_pub_key_from_extra(tx), outs, money_transfered); + } + //--------------------------------------------------------------- + bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector<size_t>& outs, uint64_t& money_transfered) + { + money_transfered = 0; + size_t i = 0; + BOOST_FOREACH(const tx_out& o, tx.vout) + { + CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key), false, "wrong type id in transaction out" ); + if(is_out_to_acc(acc, boost::get<txout_to_key>(o.target), tx_pub_key, i)) + { + outs.push_back(i); + money_transfered += o.amount; + } + i++; + } + return true; + } + //--------------------------------------------------------------- + void get_blob_hash(const blobdata& blob, crypto::hash& res) + { + cn_fast_hash(blob.data(), blob.size(), res); + } + //--------------------------------------------------------------- + std::string print_money(uint64_t amount) + { + std::string s = std::to_string(amount); + if(s.size() < CRYPTONOTE_DISPLAY_DECIMAL_POINT+1) + { + s.insert(0, CRYPTONOTE_DISPLAY_DECIMAL_POINT+1 - s.size(), '0'); + } + s.insert(s.size() - CRYPTONOTE_DISPLAY_DECIMAL_POINT, "."); + return s; + } + //--------------------------------------------------------------- + crypto::hash get_blob_hash(const blobdata& blob) + { + crypto::hash h = null_hash; + get_blob_hash(blob, h); + return h; + } + //--------------------------------------------------------------- + crypto::hash get_transaction_hash(const transaction& t) + { + crypto::hash h = null_hash; + size_t blob_size = 0; + get_object_hash(t, h, blob_size); + return h; + } + //--------------------------------------------------------------- + bool get_transaction_hash(const transaction& t, crypto::hash& res) + { + size_t blob_size = 0; + return get_object_hash(t, res, blob_size); + } + //--------------------------------------------------------------- + bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size) + { + return get_object_hash(t, res, blob_size); + } + //--------------------------------------------------------------- + blobdata get_block_hashing_blob(const block& b) + { + blobdata blob = t_serializable_object_to_blob(static_cast<block_header>(b)); + crypto::hash tree_root_hash = get_tx_tree_hash(b); + blob.append((const char*)&tree_root_hash, sizeof(tree_root_hash )); + blob.append(tools::get_varint_data(b.tx_hashes.size()+1)); + return blob; + } + //--------------------------------------------------------------- + bool get_block_hash(const block& b, crypto::hash& res) + { + return get_object_hash(get_block_hashing_blob(b), res); + } + //--------------------------------------------------------------- + crypto::hash get_block_hash(const block& b) + { + crypto::hash p = null_hash; + get_block_hash(b, p); + return p; + } + //--------------------------------------------------------------- + bool generate_genesis_block(block& bl) + { + //genesis block + bl = boost::value_initialized<block>(); + + + account_public_address ac = boost::value_initialized<account_public_address>(); + std::vector<size_t> sz; + construct_miner_tx(0, 0, ac, bl.miner_tx, 0, sz, 0, 1); // zero fee in genesis + blobdata txb = tx_to_blob(bl.miner_tx); + std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb); + + //hard code coinbase tx in genesis block, because "tru" generating tx use random, but genesis should be always the same + std::string genesis_coinbase_tx_hex = "010a01ff0001ffffffffffff0f029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121013c086a48c15fb637a96991bc6d53caf77068b5ba6eeb3c82357228c49790584a"; + + blobdata tx_bl; + string_tools::parse_hexstr_to_binbuff(genesis_coinbase_tx_hex, tx_bl); + bool r = parse_and_validate_tx_from_blob(tx_bl, bl.miner_tx); + CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob"); + bl.major_version = CURRENT_BLOCK_MAJOR_VERSION; + bl.minor_version = CURRENT_BLOCK_MINOR_VERSION; + bl.timestamp = 0; + bl.nonce = 70; + miner::find_nonce_for_given_block(bl, 1, 0); + return true; + } + //--------------------------------------------------------------- + bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height) + { + block b_local = b; //workaround to avoid const errors with do_serialize + blobdata bd = get_block_hashing_blob(b); + crypto::cn_slow_hash(bd.data(), bd.size(), res); + return true; + } + //--------------------------------------------------------------- + std::vector<uint64_t> relative_output_offsets_to_absolute(const std::vector<uint64_t>& off) + { + std::vector<uint64_t> res = off; + for(size_t i = 1; i < res.size(); i++) + res[i] += res[i-1]; + return res; + } + //--------------------------------------------------------------- + std::vector<uint64_t> absolute_output_offsets_to_relative(const std::vector<uint64_t>& off) + { + std::vector<uint64_t> res = off; + if(!off.size()) + return res; + std::sort(res.begin(), res.end());//just to be sure, actually it is already should be sorted + for(size_t i = res.size()-1; i != 0; i--) + res[i] -= res[i-1]; + + return res; + } + //--------------------------------------------------------------- + crypto::hash get_block_longhash(const block& b, uint64_t height) + { + crypto::hash p = null_hash; + get_block_longhash(b, p, height); + return p; + } + //--------------------------------------------------------------- + bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b) + { + std::stringstream ss; + ss << b_blob; + binary_archive<false> ba(ss); + bool r = ::serialization::serialize(ba, b); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse block from blob"); + return true; + } + //--------------------------------------------------------------- + blobdata block_to_blob(const block& b) + { + return t_serializable_object_to_blob(b); + } + //--------------------------------------------------------------- + bool block_to_blob(const block& b, blobdata& b_blob) + { + return t_serializable_object_to_blob(b, b_blob); + } + //--------------------------------------------------------------- + blobdata tx_to_blob(const transaction& tx) + { + return t_serializable_object_to_blob(tx); + } + //--------------------------------------------------------------- + bool tx_to_blob(const transaction& tx, blobdata& b_blob) + { + return t_serializable_object_to_blob(tx, b_blob); + } + //--------------------------------------------------------------- + void get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes, crypto::hash& h) + { + tree_hash(tx_hashes.data(), tx_hashes.size(), h); + } + //--------------------------------------------------------------- + crypto::hash get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes) + { + crypto::hash h = null_hash; + get_tx_tree_hash(tx_hashes, h); + return h; + } + //--------------------------------------------------------------- + crypto::hash get_tx_tree_hash(const block& b) + { + std::vector<crypto::hash> txs_ids; + crypto::hash h = null_hash; + size_t bl_sz = 0; + get_transaction_hash(b.miner_tx, h, bl_sz); + txs_ids.push_back(h); + BOOST_FOREACH(auto& th, b.tx_hashes) + txs_ids.push_back(th); + return get_tx_tree_hash(txs_ids); + } + //--------------------------------------------------------------- +} diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h new file mode 100644 index 000000000..1c50832b6 --- /dev/null +++ b/src/cryptonote_core/cryptonote_format_utils.h @@ -0,0 +1,191 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include "cryptonote_protocol/cryptonote_protocol_defs.h" +#include "cryptonote_core/cryptonote_basic_impl.h" +#include "account.h" +#include "include_base_utils.h" +#include "crypto/crypto.h" +#include "crypto/hash.h" + + +namespace cryptonote +{ + //--------------------------------------------------------------- + void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h); + crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx); + bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash); + bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx); + bool construct_miner_tx(uint64_t height, uint64_t already_generated_coins, const account_public_address& miner_address, transaction& tx, uint64_t fee, std::vector<size_t>& blocks_sizes, size_t current_block_size, const blobdata& extra_nonce, size_t max_outs = 1); + bool construct_miner_tx(uint64_t height, uint64_t already_generated_coins, const account_public_address& miner_address, transaction& tx, uint64_t fee, std::vector<size_t>& blocks_sizes, size_t current_block_size, size_t max_outs = 1); + + struct tx_source_entry + { + typedef std::pair<uint64_t, crypto::public_key> output_entry; + + std::vector<output_entry> outputs; //index + key + uint64_t real_output; //index in outputs vector of real output_entry + crypto::public_key real_out_tx_key; //incoming real tx public key + size_t real_output_in_tx_index; //index in transaction outputs vector + uint64_t amount; //money + }; + + struct tx_destination_entry + { + uint64_t amount; //money + account_public_address addr; //destination address + + tx_destination_entry() { } + tx_destination_entry(uint64_t a, const account_public_address &ad) : amount(a), addr(ad) { } + }; + + //--------------------------------------------------------------- + bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, transaction& tx, uint64_t unlock_time); + bool parse_and_validate_tx_extra(const transaction& tx, crypto::public_key& tx_pub_key); + crypto::public_key get_tx_pub_key_from_extra(const transaction& tx); + bool add_tx_pub_key_to_extra(transaction& tx, const crypto::public_key& tx_pub_key); + bool add_tx_extra_nonce(transaction& tx, const blobdata& extra_nonce); + bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, size_t output_index); + bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector<size_t>& outs, uint64_t& money_transfered); + bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector<size_t>& outs, uint64_t& money_transfered); + bool get_tx_fee(const transaction& tx, uint64_t & fee); + uint64_t get_tx_fee(const transaction& tx); + bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki); + void get_blob_hash(const blobdata& blob, crypto::hash& res); + crypto::hash get_blob_hash(const blobdata& blob); + std::string short_hash_str(const crypto::hash& h); + + crypto::hash get_transaction_hash(const transaction& t); + bool get_transaction_hash(const transaction& t, crypto::hash& res); + bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size); + blobdata get_block_hashing_blob(const block& b); + bool get_block_hash(const block& b, crypto::hash& res); + crypto::hash get_block_hash(const block& b); + bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height); + crypto::hash get_block_longhash(const block& b, uint64_t height); + bool generate_genesis_block(block& bl); + bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b); + bool get_inputs_money_amount(const transaction& tx, uint64_t& money); + uint64_t get_outs_money_amount(const transaction& tx); + bool check_inputs_types_supported(const transaction& tx); + bool check_outs_valid(const transaction& tx); + blobdata get_block_hashing_blob(const block& b); + bool parse_amount(uint64_t& amount, const std::string& str_amount); + + bool check_money_overflow(const transaction& tx); + bool check_outs_overflow(const transaction& tx); + bool check_inputs_overflow(const transaction& tx); + uint64_t get_block_height(const block& b); + std::vector<uint64_t> relative_output_offsets_to_absolute(const std::vector<uint64_t>& off); + std::vector<uint64_t> absolute_output_offsets_to_relative(const std::vector<uint64_t>& off); + std::string print_money(uint64_t amount); + //--------------------------------------------------------------- + template<class t_object> + bool t_serializable_object_to_blob(const t_object& to, blobdata& b_blob) + { + std::stringstream ss; + binary_archive<true> ba(ss); + bool r = ::serialization::serialize(ba, const_cast<t_object&>(to)); + b_blob = ss.str(); + return r; + } + //--------------------------------------------------------------- + template<class t_object> + blobdata t_serializable_object_to_blob(const t_object& to) + { + blobdata b; + t_serializable_object_to_blob(to, b); + return b; + } + //--------------------------------------------------------------- + template<class t_object> + bool get_object_hash(const t_object& o, crypto::hash& res) + { + get_blob_hash(t_serializable_object_to_blob(o), res); + return true; + } + //--------------------------------------------------------------- + template<class t_object> + size_t get_object_blobsize(const t_object& o) + { + blobdata b = t_serializable_object_to_blob(o); + return b.size(); + } + //--------------------------------------------------------------- + template<class t_object> + bool get_object_hash(const t_object& o, crypto::hash& res, size_t& blob_size) + { + blobdata bl = t_serializable_object_to_blob(o); + blob_size = bl.size(); + get_blob_hash(bl, res); + return true; + } + //--------------------------------------------------------------- + template <typename T> + std::string obj_to_json_str(T& obj) + { + std::stringstream ss; + json_archive<true> ar(ss, true); + bool r = ::serialization::serialize(ar, obj); + CHECK_AND_ASSERT_MES(r, "", "obj_to_json_str failed: serialization::serialize returned false"); + return ss.str(); + } + //--------------------------------------------------------------- + // 62387455827 -> 455827 + 7000000 + 80000000 + 300000000 + 2000000000 + 60000000000, where 455827 <= dust_threshold + template<typename chunk_handler_t, typename dust_handler_t> + void decompose_amount_into_digits(uint64_t amount, uint64_t dust_threshold, const chunk_handler_t& chunk_handler, const dust_handler_t& dust_handler) + { + if (0 == amount) + { + chunk_handler(0); + return; + } + + bool is_dust_handled = false; + uint64_t dust = 0; + uint64_t order = 1; + while (0 != amount) + { + uint64_t chunk = (amount % 10) * order; + amount /= 10; + order *= 10; + + if (dust + chunk <= dust_threshold) + { + dust += chunk; + } + else + { + if (!is_dust_handled && 0 != dust) + { + dust_handler(dust); + is_dust_handled = true; + } + if (0 != chunk) + { + chunk_handler(chunk); + } + } + } + + if (!is_dust_handled && 0 != dust) + { + dust_handler(dust); + } + } + //--------------------------------------------------------------- + blobdata block_to_blob(const block& b); + bool block_to_blob(const block& b, blobdata& b_blob); + blobdata tx_to_blob(const transaction& b); + bool tx_to_blob(const transaction& b, blobdata& b_blob); + void get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes, crypto::hash& h); + crypto::hash get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes); + crypto::hash get_tx_tree_hash(const block& b); + +#define CHECKED_GET_SPECIFIC_VARIANT(variant_var, specific_type, variable_name, fail_return_val) \ + CHECK_AND_ASSERT_MES(variant_var.type() == typeid(specific_type), fail_return_val, "wrong variant type: " << variant_var.type().name() << ", expected " << typeid(specific_type).name()); \ + specific_type& variable_name = boost::get<specific_type>(variant_var); + +} diff --git a/src/cryptonote_core/cryptonote_stat_info.h b/src/cryptonote_core/cryptonote_stat_info.h new file mode 100644 index 000000000..9d406748c --- /dev/null +++ b/src/cryptonote_core/cryptonote_stat_info.h @@ -0,0 +1,27 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include "serialization/keyvalue_serialization.h" + + +namespace cryptonote +{ + struct core_stat_info + { + uint64_t tx_pool_size; + uint64_t blockchain_height; + uint64_t mining_speed; + uint64_t alternative_blocks; + std::string top_block_id_str; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tx_pool_size) + KV_SERIALIZE(blockchain_height) + KV_SERIALIZE(mining_speed) + KV_SERIALIZE(alternative_blocks) + KV_SERIALIZE(top_block_id_str) + END_KV_SERIALIZE_MAP() + }; +} diff --git a/src/cryptonote_core/difficulty.cpp b/src/cryptonote_core/difficulty.cpp new file mode 100644 index 000000000..052f46662 --- /dev/null +++ b/src/cryptonote_core/difficulty.cpp @@ -0,0 +1,111 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <vector> + +#include "common/int-util.h" +#include "crypto/hash.h" +#include "cryptonote_config.h" +#include "difficulty.h" + +namespace cryptonote { + + using std::size_t; + using std::uint64_t; + using std::vector; + +#if defined(_MSC_VER) +#include <windows.h> +#include <winnt.h> + + static inline void mul(uint64_t a, uint64_t b, uint64_t &low, uint64_t &high) { + low = UnsignedMultiply128(a, b, &high); + } + +#else + + static inline void mul(uint64_t a, uint64_t b, uint64_t &low, uint64_t &high) { + typedef unsigned __int128 uint128_t; + uint128_t res = (uint128_t) a * (uint128_t) b; + low = (uint64_t) res; + high = (uint64_t) (res >> 64); + } + +#endif + + static inline bool cadd(uint64_t a, uint64_t b) { + return a + b < a; + } + + static inline bool cadc(uint64_t a, uint64_t b, bool c) { + return a + b < a || (c && a + b == (uint64_t) -1); + } + + bool check_hash(const crypto::hash &hash, difficulty_type difficulty) { + uint64_t low, high, top, cur; + // First check the highest word, this will most likely fail for a random hash. + mul(swap64le(((const uint64_t *) &hash)[3]), difficulty, top, high); + if (high != 0) { + return false; + } + mul(swap64le(((const uint64_t *) &hash)[0]), difficulty, low, cur); + mul(swap64le(((const uint64_t *) &hash)[1]), difficulty, low, high); + bool carry = cadd(cur, low); + cur = high; + mul(swap64le(((const uint64_t *) &hash)[2]), difficulty, low, high); + carry = cadc(cur, low, carry); + carry = cadc(high, top, carry); + return !carry; + } + + difficulty_type next_difficulty(vector<uint64_t> timestamps, vector<difficulty_type> cumulative_difficulties, size_t target_seconds) { + //cutoff DIFFICULTY_LAG + if(timestamps.size() > DIFFICULTY_WINDOW) + { + timestamps.resize(DIFFICULTY_WINDOW); + cumulative_difficulties.resize(DIFFICULTY_WINDOW); + } + + + size_t length = timestamps.size(); + assert(length == cumulative_difficulties.size()); + if (length <= 1) { + return 1; + } + static_assert(DIFFICULTY_WINDOW >= 2, "Window is too small"); + assert(length <= DIFFICULTY_WINDOW); + sort(timestamps.begin(), timestamps.end()); + size_t cut_begin, cut_end; + static_assert(2 * DIFFICULTY_CUT <= DIFFICULTY_WINDOW - 2, "Cut length is too large"); + if (length <= DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT) { + cut_begin = 0; + cut_end = length; + } else { + cut_begin = (length - (DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT) + 1) / 2; + cut_end = cut_begin + (DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT); + } + assert(/*cut_begin >= 0 &&*/ cut_begin + 2 <= cut_end && cut_end <= length); + uint64_t time_span = timestamps[cut_end - 1] - timestamps[cut_begin]; + if (time_span == 0) { + time_span = 1; + } + difficulty_type total_work = cumulative_difficulties[cut_end - 1] - cumulative_difficulties[cut_begin]; + assert(total_work > 0); + uint64_t low, high; + mul(total_work, target_seconds, low, high); + if (high != 0 || low + time_span - 1 < low) { + return 0; + } + return (low + time_span - 1) / time_span; + } + + difficulty_type next_difficulty(vector<uint64_t> timestamps, vector<difficulty_type> cumulative_difficulties) + { + return next_difficulty(std::move(timestamps), std::move(cumulative_difficulties), DIFFICULTY_TARGET); + } +} diff --git a/src/cryptonote_core/difficulty.h b/src/cryptonote_core/difficulty.h new file mode 100644 index 000000000..aad1e27ca --- /dev/null +++ b/src/cryptonote_core/difficulty.h @@ -0,0 +1,19 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <cstdint> +#include <vector> + +#include "crypto/hash.h" + +namespace cryptonote +{ + typedef std::uint64_t difficulty_type; + + bool check_hash(const crypto::hash &hash, difficulty_type difficulty); + difficulty_type next_difficulty(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties); + difficulty_type next_difficulty(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds); +} diff --git a/src/cryptonote_core/miner.cpp b/src/cryptonote_core/miner.cpp new file mode 100644 index 000000000..a882e1899 --- /dev/null +++ b/src/cryptonote_core/miner.cpp @@ -0,0 +1,356 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include <sstream> +#include <numeric> +#include <boost/utility/value_init.hpp> +#include <boost/interprocess/detail/atomic.hpp> +#include <boost/limits.hpp> +#include <boost/foreach.hpp> +#include "misc_language.h" +#include "include_base_utils.h" +#include "cryptonote_basic_impl.h" +#include "cryptonote_format_utils.h" +#include "file_io_utils.h" +#include "common/command_line.h" +#include "string_coding.h" +#include "storages/portable_storage_template_helper.h" + +using namespace epee; + +#include "miner.h" + + + +namespace cryptonote +{ + + namespace + { + const command_line::arg_descriptor<std::string> arg_extra_messages = {"extra-messages-file", "Specify file for extra messages to include into coinbase transactions", "", true}; + const command_line::arg_descriptor<std::string> arg_start_mining = {"start-mining", "Specify wallet address to mining for", "", true}; + const command_line::arg_descriptor<uint32_t> arg_mining_threads = {"mining-threads", "Specify mining threads count", 0, true}; + } + + + miner::miner(i_miner_handler* phandler):m_stop(1), + m_template(boost::value_initialized<block>()), + m_template_no(0), + m_diffic(0), + m_thread_index(0), + m_phandler(phandler), + m_height(0), + m_pausers_count(0), + m_threads_total(0), + m_starter_nonce(0), + m_last_hr_merge_time(0), + m_hashes(0), + m_do_print_hashrate(false), + m_do_mining(false) + { + + } + //----------------------------------------------------------------------------------------------------- + miner::~miner() + { + stop(); + } + //----------------------------------------------------------------------------------------------------- + bool miner::set_block_template(const block& bl, const difficulty_type& di, uint64_t height) + { + CRITICAL_REGION_LOCAL(m_template_lock); + m_template = bl; + m_diffic = di; + m_height = height; + ++m_template_no; + m_starter_nonce = crypto::rand<uint32_t>(); + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::on_block_chain_update() + { + if(!is_mining()) + return true; + + return request_block_template(); + } + //----------------------------------------------------------------------------------------------------- + bool miner::request_block_template() + { + block bl = AUTO_VAL_INIT(bl); + difficulty_type di = AUTO_VAL_INIT(di); + uint64_t height = AUTO_VAL_INIT(height); + cryptonote::blobdata extra_nonce; + if(m_extra_messages.size() && m_config.current_extra_message_index < m_extra_messages.size()) + { + extra_nonce = m_extra_messages[m_config.current_extra_message_index]; + } + + if(!m_phandler->get_block_template(bl, m_mine_address, di, height, extra_nonce)) + { + LOG_ERROR("Failed to get_block_template(), stopping mining"); + return false; + } + set_block_template(bl, di, height); + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::on_idle() + { + m_update_block_template_interval.do_call([&](){ + if(is_mining())request_block_template(); + return true; + }); + + m_update_merge_hr_interval.do_call([&](){ + merge_hr(); + return true; + }); + + return true; + } + //----------------------------------------------------------------------------------------------------- + void miner::do_print_hashrate(bool do_hr) + { + m_do_print_hashrate = do_hr; + } + //----------------------------------------------------------------------------------------------------- + void miner::merge_hr() + { + if(m_last_hr_merge_time && is_mining()) + { + m_current_hash_rate = m_hashes * 1000 / ((misc_utils::get_tick_count() - m_last_hr_merge_time + 1)); + CRITICAL_REGION_LOCAL(m_last_hash_rates_lock); + m_last_hash_rates.push_back(m_current_hash_rate); + if(m_last_hash_rates.size() > 19) + m_last_hash_rates.pop_front(); + if(m_do_print_hashrate) + { + uint64_t total_hr = std::accumulate(m_last_hash_rates.begin(), m_last_hash_rates.end(), 0); + float hr = static_cast<float>(total_hr)/static_cast<float>(m_last_hash_rates.size()); + std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << ENDL; + } + } + m_last_hr_merge_time = misc_utils::get_tick_count(); + m_hashes = 0; + } + //----------------------------------------------------------------------------------------------------- + void miner::init_options(boost::program_options::options_description& desc) + { + command_line::add_arg(desc, arg_extra_messages); + command_line::add_arg(desc, arg_start_mining); + command_line::add_arg(desc, arg_mining_threads); + } + //----------------------------------------------------------------------------------------------------- + bool miner::init(const boost::program_options::variables_map& vm) + { + if(command_line::has_arg(vm, arg_extra_messages)) + { + std::string buff; + bool r = file_io_utils::load_file_to_string(command_line::get_arg(vm, arg_extra_messages), buff); + CHECK_AND_ASSERT_MES(r, false, "Failed to load file with extra messages: " << command_line::get_arg(vm, arg_extra_messages)); + std::vector<std::string> extra_vec; + boost::split(extra_vec, buff, boost::is_any_of("\n"), boost::token_compress_on ); + m_extra_messages.resize(extra_vec.size()); + for(size_t i = 0; i != extra_vec.size(); i++) + { + string_tools::trim(extra_vec[i]); + if(!extra_vec[i].size()) + continue; + std::string buff = string_encoding::base64_decode(extra_vec[i]); + if(buff != "0") + m_extra_messages[i] = buff; + } + m_config_folder_path = boost::filesystem::path(command_line::get_arg(vm, arg_extra_messages)).parent_path().string(); + m_config = AUTO_VAL_INIT(m_config); + epee::serialization::load_t_from_json_file(m_config, m_config_folder_path + "/" + MINER_CONFIG_FILE_NAME); + LOG_PRINT_L0("Loaded " << m_extra_messages.size() << " extra messages, current index " << m_config.current_extra_message_index); + } + + if(command_line::has_arg(vm, arg_start_mining)) + { + if(!cryptonote::get_account_address_from_str(m_mine_address, command_line::get_arg(vm, arg_start_mining))) + { + LOG_ERROR("Target account address " << command_line::get_arg(vm, arg_start_mining) << " has wrong format, starting daemon canceled"); + return false; + } + m_threads_total = 1; + m_do_mining = true; + if(command_line::has_arg(vm, arg_mining_threads)) + { + m_threads_total = command_line::get_arg(vm, arg_mining_threads); + } + } + + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::is_mining() + { + return !m_stop; + } + //----------------------------------------------------------------------------------------------------- + bool miner::start(const account_public_address& adr, size_t threads_count) + { + m_mine_address = adr; + m_threads_total = static_cast<uint32_t>(threads_count); + m_starter_nonce = crypto::rand<uint32_t>(); + CRITICAL_REGION_LOCAL(m_threads_lock); + if(is_mining()) + { + LOG_ERROR("Starting miner but it's already started"); + return false; + } + + if(!m_threads.empty()) + { + LOG_ERROR("Unable to start miner because there are active mining threads"); + return false; + } + + if(!m_template_no) + request_block_template();//lets update block template + + boost::interprocess::ipcdetail::atomic_write32(&m_stop, 0); + boost::interprocess::ipcdetail::atomic_write32(&m_thread_index, 0); + + for(size_t i = 0; i != threads_count; i++) + m_threads.push_back(boost::thread(boost::bind(&miner::worker_thread, this))); + + LOG_PRINT_L0("Mining has started with " << threads_count << " threads, good luck!" ) + return true; + } + //----------------------------------------------------------------------------------------------------- + uint64_t miner::get_speed() + { + return m_current_hash_rate; + } + //----------------------------------------------------------------------------------------------------- + void miner::send_stop_signal() + { + boost::interprocess::ipcdetail::atomic_write32(&m_stop, 1); + } + //----------------------------------------------------------------------------------------------------- + bool miner::stop() + { + send_stop_signal(); + CRITICAL_REGION_LOCAL(m_threads_lock); + + BOOST_FOREACH(boost::thread& th, m_threads) + th.join(); + + m_threads.clear(); + LOG_PRINT_L0("Mining has been stopped, " << m_threads.size() << " finished" ); + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::find_nonce_for_given_block(block& bl, const difficulty_type& diffic, uint64_t height) + { + for(; bl.nonce != std::numeric_limits<uint32_t>::max(); bl.nonce++) + { + crypto::hash h; + get_block_longhash(bl, h, height); + + if(check_hash(h, diffic)) + { + return true; + } + } + return false; + } + //----------------------------------------------------------------------------------------------------- + void miner::on_synchronized() + { + if(m_do_mining) + { + start(m_mine_address, m_threads_total); + } + } + //----------------------------------------------------------------------------------------------------- + void miner::pause() + { + CRITICAL_REGION_LOCAL(m_miners_count_lock); + ++m_pausers_count; + if(m_pausers_count == 1 && is_mining()) + LOG_PRINT_L2("MINING PAUSED"); + } + //----------------------------------------------------------------------------------------------------- + void miner::resume() + { + CRITICAL_REGION_LOCAL(m_miners_count_lock); + --m_pausers_count; + if(m_pausers_count < 0) + { + m_pausers_count = 0; + LOG_PRINT_RED_L0("Unexpected miner::resume() called"); + } + if(!m_pausers_count && is_mining()) + LOG_PRINT_L2("MINING RESUMED"); + } + //----------------------------------------------------------------------------------------------------- + bool miner::worker_thread() + { + uint32_t th_local_index = boost::interprocess::ipcdetail::atomic_inc32(&m_thread_index); + LOG_PRINT_L0("Miner thread was started ["<< th_local_index << "]"); + log_space::log_singletone::set_thread_log_prefix(std::string("[miner ") + std::to_string(th_local_index) + "]"); + uint32_t nonce = m_starter_nonce + th_local_index; + uint64_t height = 0; + difficulty_type local_diff = 0; + uint32_t local_template_ver = 0; + block b; + while(!m_stop) + { + if(m_pausers_count)//anti split workaround + { + misc_utils::sleep_no_w(100); + continue; + } + + if(local_template_ver != m_template_no) + { + + CRITICAL_REGION_BEGIN(m_template_lock); + b = m_template; + local_diff = m_diffic; + height = m_height; + CRITICAL_REGION_END(); + local_template_ver = m_template_no; + nonce = m_starter_nonce + th_local_index; + } + + if(!local_template_ver)//no any set_block_template call + { + LOG_PRINT_L2("Block template not set yet"); + epee::misc_utils::sleep_no_w(1000); + continue; + } + + b.nonce = nonce; + crypto::hash h; + get_block_longhash(b, h, height); + + if(check_hash(h, local_diff)) + { + //we lucky! + ++m_config.current_extra_message_index; + LOG_PRINT_GREEN("Found block for difficulty: " << local_diff, LOG_LEVEL_0); + if(!m_phandler->handle_block_found(b)) + { + --m_config.current_extra_message_index; + }else + { + //success update, lets update config + epee::serialization::store_t_to_json_file(m_config, m_config_folder_path + "/" + MINER_CONFIG_FILE_NAME); + } + } + nonce+=m_threads_total; + ++m_hashes; + } + LOG_PRINT_L0("Miner thread stopped ["<< th_local_index << "]"); + return true; + } + //----------------------------------------------------------------------------------------------------- +} + diff --git a/src/cryptonote_core/miner.h b/src/cryptonote_core/miner.h new file mode 100644 index 000000000..03f09509e --- /dev/null +++ b/src/cryptonote_core/miner.h @@ -0,0 +1,99 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <boost/atomic.hpp> +#include <boost/program_options.hpp> +#include <atomic> +#include "cryptonote_basic.h" +#include "difficulty.h" +#include "math_helper.h" + + +namespace cryptonote +{ + + struct i_miner_handler + { + virtual bool handle_block_found(block& b) = 0; + virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) = 0; + protected: + ~i_miner_handler(){}; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + class miner + { + public: + miner(i_miner_handler* phandler); + ~miner(); + bool init(const boost::program_options::variables_map& vm); + static void init_options(boost::program_options::options_description& desc); + 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); + uint64_t get_speed(); + void send_stop_signal(); + bool stop(); + bool is_mining(); + bool on_idle(); + void on_synchronized(); + //synchronous analog (for fast calls) + static bool find_nonce_for_given_block(block& bl, const difficulty_type& diffic, uint64_t height); + void pause(); + void resume(); + void do_print_hashrate(bool do_hr); + + private: + bool worker_thread(); + bool request_block_template(); + void merge_hr(); + + struct miner_config + { + uint64_t current_extra_message_index; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(current_extra_message_index) + END_KV_SERIALIZE_MAP() + }; + + + volatile uint32_t m_stop; + ::critical_section m_template_lock; + block m_template; + std::atomic<uint32_t> m_template_no; + std::atomic<uint32_t> m_starter_nonce; + difficulty_type m_diffic; + uint64_t m_height; + volatile uint32_t m_thread_index; + volatile uint32_t m_threads_total; + std::atomic<int32_t> m_pausers_count; + ::critical_section m_miners_count_lock; + + std::list<boost::thread> m_threads; + ::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; + 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; + 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 new file mode 100644 index 000000000..7e27cbc7f --- /dev/null +++ b/src/cryptonote_core/tx_extra.h @@ -0,0 +1,11 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + + + +#define TX_EXTRA_PADDING_MAX_COUNT 40 +#define TX_EXTRA_TAG_PUBKEY 0x01 +#define TX_EXTRA_NONCE 0x02 diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp new file mode 100644 index 000000000..3a1799675 --- /dev/null +++ b/src/cryptonote_core/tx_pool.cpp @@ -0,0 +1,410 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <boost/filesystem.hpp> +#include <unordered_set> + +#include "tx_pool.h" +#include "cryptonote_format_utils.h" +#include "cryptonote_boost_serialization.h" +#include "cryptonote_config.h" +#include "blockchain_storage.h" +#include "common/boost_serialization_helper.h" +#include "misc_language.h" +#include "warnings.h" +#include "crypto/hash.h" + +DISABLE_VS_WARNINGS(4244 4345 4503) //'boost::foreach_detail_::or_' : decorated name length exceeded, name was truncated + +namespace cryptonote +{ + //--------------------------------------------------------------------------------- + tx_memory_pool::tx_memory_pool(blockchain_storage& bchs): m_blockchain(bchs) + { + + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::add_tx(const transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block) + { + + + if(!check_inputs_types_supported(tx)) + { + tvc.m_verifivation_failed = true; + return false; + } + + uint64_t inputs_amount = 0; + if(!get_inputs_money_amount(tx, inputs_amount)) + { + tvc.m_verifivation_failed = true; + return false; + } + + uint64_t outputs_amount = get_outs_money_amount(tx); + + if(outputs_amount >= inputs_amount) + { + LOG_PRINT_L0("transaction use more money then it has: use " << outputs_amount << ", have " << inputs_amount); + tvc.m_verifivation_failed = true; + return false; + } + + //check key images for transaction if it is not kept by block + if(!kept_by_block) + { + if(have_tx_keyimges_as_spent(tx)) + { + LOG_ERROR("Transaction with id= "<< id << " used already spent key images"); + tvc.m_verifivation_failed = true; + return false; + } + } + + + crypto::hash max_used_block_id = null_hash; + uint64_t max_used_block_height = 0; + bool ch_inp_res = m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id); + CRITICAL_REGION_LOCAL(m_transactions_lock); + if(!ch_inp_res) + { + if(kept_by_block) + { + //anyway add this transaction to pool, because it related to block + auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details())); + CHECK_AND_ASSERT_MES(txd_p.second, false, "transaction already exists at inserting in memory pool"); + txd_p.first->second.blob_size = blob_size; + txd_p.first->second.tx = tx; + txd_p.first->second.fee = inputs_amount - outputs_amount; + 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; + tvc.m_verifivation_impossible = true; + tvc.m_added_to_pool = true; + }else + { + LOG_PRINT_L0("tx used wrong inputs, rejected"); + tvc.m_verifivation_failed = true; + return false; + } + }else + { + //update transactions container + auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details())); + CHECK_AND_ASSERT_MES(txd_p.second, false, "intrnal error: transaction already exists at inserting in memorypool"); + txd_p.first->second.blob_size = blob_size; + txd_p.first->second.tx = tx; + txd_p.first->second.kept_by_block = kept_by_block; + txd_p.first->second.fee = inputs_amount - outputs_amount; + txd_p.first->second.max_used_block_id = max_used_block_id; + 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; + tvc.m_added_to_pool = true; + + if(txd_p.first->second.fee > 0) + tvc.m_should_be_relayed = true; + } + + tvc.m_verifivation_failed = true; + //update image_keys container, here should everything goes ok. + BOOST_FOREACH(const auto& in, tx.vin) + { + CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, txin, false); + std::unordered_set<crypto::hash>& kei_image_set = m_spent_key_images[txin.k_image]; + CHECK_AND_ASSERT_MES(kept_by_block || kei_image_set.size() == 0, false, "internal error: keeped_by_block=" << kept_by_block + << ", kei_image_set.size()=" << kei_image_set.size() << ENDL << "txin.k_image=" << txin.k_image << ENDL + << "tx_id=" << id ); + auto ins_res = kei_image_set.insert(id); + CHECK_AND_ASSERT_MES(ins_res.second, false, "internal error: try to insert duplicate iterator in key_image set"); + } + + tvc.m_verifivation_failed = false; + //succeed + return true; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::add_tx(const transaction &tx, tx_verification_context& tvc, bool keeped_by_block) + { + crypto::hash h = null_hash; + size_t blob_size = 0; + get_transaction_hash(tx, h, blob_size); + return add_tx(tx, h, blob_size, tvc, keeped_by_block); + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::remove_transaction_keyimages(const transaction& tx) + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + BOOST_FOREACH(const txin_v& vi, tx.vin) + { + CHECKED_GET_SPECIFIC_VARIANT(vi, const txin_to_key, txin, false); + auto it = m_spent_key_images.find(txin.k_image); + CHECK_AND_ASSERT_MES(it != m_spent_key_images.end(), false, "failed to find transaction input in key images. img=" << txin.k_image << ENDL + << "transaction id = " << get_transaction_hash(tx)); + std::unordered_set<crypto::hash>& key_image_set = it->second; + CHECK_AND_ASSERT_MES(key_image_set.size(), false, "empty key_image set, img=" << txin.k_image << ENDL + << "transaction id = " << get_transaction_hash(tx)); + + auto it_in_set = key_image_set.find(get_transaction_hash(tx)); + CHECK_AND_ASSERT_MES(key_image_set.size(), false, "transaction id not found in key_image set, img=" << txin.k_image << ENDL + << "transaction id = " << get_transaction_hash(tx)); + key_image_set.erase(it_in_set); + if(!key_image_set.size()) + { + //it is now empty hash container for this key_image + m_spent_key_images.erase(it); + } + + } + return true; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee) + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + auto it = m_transactions.find(id); + if(it == m_transactions.end()) + return false; + + tx = it->second.tx; + blob_size = it->second.blob_size; + fee = it->second.fee; + remove_transaction_keyimages(it->second.tx); + m_transactions.erase(it); + return true; + } + //--------------------------------------------------------------------------------- + size_t tx_memory_pool::get_transactions_count() + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + return m_transactions.size(); + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::get_transactions(std::list<transaction>& txs) + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + BOOST_FOREACH(const auto& tx_vt, m_transactions) + txs.push_back(tx_vt.second.tx); + + return true; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::get_transaction(const crypto::hash& id, transaction& tx) + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + auto it = m_transactions.find(id); + if(it == m_transactions.end()) + return false; + tx = it->second.tx; + return true; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id) + { + return true; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::on_blockchain_dec(uint64_t new_block_height, const crypto::hash& top_block_id) + { + return true; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::have_tx(const crypto::hash &id) + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + if(m_transactions.count(id)) + return true; + return false; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::have_tx_keyimges_as_spent(const transaction& tx) + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + BOOST_FOREACH(const auto& in, tx.vin) + { + CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, true);//should never fail + if(have_tx_keyimg_as_spent(tokey_in.k_image)) + return true; + } + return false; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::have_tx_keyimg_as_spent(const crypto::key_image& key_im) + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + return m_spent_key_images.end() != m_spent_key_images.find(key_im); + } + //--------------------------------------------------------------------------------- + void tx_memory_pool::lock() + { + m_transactions_lock.lock(); + } + //--------------------------------------------------------------------------------- + void tx_memory_pool::unlock() + { + m_transactions_lock.unlock(); + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::is_transaction_ready_to_go(tx_details& txd) + { + //not the best implementation at this time, sorry :( + //check is ring_signature already checked ? + if(txd.max_used_block_id == null_hash) + {//not checked, lets try to check + + if(txd.last_failed_id != null_hash && m_blockchain.get_current_blockchain_height() > txd.last_failed_height && txd.last_failed_id == m_blockchain.get_block_id_by_height(txd.last_failed_height)) + return false;//we already sure that this tx is broken for this height + + if(!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id)) + { + txd.last_failed_height = m_blockchain.get_current_blockchain_height()-1; + txd.last_failed_id = m_blockchain.get_block_id_by_height(txd.last_failed_height); + return false; + } + }else + { + if(txd.max_used_block_height >= m_blockchain.get_current_blockchain_height()) + return false; + if(m_blockchain.get_block_id_by_height(txd.max_used_block_height) != txd.max_used_block_id) + { + //if we already failed on this height and id, skip actual ring signature check + if(txd.last_failed_id == m_blockchain.get_block_id_by_height(txd.last_failed_height)) + return false; + //check ring signature again, it is possible (with very small chance) that this transaction become again valid + if(!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id)) + { + txd.last_failed_height = m_blockchain.get_current_blockchain_height()-1; + txd.last_failed_id = m_blockchain.get_block_id_by_height(txd.last_failed_height); + return false; + } + } + } + //if we here, transaction seems valid, but, anyway, check for key_images collisions with blockchain, just to be sure + if(m_blockchain.have_tx_keyimges_as_spent(txd.tx)) + return false; + + //transaction is ok. + return true; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::have_key_images(const std::unordered_set<crypto::key_image>& k_images, const transaction& tx) + { + for(size_t i = 0; i!= tx.vin.size(); i++) + { + CHECKED_GET_SPECIFIC_VARIANT(tx.vin[i], const txin_to_key, itk, false); + if(k_images.count(itk.k_image)) + return true; + } + return false; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::append_key_images(std::unordered_set<crypto::key_image>& k_images, const transaction& tx) + { + for(size_t i = 0; i!= tx.vin.size(); i++) + { + CHECKED_GET_SPECIFIC_VARIANT(tx.vin[i], const txin_to_key, itk, false); + auto i_res = k_images.insert(itk.k_image); + CHECK_AND_ASSERT_MES(i_res.second, false, "internal error: key images pool cache - inserted duplicate image in set: " << itk.k_image); + } + return true; + } + //--------------------------------------------------------------------------------- + std::string tx_memory_pool::print_pool(bool short_format) + { + std::stringstream ss; + CRITICAL_REGION_LOCAL(m_transactions_lock); + BOOST_FOREACH(transactions_container::value_type& txe, m_transactions) + { + if(short_format) + { + tx_details& txd = txe.second; + ss << "id: " << txe.first << ENDL + << "blob_size: " << txd.blob_size << ENDL + << "fee: " << txd.fee << ENDL + << "kept_by_block: " << txd.kept_by_block << ENDL + << "max_used_block_height: " << txd.max_used_block_height << ENDL + << "max_used_block_id: " << txd.max_used_block_id << ENDL + << "last_failed_height: " << txd.last_failed_height << ENDL + << "last_failed_id: " << txd.last_failed_id << ENDL; + }else + { + tx_details& txd = txe.second; + ss << "id: " << txe.first << ENDL + << obj_to_json_str(txd.tx) << ENDL + << "blob_size: " << txd.blob_size << ENDL + << "fee: " << txd.fee << ENDL + << "kept_by_block: " << txd.kept_by_block << ENDL + << "max_used_block_height: " << txd.max_used_block_height << ENDL + << "max_used_block_id: " << txd.max_used_block_id << ENDL + << "last_failed_height: " << txd.last_failed_height << ENDL + << "last_failed_id: " << txd.last_failed_id << ENDL; + } + + } + return ss.str(); + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::fill_block_template(block& bl, size_t& cumulative_sizes, size_t max_comulative_sz, uint64_t& fee) + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + + fee = 0; + std::unordered_set<crypto::key_image> k_images; + + BOOST_FOREACH(transactions_container::value_type& tx, m_transactions) + { + if(cumulative_sizes + tx.second.blob_size > max_comulative_sz) + continue; + + if(!is_transaction_ready_to_go(tx.second)) + continue; + + if(have_key_images(k_images, tx.second.tx)) + continue; + + bl.tx_hashes.push_back(tx.first); + cumulative_sizes += tx.second.blob_size; + fee += tx.second.fee; + append_key_images(k_images, tx.second.tx); + + if(cumulative_sizes >= max_comulative_sz) + break; + } + + return true; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::init(const std::string& config_folder) + { + m_config_folder = config_folder; + std::string state_file_path = config_folder + "/" + CRYPTONOTE_POOLDATA_FILENAME; + boost::system::error_code ec; + if(!boost::filesystem::exists(state_file_path, ec)) + return true; + bool res = tools::unserialize_obj_from_file(*this, state_file_path); + if(!res) + { + LOG_PRINT_L0("Failed to load memory pool from file " << state_file_path); + } + return res; + } + + //--------------------------------------------------------------------------------- + bool tx_memory_pool::deinit() + { + if (!tools::create_directories_if_necessary(m_config_folder)) + { + LOG_PRINT_L0("Failed to create data directory: " << m_config_folder); + return false; + } + + std::string state_file_path = m_config_folder + "/" + CRYPTONOTE_POOLDATA_FILENAME; + bool res = tools::serialize_obj_to_file(*this, state_file_path); + if(!res) + { + LOG_PRINT_L0("Failed to serialize memory pool to file " << state_file_path); + } + return true; + } +} diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h new file mode 100644 index 000000000..1dff7ee1c --- /dev/null +++ b/src/cryptonote_core/tx_pool.h @@ -0,0 +1,168 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include "include_base_utils.h" +using namespace epee; + + +#include <set> +#include <unordered_map> +#include <unordered_set> +#include <boost/serialization/version.hpp> +#include <boost/utility.hpp> + +#include "string_tools.h" +#include "syncobj.h" +#include "cryptonote_basic_impl.h" +#include "verification_context.h" +#include "crypto/hash.h" + + +namespace cryptonote +{ + class blockchain_storage; + /************************************************************************/ + /* */ + /************************************************************************/ + + class tx_memory_pool: boost::noncopyable + { + public: + tx_memory_pool(blockchain_storage& bchs); + bool add_tx(const transaction &tx, const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block); + bool add_tx(const transaction &tx, tx_verification_context& tvc, bool keeped_by_block); + //gets tx and remove it from pool + bool take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee); + + bool have_tx(const crypto::hash &id); + bool have_tx_keyimg_as_spent(const crypto::key_image& key_im); + bool have_tx_keyimges_as_spent(const transaction& tx); + + bool on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id); + bool on_blockchain_dec(uint64_t new_block_height, const crypto::hash& top_block_id); + + void lock(); + void unlock(); + + // load/store operations + bool init(const std::string& config_folder); + bool deinit(); + bool fill_block_template(block& bl, size_t& cumulative_sizes, size_t max_comulative_sz, uint64_t& fee); + bool get_transactions(std::list<transaction>& txs); + bool get_transaction(const crypto::hash& h, transaction& tx); + size_t get_transactions_count(); + bool remove_transaction_keyimages(const transaction& tx); + bool have_key_images(const std::unordered_set<crypto::key_image>& kic, const transaction& tx); + bool append_key_images(std::unordered_set<crypto::key_image>& kic, const transaction& tx); + std::string print_pool(bool short_format); + + /*bool flush_pool(const std::strig& folder); + bool inflate_pool(const std::strig& folder);*/ + +#define CURRENT_MEMPOOL_ARCHIVE_VER 7 + + template<class archive_t> + void serialize(archive_t & a, const unsigned int version) + { + if(version < CURRENT_MEMPOOL_ARCHIVE_VER ) + return; + CRITICAL_REGION_LOCAL(m_transactions_lock); + a & m_transactions; + a & m_spent_key_images; + } + + struct tx_details + { + transaction tx; + size_t blob_size; + uint64_t fee; + crypto::hash max_used_block_id; + uint64_t max_used_block_height; + bool kept_by_block; + // + uint64_t last_failed_height; + crypto::hash last_failed_id; + }; + + private: + 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; + + epee::critical_section m_transactions_lock; + transactions_container m_transactions; + key_images_container m_spent_key_images; + + //transactions_container m_alternative_transactions; + + std::string m_config_folder; + blockchain_storage& m_blockchain; + /************************************************************************/ + /* */ + /************************************************************************/ + /*class inputs_visitor: public boost::static_visitor<bool> + { + key_images_container& m_spent_keys; + public: + inputs_visitor(key_images_container& spent_keys): m_spent_keys(spent_keys) + {} + bool operator()(const txin_to_key& tx) const + { + auto pr = m_spent_keys.insert(tx.k_image); + CHECK_AND_ASSERT_MES(pr.second, false, "Tried to insert transaction with input seems already spent, input: " << epee::string_tools::pod_to_hex(tx.k_image)); + return true; + } + bool operator()(const txin_gen& tx) const + { + CHECK_AND_ASSERT_MES(false, false, "coinbase transaction in memory pool"); + return false; + } + bool operator()(const txin_to_script& tx) const {return false;} + bool operator()(const txin_to_scripthash& tx) const {return false;} + }; */ + /************************************************************************/ + /* */ + /************************************************************************/ + class amount_visitor: public boost::static_visitor<uint64_t> + { + public: + uint64_t operator()(const txin_to_key& tx) const + { + return tx.amount; + } + uint64_t operator()(const txin_gen& tx) const + { + CHECK_AND_ASSERT_MES(false, false, "coinbase transaction in memory pool"); + return 0; + } + uint64_t operator()(const txin_to_script& tx) const {return 0;} + uint64_t operator()(const txin_to_scripthash& tx) const {return 0;} + }; + + }; +} + +namespace boost +{ + namespace serialization + { + template<class archive_t> + void serialize(archive_t & ar, cryptonote::tx_memory_pool::tx_details& td, const unsigned int version) + { + ar & td.blob_size; + ar & td.fee; + ar & td.tx; + ar & td.max_used_block_height; + ar & td.max_used_block_id; + ar & td.last_failed_height; + ar & td.last_failed_id; + + } + } +} +BOOST_CLASS_VERSION(cryptonote::tx_memory_pool, CURRENT_MEMPOOL_ARCHIVE_VER) + + + diff --git a/src/cryptonote_core/verification_context.h b/src/cryptonote_core/verification_context.h new file mode 100644 index 000000000..210cc2d5b --- /dev/null +++ b/src/cryptonote_core/verification_context.h @@ -0,0 +1,27 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once +namespace cryptonote +{ + /************************************************************************/ + /* */ + /************************************************************************/ + struct tx_verification_context + { + bool m_should_be_relayed; + bool m_verifivation_failed; //bad tx, should drop connection + bool m_verifivation_impossible; //the transaction is related with an alternative blockchain + bool m_added_to_pool; + }; + + struct block_verification_context + { + bool m_added_to_main_chain; + bool m_verifivation_failed; //bad block, should drop connection + bool m_marked_as_orphaned; + bool m_already_exists; + }; +} diff --git a/src/cryptonote_protocol/blobdatatype.h b/src/cryptonote_protocol/blobdatatype.h new file mode 100644 index 000000000..23111f048 --- /dev/null +++ b/src/cryptonote_protocol/blobdatatype.h @@ -0,0 +1,10 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +namespace cryptonote +{ + typedef std::string blobdata; +} diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h new file mode 100644 index 000000000..d646a7f6f --- /dev/null +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -0,0 +1,152 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <list> +#include "serialization/keyvalue_serialization.h" +#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_protocol/blobdatatype.h" +namespace cryptonote +{ + + +#define BC_COMMANDS_POOL_BASE 2000 + + + /************************************************************************/ + /* */ + /************************************************************************/ + struct block_complete_entry + { + blobdata block; + std::list<blobdata> txs; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(block) + KV_SERIALIZE(txs) + END_KV_SERIALIZE_MAP() + }; + + + /************************************************************************/ + /* */ + /************************************************************************/ + struct NOTIFY_NEW_BLOCK + { + const static int ID = BC_COMMANDS_POOL_BASE + 1; + + struct request + { + block_complete_entry b; + uint64_t current_blockchain_height; + uint32_t hop; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(b) + KV_SERIALIZE(current_blockchain_height) + KV_SERIALIZE(hop) + END_KV_SERIALIZE_MAP() + }; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + struct NOTIFY_NEW_TRANSACTIONS + { + const static int ID = BC_COMMANDS_POOL_BASE + 2; + + struct request + { + std::list<blobdata> txs; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(txs) + END_KV_SERIALIZE_MAP() + }; + }; + /************************************************************************/ + /* */ + /************************************************************************/ + struct NOTIFY_REQUEST_GET_OBJECTS + { + const static int ID = BC_COMMANDS_POOL_BASE + 3; + + struct request + { + std::list<crypto::hash> txs; + std::list<crypto::hash> blocks; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(txs) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(blocks) + END_KV_SERIALIZE_MAP() + }; + }; + + struct NOTIFY_RESPONSE_GET_OBJECTS + { + const static int ID = BC_COMMANDS_POOL_BASE + 4; + + struct request + { + std::list<blobdata> txs; + std::list<block_complete_entry> blocks; + std::list<crypto::hash> missed_ids; + uint64_t current_blockchain_height; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(txs) + KV_SERIALIZE(blocks) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(missed_ids) + KV_SERIALIZE(current_blockchain_height) + END_KV_SERIALIZE_MAP() + }; + }; + + + struct CORE_SYNC_DATA + { + uint64_t current_height; + crypto::hash top_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(current_height) + KV_SERIALIZE_VAL_POD_AS_BLOB(top_id) + END_KV_SERIALIZE_MAP() + }; + + struct NOTIFY_REQUEST_CHAIN + { + const static int ID = BC_COMMANDS_POOL_BASE + 6; + + struct request + { + std::list<crypto::hash> block_ids; /*IDs of the first 10 blocks are sequential, next goes with pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids) + END_KV_SERIALIZE_MAP() + }; + }; + + struct NOTIFY_RESPONSE_CHAIN_ENTRY + { + const static int ID = BC_COMMANDS_POOL_BASE + 7; + + struct request + { + uint64_t start_height; + uint64_t total_height; + std::list<crypto::hash> m_block_ids; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(start_height) + KV_SERIALIZE(total_height) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_ids) + END_KV_SERIALIZE_MAP() + }; + }; + +} diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h new file mode 100644 index 000000000..f599cf40f --- /dev/null +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -0,0 +1,107 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <boost/program_options/variables_map.hpp> + +#include "storages/levin_abstract_invoke2.h" +#include "warnings.h" +#include "cryptonote_protocol_defs.h" +#include "cryptonote_protocol_handler_common.h" +#include "cryptonote_core/connection_context.h" +#include "cryptonote_core/cryptonote_stat_info.h" +#include "cryptonote_core/verification_context.h" + +PUSH_WARNINGS +DISABLE_VS_WARNINGS(4355) + +namespace cryptonote +{ + + template<class t_core> + class t_cryptonote_protocol_handler: public i_cryptonote_protocol + { + public: + typedef cryptonote_connection_context connection_context; + typedef core_stat_info stat_info; + typedef t_cryptonote_protocol_handler<t_core> cryptonote_protocol_handler; + typedef CORE_SYNC_DATA payload_type; + + t_cryptonote_protocol_handler(t_core& rcore, nodetool::i_p2p_endpoint<connection_context>* p_net_layout); + + BEGIN_INVOKE_MAP2(cryptonote_protocol_handler) + HANDLE_NOTIFY_T2(NOTIFY_NEW_BLOCK, &cryptonote_protocol_handler::handle_notify_new_block) + HANDLE_NOTIFY_T2(NOTIFY_NEW_TRANSACTIONS, &cryptonote_protocol_handler::handle_notify_new_transactions) + HANDLE_NOTIFY_T2(NOTIFY_REQUEST_GET_OBJECTS, &cryptonote_protocol_handler::handle_request_get_objects) + HANDLE_NOTIFY_T2(NOTIFY_RESPONSE_GET_OBJECTS, &cryptonote_protocol_handler::handle_response_get_objects) + HANDLE_NOTIFY_T2(NOTIFY_REQUEST_CHAIN, &cryptonote_protocol_handler::handle_request_chain) + HANDLE_NOTIFY_T2(NOTIFY_RESPONSE_CHAIN_ENTRY, &cryptonote_protocol_handler::handle_response_chain_entry) + END_INVOKE_MAP2() + + bool on_idle(); + bool init(const boost::program_options::variables_map& vm); + bool deinit(); + void set_p2p_endpoint(nodetool::i_p2p_endpoint<connection_context>* p2p); + //bool process_handshake_data(const blobdata& data, cryptonote_connection_context& context); + bool process_payload_sync_data(const CORE_SYNC_DATA& hshd, cryptonote_connection_context& context, bool is_inital); + bool get_payload_sync_data(blobdata& data); + bool get_payload_sync_data(CORE_SYNC_DATA& hshd); + bool get_stat_info(core_stat_info& stat_inf); + bool on_callback(cryptonote_connection_context& context); + t_core& get_core(){return m_core;} + + + private: + //----------------- commands handlers ---------------------------------------------- + int handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& context); + int handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& context); + int handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context); + int handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, cryptonote_connection_context& context); + int handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, cryptonote_connection_context& context); +// int handle_request_chain_entry(int command, NOTIFY_REQUEST_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context); + int handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context); + + + //----------------- i_bc_protocol_layout --------------------------------------- + virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context); + virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context); + //---------------------------------------------------------------------------------- + //bool get_payload_sync_data(HANDSHAKE_DATA::request& hshd, cryptonote_connection_context& context); + bool request_missing_objects(cryptonote_connection_context& context, bool check_having_blocks); + size_t get_synchronizing_connections_count(); + bool on_connection_synchronized(); + t_core& m_core; + + nodetool::p2p_endpoint_stub<connection_context> m_p2p_stub; + nodetool::i_p2p_endpoint<connection_context>* m_p2p; + std::atomic<uint32_t> m_syncronized_connections_count; + //std::atomic<uint32_t> m_syncronizing_connections_count; + std::atomic<bool> m_welcome_showed; + + + 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() << " -->"); + std::string blob; + epee::serialization::store_t_to_binary(arg, blob); + return m_p2p->invoke_notify_to_peer(t_parametr::ID, blob, context); + } + + 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() << " -->"); + 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); + } + }; +} + + +#include "cryptonote_protocol_handler.inl" + +POP_WARNINGS diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl new file mode 100644 index 000000000..9c9668071 --- /dev/null +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -0,0 +1,474 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <boost/interprocess/detail/atomic.hpp> +#include "cryptonote_core/cryptonote_format_utils.h" +#include "profile_tools.h" +namespace cryptonote +{ + + //----------------------------------------------------------------------------------------------------------------------- + template<class t_core> + t_cryptonote_protocol_handler<t_core>::t_cryptonote_protocol_handler(t_core& rcore, nodetool::i_p2p_endpoint<connection_context>* p_net_layout):m_core(rcore), + m_p2p(p_net_layout), + m_syncronized_connections_count(0), + m_welcome_showed(false) + + { + if(!m_p2p) + m_p2p = &m_p2p_stub; + } + //----------------------------------------------------------------------------------------------------------------------- + template<class t_core> + bool t_cryptonote_protocol_handler<t_core>::init(const boost::program_options::variables_map& vm) + { + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> + bool t_cryptonote_protocol_handler<t_core>::deinit() + { + + + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> + void t_cryptonote_protocol_handler<t_core>::set_p2p_endpoint(nodetool::i_p2p_endpoint<connection_context>* p2p) + { + if(p2p) + m_p2p = p2p; + else + m_p2p = &m_p2p_stub; + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> + bool t_cryptonote_protocol_handler<t_core>::on_callback(cryptonote_connection_context& context) + { + LOG_PRINT_CCONTEXT_L2("callback fired"); + CHECK_AND_ASSERT_MES_CC( context.m_callback_request_count > 0, false, "false callback fired, but context.m_callback_request_count=" << context.m_callback_request_count); + --context.m_callback_request_count; + + if(context.m_state == cryptonote_connection_context::state_synchronizing) + { + NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized<NOTIFY_REQUEST_CHAIN::request>(); + m_core.get_short_chain_history(r.block_ids); + LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() ); + post_notify<NOTIFY_REQUEST_CHAIN>(r, context); + } + + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> + bool t_cryptonote_protocol_handler<t_core>::get_stat_info(core_stat_info& stat_inf) + { + return m_core.get_stat_info(stat_inf); + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> + bool t_cryptonote_protocol_handler<t_core>::process_payload_sync_data(const CORE_SYNC_DATA& hshd, cryptonote_connection_context& context, bool is_inital) + { + if(context.m_state == cryptonote_connection_context::state_befor_handshake && !is_inital) + return true; + + if(context.m_state == cryptonote_connection_context::state_synchronizing) + return true; + + if(m_core.have_block(hshd.top_id)) + { + context.m_state = cryptonote_connection_context::state_normal; + if(is_inital) + on_connection_synchronized(); + return true; + } + + LOG_PRINT_CCONTEXT_BLUE("Sync data returned unknown top block " << "["<< m_core.get_current_blockchain_height() << "->" << hshd.current_height << "] " << hshd.top_id << ", set SYNCHRONIZATION mode", LOG_LEVEL_0); + 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 + LOG_PRINT_CCONTEXT_L2("requesting callback"); + ++context.m_callback_request_count; + m_p2p->request_callback(context); + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + /* template<class t_core> + bool t_cryptonote_protocol_handler<t_core>::process_handshake_data(const blobdata& data, cryptonote_connection_context& context) + { + CORE_SYNC_DATA hsd = boost::value_initialized<CORE_SYNC_DATA>(); + StorageNamed::load_struct_from_storage_buff(hsd, data); + return process_handshake_data(hsd, context); + }*/ + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> + bool t_cryptonote_protocol_handler<t_core>::get_payload_sync_data(CORE_SYNC_DATA& hshd) + { + m_core.get_blockchain_top(hshd.current_height, hshd.top_id); + hshd.current_height +=1; + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> + bool t_cryptonote_protocol_handler<t_core>::get_payload_sync_data(blobdata& data) + { + CORE_SYNC_DATA hsd = boost::value_initialized<CORE_SYNC_DATA>(); + get_payload_sync_data(hsd); + epee::serialization::store_t_to_binary(hsd, data); + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> + int t_cryptonote_protocol_handler<t_core>::handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& context) + { + LOG_PRINT_CCONTEXT_L2("NOTIFY_NEW_BLOCK (hop " << arg.hop << ")"); + if(context.m_state != cryptonote_connection_context::state_normal) + return 1; + + for(auto tx_blob_it = arg.b.txs.begin(); tx_blob_it!=arg.b.txs.end();tx_blob_it++) + { + cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); + m_core.handle_incoming_tx(*tx_blob_it, tvc, true); + if(tvc.m_verifivation_failed) + { + LOG_PRINT_CCONTEXT_L0("Block verification failed: transaction verification failed, dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + } + + + block_verification_context bvc = boost::value_initialized<block_verification_context>(); + m_core.pause_mine(); + m_core.handle_incoming_block(arg.b.block, bvc); + m_core.resume_mine(); + if(bvc.m_verifivation_failed) + { + LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + if(bvc.m_added_to_main_chain) + { + ++arg.hop; + //TODO: Add here announce protocol usage + relay_block(arg, context); + }else if(bvc.m_marked_as_orphaned) + { + context.m_state = cryptonote_connection_context::state_synchronizing; + NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized<NOTIFY_REQUEST_CHAIN::request>(); + m_core.get_short_chain_history(r.block_ids); + LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() ); + post_notify<NOTIFY_REQUEST_CHAIN>(r, context); + } + + return 1; + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> + int t_cryptonote_protocol_handler<t_core>::handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& context) + { + LOG_PRINT_CCONTEXT_L2("NOTIFY_NEW_TRANSACTIONS"); + if(context.m_state != cryptonote_connection_context::state_normal) + return 1; + + for(auto tx_blob_it = arg.txs.begin(); tx_blob_it!=arg.txs.end();) + { + cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); + m_core.handle_incoming_tx(*tx_blob_it, tvc, false); + if(tvc.m_verifivation_failed) + { + LOG_PRINT_CCONTEXT_L0("Tx verification failed, dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + if(tvc.m_should_be_relayed) + ++tx_blob_it; + else + arg.txs.erase(tx_blob_it++); + } + + if(arg.txs.size()) + { + //TODO: add announce usage here + relay_transactions(arg, context); + } + + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> + int t_cryptonote_protocol_handler<t_core>::handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context) + { + LOG_PRINT_CCONTEXT_L2("NOTIFY_REQUEST_GET_OBJECTS"); + NOTIFY_RESPONSE_GET_OBJECTS::request rsp; + if(!m_core.handle_get_objects(arg, rsp, context)) + { + LOG_ERROR_CCONTEXT("failed to handle request NOTIFY_REQUEST_GET_OBJECTS, dropping connection"); + m_p2p->drop_connection(context); + } + LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_RESPONSE_GET_OBJECTS: blocks.size()=" << rsp.blocks.size() << ", txs.size()=" << rsp.txs.size() + << ", rsp.m_current_blockchain_height=" << rsp.current_blockchain_height << ", missed_ids.size()=" << rsp.missed_ids.size()); + post_notify<NOTIFY_RESPONSE_GET_OBJECTS>(rsp, context); + return 1; + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> + int t_cryptonote_protocol_handler<t_core>::handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, cryptonote_connection_context& context) + { + LOG_PRINT_CCONTEXT_L2("NOTIFY_RESPONSE_GET_OBJECTS"); + if(context.m_last_response_height > arg.current_blockchain_height) + { + LOG_ERROR_CCONTEXT("sent wrong NOTIFY_HAVE_OBJECTS: arg.m_current_blockchain_height=" << arg.current_blockchain_height + << " < m_last_response_height=" << context.m_last_response_height << ", dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + + context.m_remote_blockchain_height = arg.current_blockchain_height; + + BOOST_FOREACH(const block_complete_entry& block_entry, arg.blocks) + { + block b; + 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"); + m_p2p->drop_connection(context); + return 1; + } + + 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)) + << " 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)) + << ", 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; + } + + context.m_requested_objects.erase(req_it); + } + + if(context.m_requested_objects.size()) + { + LOG_PRINT_CCONTEXT_RED("returned not all requested objects (context.m_requested_objects.size()=" + << context.m_requested_objects.size() << "), dropping connection", LOG_LEVEL_0); + m_p2p->drop_connection(context); + return 1; + } + + { + m_core.pause_mine(); + misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler( + boost::bind(&t_core::resume_mine, &m_core)); + + BOOST_FOREACH(const block_complete_entry& block_entry, arg.blocks) + { + //process transactions + TIME_MEASURE_START(transactions_process_time); + BOOST_FOREACH(auto& tx_blob, block_entry.txs) + { + tx_verification_context tvc = AUTO_VAL_INIT(tvc); + m_core.handle_incoming_tx(tx_blob, tvc, true); + 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"); + m_p2p->drop_connection(context); + return 1; + } + } + TIME_MEASURE_FINISH(transactions_process_time); + + //process block + TIME_MEASURE_START(block_process_time); + block_verification_context bvc = boost::value_initialized<block_verification_context>(); + + m_core.handle_incoming_block(block_entry.block, bvc, false); + + if(bvc.m_verifivation_failed) + { + LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + if(bvc.m_marked_as_orphaned) + { + LOG_PRINT_CCONTEXT_L0("Block received at sync phase was marked as orphaned, dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + + TIME_MEASURE_FINISH(block_process_time); + LOG_PRINT_CCONTEXT_L2("Block process time: " << block_process_time + transactions_process_time << "(" << transactions_process_time << "/" << block_process_time << ")ms"); + } + } + + request_missing_objects(context, true); + return 1; + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> + bool t_cryptonote_protocol_handler<t_core>::on_idle() + { + return m_core.on_idle(); + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> + int t_cryptonote_protocol_handler<t_core>::handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, cryptonote_connection_context& context) + { + LOG_PRINT_CCONTEXT_L2("NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << arg.block_ids.size()); + NOTIFY_RESPONSE_CHAIN_ENTRY::request r; + if(!m_core.find_blockchain_supplement(arg.block_ids, r)) + { + LOG_ERROR_CCONTEXT("Failed to handle NOTIFY_REQUEST_CHAIN."); + return 1; + } + LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_RESPONSE_CHAIN_ENTRY: m_start_height=" << r.start_height << ", m_total_height=" << r.total_height << ", m_block_ids.size()=" << r.m_block_ids.size()); + post_notify<NOTIFY_RESPONSE_CHAIN_ENTRY>(r, context); + return 1; + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> + bool t_cryptonote_protocol_handler<t_core>::request_missing_objects(cryptonote_connection_context& context, bool check_having_blocks) + { + if(context.m_needed_objects.size()) + { + //we know objects that we need, request this objects + NOTIFY_REQUEST_GET_OBJECTS::request req; + size_t count = 0; + auto it = context.m_needed_objects.begin(); + + while(it != context.m_needed_objects.end() && count < BLOCKS_SYNCHRONIZING_DEFAULT_COUNT) + { + if( !(check_having_blocks && m_core.have_block(*it))) + { + req.blocks.push_back(*it); + ++count; + context.m_requested_objects.insert(*it); + } + context.m_needed_objects.erase(it++); + } + LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_REQUEST_GET_OBJECTS: blocks.size()=" << req.blocks.size() << ", txs.size()=" << req.txs.size()); + post_notify<NOTIFY_REQUEST_GET_OBJECTS>(req, context); + }else if(context.m_last_response_height < context.m_remote_blockchain_height-1) + {//we have to fetch more objects ids, request blockchain entry + + NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized<NOTIFY_REQUEST_CHAIN::request>(); + m_core.get_short_chain_history(r.block_ids); + LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() ); + post_notify<NOTIFY_REQUEST_CHAIN>(r, context); + }else + { + CHECK_AND_ASSERT_MES(context.m_last_response_height == context.m_remote_blockchain_height-1 + && !context.m_needed_objects.size() + && !context.m_requested_objects.size(), false, "request_missing_blocks final condition failed!" + << "\r\nm_last_response_height=" << context.m_last_response_height + << "\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)<< "]"); + + context.m_state = cryptonote_connection_context::state_normal; + LOG_PRINT_CCONTEXT_GREEN(" SYNCHRONIZED OK", LOG_LEVEL_0); + if( true/*get_synchronizing_connections_count() == 0 && !m_welcome_showed*/) + { + on_connection_synchronized(); + } + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> + bool t_cryptonote_protocol_handler<t_core>::on_connection_synchronized() + { + bool val_expected = false; + if(m_welcome_showed.compare_exchange_strong(val_expected, true)) + { + LOG_PRINT_L0(ENDL << "**********************************************************************" << ENDL + << "You are now synchronized with the network. You may now start simplewallet." << ENDL + << ENDL + << "Please note, that the blockchain will be saved only after you quit the daemon with \"exit\" command or if you use \"save\" command." << ENDL + << "Otherwise, you will possibly need to synchronize the blockchain again." << ENDL + << ENDL + << "Use \"help\" command to see the list of available commands." << ENDL + << "**********************************************************************"); + m_core.on_synchronized(); + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> + size_t t_cryptonote_protocol_handler<t_core>::get_synchronizing_connections_count() + { + size_t count = 0; + m_p2p->for_each_connection([&](cryptonote_connection_context& context)->bool{ + if(context.m_state == cryptonote_connection_context::state_synchronizing) + ++count; + return true; + }); + return count; + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> + int t_cryptonote_protocol_handler<t_core>::handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context) + { + LOG_PRINT_CCONTEXT_L2("NOTIFY_RESPONSE_CHAIN_ENTRY: m_block_ids.size()=" << arg.m_block_ids.size() + << ", m_start_height=" << arg.start_height << ", m_total_height=" << arg.total_height); + + if(!arg.m_block_ids.size()) + { + LOG_ERROR_CCONTEXT("sent empty m_block_ids, dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + + 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"); + m_p2p->drop_connection(context); + return 1; + } + + context.m_remote_blockchain_height = arg.total_height; + context.m_last_response_height = arg.start_height + arg.m_block_ids.size()-1; + if(context.m_last_response_height > context.m_remote_blockchain_height) + { + LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_CHAIN_ENTRY, with \r\nm_total_height=" << arg.total_height + << "\r\nm_start_height=" << arg.start_height + << "\r\nm_block_ids.size()=" << arg.m_block_ids.size()); + m_p2p->drop_connection(context); + } + + BOOST_FOREACH(auto& bl_id, arg.m_block_ids) + { + if(!m_core.have_block(bl_id)) + context.m_needed_objects.push_back(bl_id); + } + + request_missing_objects(context, false); + return 1; + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> + bool t_cryptonote_protocol_handler<t_core>::relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context) + { + return relay_post_notify<NOTIFY_NEW_BLOCK>(arg, exclude_context); + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> + bool t_cryptonote_protocol_handler<t_core>::relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context) + { + return relay_post_notify<NOTIFY_NEW_TRANSACTIONS>(arg, exclude_context); + } +} diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h new file mode 100644 index 000000000..f1ced5050 --- /dev/null +++ b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h @@ -0,0 +1,37 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include "p2p/net_node_common.h" +#include "cryptonote_protocol/cryptonote_protocol_defs.h" +#include "cryptonote_core/connection_context.h" +namespace cryptonote +{ + /************************************************************************/ + /* */ + /************************************************************************/ + struct i_cryptonote_protocol + { + virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context)=0; + virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context)=0; + //virtual bool request_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context)=0; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + struct cryptonote_protocol_stub: public i_cryptonote_protocol + { + virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context) + { + return false; + } + virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context) + { + return false; + } + + }; +} diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp new file mode 100644 index 000000000..d1ac3714b --- /dev/null +++ b/src/daemon/daemon.cpp @@ -0,0 +1,234 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// node.cpp : Defines the entry point for the console application. +// + + +#include "include_base_utils.h" +#include "version.h" + +using namespace epee; + +#include <boost/program_options.hpp> + +#include "crypto/hash.h" +#include "console_handler.h" +#include "p2p/net_node.h" +#include "cryptonote_core/checkpoints_create.h" +#include "cryptonote_core/cryptonote_core.h" +#include "rpc/core_rpc_server.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "daemon_commands_handler.h" +#include "version.h" + +#if defined(WIN32) +#include <crtdbg.h> +#endif + +namespace po = boost::program_options; + + +BOOST_CLASS_VERSION(nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >, 1); + +namespace +{ + const command_line::arg_descriptor<std::string> arg_config_file = {"config-file", "Specify configuration file", std::string(CRYPTONOTE_NAME ".conf")}; + const command_line::arg_descriptor<bool> arg_os_version = {"os-version", ""}; + const command_line::arg_descriptor<std::string> arg_log_file = {"log-file", "", ""}; + const command_line::arg_descriptor<int> arg_log_level = {"log-level", "", LOG_LEVEL_0}; +} + +bool command_line_preprocessor(const boost::program_options::variables_map& vm); + +int main(int argc, char* argv[]) +{ + + string_tools::set_module_name_and_folder(argv[0]); +#ifdef WIN32 + _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); +#endif + log_space::get_set_log_detalisation_level(true, LOG_LEVEL_0); + log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); + LOG_PRINT_L0("Starting..."); + + TRY_ENTRY(); + + po::options_description desc_cmd_only("Command line options"); + po::options_description desc_cmd_sett("Command line options and settings options"); + + command_line::add_arg(desc_cmd_only, command_line::arg_help); + command_line::add_arg(desc_cmd_only, command_line::arg_version); + command_line::add_arg(desc_cmd_only, arg_os_version); + // tools::get_default_data_dir() can't be called during static initialization + command_line::add_arg(desc_cmd_only, command_line::arg_data_dir, tools::get_default_data_dir()); + command_line::add_arg(desc_cmd_only, arg_config_file); + + command_line::add_arg(desc_cmd_sett, arg_log_file); + command_line::add_arg(desc_cmd_sett, arg_log_level); + + cryptonote::core::init_options(desc_cmd_sett); + cryptonote::core_rpc_server::init_options(desc_cmd_sett); + nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >::init_options(desc_cmd_sett); + cryptonote::miner::init_options(desc_cmd_sett); + + po::options_description desc_options("Allowed options"); + desc_options.add(desc_cmd_only).add(desc_cmd_sett); + + po::variables_map vm; + bool r = command_line::handle_error_helper(desc_options, [&]() + { + po::store(po::parse_command_line(argc, argv, desc_options), vm); + + if (command_line::get_arg(vm, command_line::arg_help)) + { + std::cout << CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG << ENDL << ENDL; + std::cout << desc_options << std::endl; + return false; + } + + std::string data_dir = command_line::get_arg(vm, command_line::arg_data_dir); + std::string config = command_line::get_arg(vm, arg_config_file); + + boost::filesystem::path data_dir_path(data_dir); + boost::filesystem::path config_path(config); + if (!config_path.has_parent_path()) + { + config_path = data_dir_path / config_path; + } + + boost::system::error_code ec; + if (boost::filesystem::exists(config_path, ec)) + { + po::store(po::parse_config_file<char>(config_path.string<std::string>().c_str(), desc_cmd_sett), vm); + } + po::notify(vm); + + return true; + }); + if (!r) + return 1; + + //set up logging options + boost::filesystem::path log_file_path(command_line::get_arg(vm, arg_log_file)); + if (log_file_path.empty()) + log_file_path = log_space::log_singletone::get_default_log_file(); + std::string log_dir; + log_dir = log_file_path.has_parent_path() ? log_file_path.parent_path().string() : log_space::log_singletone::get_default_log_folder(); + + log_space::log_singletone::add_logger(LOGGER_FILE, log_file_path.filename().string().c_str(), log_dir.c_str()); + + if (command_line_preprocessor(vm)) + { + return 0; + } + + LOG_PRINT("Module folder: " << argv[0], LOG_LEVEL_0); + + bool res = true; + cryptonote::checkpoints checkpoints; + res = cryptonote::create_checkpoints(checkpoints); + CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize checkpoints"); + + //create objects and link them + cryptonote::core ccore(NULL); + ccore.set_checkpoints(std::move(checkpoints)); + cryptonote::t_cryptonote_protocol_handler<cryptonote::core> cprotocol(ccore, NULL); + nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> > p2psrv(cprotocol); + cryptonote::core_rpc_server rpc_server(ccore, p2psrv); + cprotocol.set_p2p_endpoint(&p2psrv); + ccore.set_cryptonote_protocol(&cprotocol); + daemon_cmmands_handler dch(p2psrv); + + //initialize objects + LOG_PRINT_L0("Initializing p2p server..."); + res = p2psrv.init(vm); + CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize p2p server."); + LOG_PRINT_L0("P2p server initialized OK"); + + LOG_PRINT_L0("Initializing cryptonote protocol..."); + res = cprotocol.init(vm); + CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize cryptonote protocol."); + LOG_PRINT_L0("Cryptonote protocol initialized OK"); + + LOG_PRINT_L0("Initializing core rpc server..."); + res = rpc_server.init(vm); + CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core rpc server."); + LOG_PRINT_GREEN("Core rpc server initialized OK on port: " << rpc_server.get_binded_port(), LOG_LEVEL_0); + + //initialize core here + LOG_PRINT_L0("Initializing core..."); + res = ccore.init(vm); + CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core"); + LOG_PRINT_L0("Core initialized OK"); + + // start components + dch.start_handling(); + + LOG_PRINT_L0("Starting core rpc server..."); + res = rpc_server.run(2, false); + CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core rpc server."); + LOG_PRINT_L0("Core rpc server started ok"); + + LOG_PRINT_L0("Starting p2p net loop..."); + p2psrv.run(); + LOG_PRINT_L0("p2p net loop stopped"); + + //stop components + LOG_PRINT_L0("Stopping core rpc server..."); + rpc_server.send_stop_signal(); + rpc_server.timed_wait_server_stop(5000); + + //deinitialize components + LOG_PRINT_L0("Deinitializing core..."); + ccore.deinit(); + LOG_PRINT_L0("Deinitializing rpc server ..."); + rpc_server.deinit(); + LOG_PRINT_L0("Deinitializing cryptonote_protocol..."); + cprotocol.deinit(); + LOG_PRINT_L0("Deinitializing p2p..."); + p2psrv.deinit(); + + + ccore.set_cryptonote_protocol(NULL); + cprotocol.set_p2p_endpoint(NULL); + + LOG_PRINT("Node stopped.", LOG_LEVEL_0); + return 0; + + CATCH_ENTRY_L0("main", 1); +} + +bool command_line_preprocessor(const boost::program_options::variables_map& vm) +{ + bool exit = false; + if (command_line::get_arg(vm, command_line::arg_version)) + { + std::cout << CRYPTONOTE_NAME << PROJECT_VERSION_LONG << ENDL; + exit = true; + } + if (command_line::get_arg(vm, arg_os_version)) + { + std::cout << "OS: " << tools::get_os_version_string() << ENDL; + exit = true; + } + + if (exit) + { + return true; + } + + int new_log_level = command_line::get_arg(vm, arg_log_level); + if(new_log_level < LOG_LEVEL_MIN || new_log_level > LOG_LEVEL_MAX) + { + LOG_PRINT_L0("Wrong log level value: "); + } + else if (log_space::get_set_log_detalisation_level(false) != new_log_level) + { + log_space::get_set_log_detalisation_level(true, new_log_level); + LOG_PRINT_L0("LOG_LEVEL set to " << new_log_level); + } + + return false; +} diff --git a/src/daemon/daemon_commands_handler.h b/src/daemon/daemon_commands_handler.h new file mode 100644 index 000000000..2edd5d414 --- /dev/null +++ b/src/daemon/daemon_commands_handler.h @@ -0,0 +1,291 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <boost/lexical_cast.hpp> + +#include "console_handler.h" +#include "p2p/net_node.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "common/util.h" +#include "crypto/hash.h" + + +class daemon_cmmands_handler +{ + nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >& m_srv; +public: + daemon_cmmands_handler(nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >& srv):m_srv(srv) + { + m_cmd_binder.set_handler("help", boost::bind(&daemon_cmmands_handler::help, this, _1), "Show this help"); + m_cmd_binder.set_handler("print_pl", boost::bind(&daemon_cmmands_handler::print_pl, this, _1), "Print peer list"); + m_cmd_binder.set_handler("print_cn", boost::bind(&daemon_cmmands_handler::print_cn, this, _1), "Print connections"); + m_cmd_binder.set_handler("print_bc", boost::bind(&daemon_cmmands_handler::print_bc, this, _1), "Print blockchain info in a given blocks range, print_bc <begin_height> [<end_height>]"); + //m_cmd_binder.set_handler("print_bci", boost::bind(&daemon_cmmands_handler::print_bci, this, _1)); + //m_cmd_binder.set_handler("print_bc_outs", boost::bind(&daemon_cmmands_handler::print_bc_outs, this, _1)); + m_cmd_binder.set_handler("print_block", boost::bind(&daemon_cmmands_handler::print_block, this, _1), "Print block, print_block <block_hash> | <block_height>"); + m_cmd_binder.set_handler("print_tx", boost::bind(&daemon_cmmands_handler::print_tx, this, _1), "Print transaction, print_tx <transaction_hash>"); + m_cmd_binder.set_handler("start_mining", boost::bind(&daemon_cmmands_handler::start_mining, this, _1), "Start mining for specified address, start_mining <addr> [threads=1]"); + m_cmd_binder.set_handler("stop_mining", boost::bind(&daemon_cmmands_handler::stop_mining, this, _1), "Stop mining"); + m_cmd_binder.set_handler("print_pool", boost::bind(&daemon_cmmands_handler::print_pool, this, _1), "Print transaction pool (long format)"); + m_cmd_binder.set_handler("print_pool_sh", boost::bind(&daemon_cmmands_handler::print_pool_sh, this, _1), "Print transaction pool (short format)"); + 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"); + } + + bool start_handling() + { + m_cmd_binder.start_handling(&m_srv, ""); + return true; + } + +private: + epee::srv_console_handlers_binder<nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> > > m_cmd_binder; + + //-------------------------------------------------------------------------------- + std::string get_commands_str() + { + std::stringstream ss; + ss << "Commands: " << ENDL; + std::string usage = m_cmd_binder.get_usage(); + boost::replace_all(usage, "\n", "\n "); + usage.insert(0, " "); + ss << usage << ENDL; + return ss.str(); + } + //-------------------------------------------------------------------------------- + bool help(const std::vector<std::string>& args) + { + std::cout << get_commands_str() << ENDL; + return true; + } + //-------------------------------------------------------------------------------- + bool print_pl(const std::vector<std::string>& args) + { + m_srv.log_peerlist(); + return true; + } + //-------------------------------------------------------------------------------- + bool save(const std::vector<std::string>& args) + { + m_srv.get_payload_object().get_core().get_blockchain_storage().store_blockchain(); + return true; + } + //-------------------------------------------------------------------------------- + bool show_hr(const std::vector<std::string>& args) + { + m_srv.get_payload_object().get_core().get_miner().do_print_hashrate(true); + return true; + } + //-------------------------------------------------------------------------------- + bool hide_hr(const std::vector<std::string>& args) + { + m_srv.get_payload_object().get_core().get_miner().do_print_hashrate(false); + return true; + } + //-------------------------------------------------------------------------------- + bool print_bc_outs(const std::vector<std::string>& args) + { + if(args.size() != 1) + { + std::cout << "need file path as parameter" << ENDL; + return true; + } + m_srv.get_payload_object().get_core().print_blockchain_outs(args[0]); + return true; + } + //-------------------------------------------------------------------------------- + bool print_cn(const std::vector<std::string>& args) + { + m_srv.log_connections(); + return true; + } + //-------------------------------------------------------------------------------- + bool print_bc(const std::vector<std::string>& args) + { + if(!args.size()) + { + std::cout << "need block index parameter" << ENDL; + return false; + } + uint64_t start_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])) + { + std::cout << "wrong end block index parameter" << ENDL; + return false; + } + + m_srv.get_payload_object().get_core().print_blockchain(start_index, end_block_parametr); + return true; + } + //-------------------------------------------------------------------------------- + bool print_bci(const std::vector<std::string>& args) + { + m_srv.get_payload_object().get_core().print_blockchain_index(); + return true; + } + //-------------------------------------------------------------------------------- + template <typename T> + static bool print_as_json(T& obj) + { + std::cout << cryptonote::obj_to_json_str(obj) << ENDL; + return true; + } + //-------------------------------------------------------------------------------- + bool print_block_by_height(uint64_t height) + { + std::list<cryptonote::block> blocks; + m_srv.get_payload_object().get_core().get_blocks(height, 1, blocks); + + if (1 == blocks.size()) + { + cryptonote::block& block = blocks.front(); + std::cout << "block_id: " << get_block_hash(block) << ENDL; + print_as_json(block); + } + else + { + uint64_t current_height; + crypto::hash top_id; + m_srv.get_payload_object().get_core().get_blockchain_top(current_height, top_id); + std::cout << "block wasn't found. Current block chain height: " << current_height << ", requested: " << height << std::endl; + return false; + } + + return true; + } + //-------------------------------------------------------------------------------- + bool print_block_by_hash(const std::string& arg) + { + crypto::hash block_hash; + if (!parse_hash256(arg, block_hash)) + { + return false; + } + + std::list<crypto::hash> block_ids; + block_ids.push_back(block_hash); + std::list<cryptonote::block> blocks; + std::list<crypto::hash> missed_ids; + m_srv.get_payload_object().get_core().get_blocks(block_ids, blocks, missed_ids); + + if (1 == blocks.size()) + { + cryptonote::block block = blocks.front(); + print_as_json(block); + } + else + { + std::cout << "block wasn't found: " << arg << std::endl; + return false; + } + + return true; + } + //-------------------------------------------------------------------------------- + bool print_block(const std::vector<std::string>& args) + { + if (args.empty()) + { + std::cout << "expected: print_block (<block_hash> | <block_height>)" << std::endl; + return true; + } + + const std::string& arg = args.front(); + try + { + uint64_t height = boost::lexical_cast<uint64_t>(arg); + print_block_by_height(height); + } + catch (boost::bad_lexical_cast&) + { + print_block_by_hash(arg); + } + + return true; + } + //-------------------------------------------------------------------------------- + bool print_tx(const std::vector<std::string>& args) + { + if (args.empty()) + { + std::cout << "expected: print_tx <transaction hash>" << std::endl; + return true; + } + + const std::string& str_hash = args.front(); + crypto::hash tx_hash; + if (!parse_hash256(str_hash, tx_hash)) + { + return true; + } + + std::vector<crypto::hash> tx_ids; + tx_ids.push_back(tx_hash); + std::list<cryptonote::transaction> txs; + std::list<crypto::hash> missed_ids; + m_srv.get_payload_object().get_core().get_transactions(tx_ids, txs, missed_ids); + + if (1 == txs.size()) + { + cryptonote::transaction tx = txs.front(); + print_as_json(tx); + } + else + { + std::cout << "transaction wasn't found: <" << str_hash << '>' << std::endl; + } + + return true; + } + //-------------------------------------------------------------------------------- + bool print_pool(const std::vector<std::string>& args) + { + LOG_PRINT_L0("Pool state: " << ENDL << m_srv.get_payload_object().get_core().print_pool(false)); + return true; + } + //-------------------------------------------------------------------------------- + bool print_pool_sh(const std::vector<std::string>& args) + { + LOG_PRINT_L0("Pool state: " << ENDL << m_srv.get_payload_object().get_core().print_pool(true)); + return true; + } //-------------------------------------------------------------------------------- + bool start_mining(const std::vector<std::string>& args) + { + if(!args.size()) + { + std::cout << "target account address for mining is not set" << std::endl; + return true; + } + + cryptonote::account_public_address adr; + if(!cryptonote::get_account_address_from_str(adr, args.front())) + { + std::cout << "target account address has wrong format" << std::endl; + return true; + } + size_t threads_count = 1; + if(args.size() > 1) + { + string_tools::get_xtype_from_string(threads_count, args[1]); + } + + m_srv.get_payload_object().get_core().get_miner().start(adr, threads_count); + return true; + } + //-------------------------------------------------------------------------------- + bool stop_mining(const std::vector<std::string>& args) + { + m_srv.get_payload_object().get_core().get_miner().stop(); + return true; + } +}; diff --git a/src/miner/simpleminer.cpp b/src/miner/simpleminer.cpp new file mode 100644 index 000000000..a9f0ceab0 --- /dev/null +++ b/src/miner/simpleminer.cpp @@ -0,0 +1,209 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "common/command_line.h" +#include "misc_log_ex.h" +#include "simpleminer.h" +#include "target_helper.h" +#include "net/http_server_handlers_map2.h" +#include "simpleminer_protocol_defs.h" +#include "storages/http_abstract_invoke.h" +#include "string_tools.h" +#include "cryptonote_core/account.h" +#include "cryptonote_core/cryptonote_format_utils.h" + +using namespace epee; +namespace po = boost::program_options; + +int main(int argc, char** argv) +{ + string_tools::set_module_name_and_folder(argv[0]); + + //set up logging options + log_space::get_set_log_detalisation_level(true, LOG_LEVEL_4); + log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); + log_space::log_singletone::add_logger(LOGGER_FILE, + log_space::log_singletone::get_default_log_file().c_str(), + log_space::log_singletone::get_default_log_folder().c_str()); + + po::options_description desc("Allowed options"); + command_line::add_arg(desc, command_line::arg_help); + mining::simpleminer::init_options(desc); + + //uint32_t t = mining::get_target_for_difficulty(700000); + + po::variables_map vm; + bool r = command_line::handle_error_helper(desc, [&]() + { + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + if (command_line::get_arg(vm, command_line::arg_help)) + { + std::cout << desc << std::endl; + return false; + } + + return true; + }); + if (!r) + return 1; + + mining::simpleminer miner; + r = miner.init(vm); + r = r && miner.run(); // Never returns... + + return 0; +} + + + +namespace mining +{ + const command_line::arg_descriptor<std::string, true> arg_pool_addr = {"pool-addr", ""}; + const command_line::arg_descriptor<std::string, true> arg_login = {"login", ""}; + const command_line::arg_descriptor<std::string, true> arg_pass = {"pass", ""}; + + //----------------------------------------------------------------------------------------------------- + void simpleminer::init_options(boost::program_options::options_description& desc) + { + command_line::add_arg(desc, arg_pool_addr); + command_line::add_arg(desc, arg_login); + command_line::add_arg(desc, arg_pass); + } + bool simpleminer::init(const boost::program_options::variables_map& vm) + { + std::string pool_addr = command_line::get_arg(vm, arg_pool_addr); + //parse ip and address + std::string::size_type p = pool_addr.find(':'); + CHECK_AND_ASSERT_MES(p != std::string::npos && (p + 1 != pool_addr.size()), false, "Wrong srv address syntax"); + m_pool_ip = pool_addr.substr(0, p); + m_pool_port = pool_addr.substr(p + 1, pool_addr.size()); + m_login = command_line::get_arg(vm, arg_login); + m_pass = command_line::get_arg(vm, arg_pass); + return true; + } + + bool simpleminer::text_job_details_to_native_job_details(const job_details& job, simpleminer::job_details_native& native_details) + { + bool r = epee::string_tools::parse_hexstr_to_binbuff(job.blob, native_details.blob); + CHECK_AND_ASSERT_MES(r, false, "wrong buffer sent from pool server"); + r = epee::string_tools::parse_tpod_from_hex_string(job.target, native_details.target); + CHECK_AND_ASSERT_MES(r, false, "wrong buffer sent from pool server"); + native_details.job_id = job.job_id; + return true; + } + + bool simpleminer::run() + { + std::string pool_session_id; + simpleminer::job_details_native job = AUTO_VAL_INIT(job); + uint64_t last_job_ticks = 0; + + while(true) + { + //----------------- + last_job_ticks = epee::misc_utils::get_tick_count(); + 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)) + { + LOG_PRINT_L0("Failed to connect " << m_pool_ip << ":" << m_pool_port << ", sleep...."); + epee::misc_utils::sleep_no_w(1000); + continue; + } + //DO AUTH + LOG_PRINT_L0("Connected " << m_pool_ip << ":" << m_pool_port << " OK"); + COMMAND_RPC_LOGIN::request req = AUTO_VAL_INIT(req); + req.login = m_login; + req.pass = m_pass; + COMMAND_RPC_LOGIN::response resp = AUTO_VAL_INIT(resp); + if(!epee::net_utils::invoke_http_json_rpc<mining::COMMAND_RPC_LOGIN>("/", req, resp, m_http_client)) + { + LOG_PRINT_L0("Failed to invoke login " << m_pool_ip << ":" << m_pool_port << ", disconnect and sleep...."); + m_http_client.disconnect(); + epee::misc_utils::sleep_no_w(1000); + continue; + } + if(resp.status != "OK" || resp.id.empty()) + { + LOG_PRINT_L0("Failed to login " << m_pool_ip << ":" << m_pool_port << ", disconnect and sleep...."); + m_http_client.disconnect(); + epee::misc_utils::sleep_no_w(1000); + continue; + } + pool_session_id = resp.id; + //78 + if(!text_job_details_to_native_job_details(resp.job, job)) + { + LOG_PRINT_L0("Failed to text_job_details_to_native_job_details(), disconnect and sleep...."); + m_http_client.disconnect(); + epee::misc_utils::sleep_no_w(1000); + continue; + } + last_job_ticks = epee::misc_utils::get_tick_count(); + + } + while(epee::misc_utils::get_tick_count() - last_job_ticks < 20000) + { + //uint32_t c = (*((uint32_t*)&job.blob.data()[39])); + ++(*((uint32_t*)&job.blob.data()[39])); + crypto::hash h = cryptonote::null_hash; + crypto::cn_slow_hash(job.blob.data(), job.blob.size(), h); + if( ((uint32_t*)&h)[7] < job.target ) + { + //found! + + COMMAND_RPC_SUBMITSHARE::request submit_request = AUTO_VAL_INIT(submit_request); + COMMAND_RPC_SUBMITSHARE::response submit_response = AUTO_VAL_INIT(submit_response); + submit_request.id = pool_session_id; + submit_request.job_id = job.job_id; + submit_request.nonce = epee::string_tools::pod_to_hex((*((uint32_t*)&job.blob.data()[78]))); + LOG_PRINT_L0("Share found: nonce=" << submit_request.nonce << " for job=" << job.job_id << ", submitting..."); + if(!epee::net_utils::invoke_http_json_rpc<mining::COMMAND_RPC_SUBMITSHARE>("/", submit_request, submit_response, m_http_client)) + { + LOG_PRINT_L0("Failed to submit share! disconnect and sleep...."); + m_http_client.disconnect(); + epee::misc_utils::sleep_no_w(1000); + break; + } + if(submit_response.status != "OK") + { + LOG_PRINT_L0("Failed to submit share! (submitted share rejected) disconnect and sleep...."); + m_http_client.disconnect(); + epee::misc_utils::sleep_no_w(1000); + break; + } + LOG_PRINT_GREEN("Share submitted successfully!", LOG_LEVEL_0); + break; + } + } + //get next job + COMMAND_RPC_GETJOB::request getjob_request = AUTO_VAL_INIT(getjob_request); + COMMAND_RPC_GETJOB::response getjob_response = AUTO_VAL_INIT(getjob_response); + getjob_request.id = pool_session_id; + LOG_PRINT_L0("Getting next job..."); + if(!epee::net_utils::invoke_http_json_rpc<mining::COMMAND_RPC_GETJOB>("/", getjob_request, getjob_response, m_http_client)) + { + LOG_PRINT_L0("Can't get new job! Disconnect and sleep...."); + m_http_client.disconnect(); + epee::misc_utils::sleep_no_w(1000); + break; + } + if(!text_job_details_to_native_job_details(getjob_response, job)) + { + LOG_PRINT_L0("Failed to text_job_details_to_native_job_details(), disconnect and sleep...."); + m_http_client.disconnect(); + epee::misc_utils::sleep_no_w(1000); + continue; + } + last_job_ticks = epee::misc_utils::get_tick_count(); + } + + return true; + + } +} diff --git a/src/miner/simpleminer.h b/src/miner/simpleminer.h new file mode 100644 index 000000000..803ea12fb --- /dev/null +++ b/src/miner/simpleminer.h @@ -0,0 +1,33 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include "net/http_client.h" +#include "cryptonote_protocol/blobdatatype.h" +#include "simpleminer_protocol_defs.h" +namespace mining +{ + class simpleminer + { + public: + static void init_options(boost::program_options::options_description& desc); + bool init(const boost::program_options::variables_map& vm); + bool run(); + private: + struct job_details_native + { + cryptonote::blobdata blob; + uint32_t target; + std::string job_id; + }; + + static bool text_job_details_to_native_job_details(const job_details& job, job_details_native& native_details); + + std::string m_pool_ip; + std::string m_pool_port; + std::string m_login; + std::string m_pass; + epee::net_utils::http::http_simple_client m_http_client; + }; +} diff --git a/src/miner/simpleminer_protocol_defs.h b/src/miner/simpleminer_protocol_defs.h new file mode 100644 index 000000000..9b70f8cbf --- /dev/null +++ b/src/miner/simpleminer_protocol_defs.h @@ -0,0 +1,104 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include "cryptonote_protocol/cryptonote_protocol_defs.h" +#include "cryptonote_core/cryptonote_basic.h" +#include "crypto/hash.h" +#include "net/rpc_method_name.h" + +namespace mining +{ + //----------------------------------------------- +#define CORE_RPC_STATUS_OK "OK" + + + struct job_details + { + std::string blob; + std::string target; + std::string job_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(blob) + KV_SERIALIZE(target) + KV_SERIALIZE(job_id) + END_KV_SERIALIZE_MAP() + }; + + + struct COMMAND_RPC_LOGIN + { + RPC_METHOD_NAME("login"); + + struct request + { + std::string login; + std::string pass; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(login) + KV_SERIALIZE(pass) + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + std::string status; + std::string id; + job_details job; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(id) + KV_SERIALIZE(job) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_GETJOB + { + RPC_METHOD_NAME("getjob"); + + struct request + { + std::string id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(id) + END_KV_SERIALIZE_MAP() + }; + + typedef job_details response; + }; + + struct COMMAND_RPC_SUBMITSHARE + { + RPC_METHOD_NAME("submit"); + + struct request + { + std::string id; + std::string nonce; + std::string job_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(id) + KV_SERIALIZE(nonce) + KV_SERIALIZE(job_id) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; +} + diff --git a/src/miner/target_helper.h b/src/miner/target_helper.h new file mode 100644 index 000000000..5ac6eed14 --- /dev/null +++ b/src/miner/target_helper.h @@ -0,0 +1,18 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include "cryptonote_core/difficulty.h" + + +namespace mining +{ + inline uint32_t get_target_for_difficulty(cryptonote::difficulty_type difficulty) + { + if(!difficulty) + return 0xffffffff; + return 0xffffffff/static_cast<uint32_t>(difficulty); + } + +} diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h new file mode 100644 index 000000000..32249a6d1 --- /dev/null +++ b/src/p2p/net_node.h @@ -0,0 +1,204 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include <boost/thread.hpp> +#include <boost/bind.hpp> +#include <boost/foreach.hpp> +#include <boost/bimap.hpp> +#include <boost/multi_index_container.hpp> +#include <boost/multi_index/ordered_index.hpp> +#include <boost/multi_index/identity.hpp> +#include <boost/multi_index/member.hpp> +#include <boost/multi_index/global_fun.hpp> +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/variables_map.hpp> +#include <boost/serialization/version.hpp> + +#include "warnings.h" +#include "net/levin_server_cp2.h" +#include "p2p_protocol_defs.h" +#include "storages/levin_abstract_invoke2.h" +#include "net_peerlist.h" +#include "p2p_networks.h" +#include "math_helper.h" +#include "net_node_common.h" + +using namespace epee; + +PUSH_WARNINGS +DISABLE_VS_WARNINGS(4355) + +namespace nodetool +{ + template<class base_type> + struct p2p_connection_context_t: base_type //t_payload_net_handler::connection_context //public net_utils::connection_context_base + { + peerid_type peer_id; + }; + + 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> >, + public i_p2p_endpoint<typename t_payload_net_handler::connection_context> + { + struct by_conn_id{}; + struct by_peer_id{}; + struct by_addr{}; + + typedef p2p_connection_context_t<typename t_payload_net_handler::connection_context> p2p_connection_context; + + typedef COMMAND_HANDSHAKE_T<typename t_payload_net_handler::payload_type> COMMAND_HANDSHAKE; + typedef COMMAND_TIMED_SYNC_T<typename t_payload_net_handler::payload_type> COMMAND_TIMED_SYNC; + + public: + typedef t_payload_net_handler payload_net_handler; + // Some code + node_server(t_payload_net_handler& payload_handler):m_payload_handler(payload_handler), m_allow_local_ip(false), m_hide_my_port(false) + {} + + static void init_options(boost::program_options::options_description& desc); + + bool run(); + bool init(const boost::program_options::variables_map& vm); + bool deinit(); + bool send_stop_signal(); + uint32_t get_this_peer_port(){return m_listenning_port;} + t_payload_net_handler& get_payload_object(); + + template <class Archive, class t_version_type> + void serialize(Archive &a, const t_version_type ver) + { + a & m_peerlist; + a & m_config.m_peer_id; + } + // debug functions + bool log_peerlist(); + bool log_connections(); + virtual uint64_t get_connections_count(); + size_t get_outgoing_connections_count(); + peerlist_manager& get_peerlist_manager(){return m_peerlist;} + private: + typedef COMMAND_REQUEST_STAT_INFO_T<typename t_payload_net_handler::stat_info> COMMAND_REQUEST_STAT_INFO; + + CHAIN_LEVIN_INVOKE_MAP2(p2p_connection_context); //move levin_commands_handler interface invoke(...) callbacks into invoke map + CHAIN_LEVIN_NOTIFY_MAP2(p2p_connection_context); //move levin_commands_handler interface notify(...) callbacks into nothing + + BEGIN_INVOKE_MAP2(node_server) + HANDLE_INVOKE_T2(COMMAND_HANDSHAKE, &node_server::handle_handshake) + HANDLE_INVOKE_T2(COMMAND_TIMED_SYNC, &node_server::handle_timed_sync) + HANDLE_INVOKE_T2(COMMAND_PING, &node_server::handle_ping) +#ifdef ALLOW_DEBUG_COMMANDS + HANDLE_INVOKE_T2(COMMAND_REQUEST_STAT_INFO, &node_server::handle_get_stat_info) + HANDLE_INVOKE_T2(COMMAND_REQUEST_NETWORK_STATE, &node_server::handle_get_network_state) + HANDLE_INVOKE_T2(COMMAND_REQUEST_PEER_ID, &node_server::handle_get_peer_id) +#endif + CHAIN_INVOKE_MAP_TO_OBJ_FORCE_CONTEXT(m_payload_handler, typename t_payload_net_handler::connection_context&) + END_INVOKE_MAP2() + + //----------------- commands handlers ---------------------------------------------- + int handle_handshake(int command, typename COMMAND_HANDSHAKE::request& arg, typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context); + int handle_timed_sync(int command, typename COMMAND_TIMED_SYNC::request& arg, typename COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context); + int handle_ping(int command, COMMAND_PING::request& arg, COMMAND_PING::response& rsp, p2p_connection_context& context); +#ifdef ALLOW_DEBUG_COMMANDS + int handle_get_stat_info(int command, typename COMMAND_REQUEST_STAT_INFO::request& arg, typename COMMAND_REQUEST_STAT_INFO::response& rsp, p2p_connection_context& context); + int handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, p2p_connection_context& context); + int handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, p2p_connection_context& context); +#endif + bool init_config(); + bool make_default_config(); + bool store_config(); + bool check_trust(const proof_of_trust& tr); + + + //----------------- levin_commands_handler ------------------------------------------------------------- + virtual void on_connection_new(p2p_connection_context& context); + virtual void on_connection_close(p2p_connection_context& context); + virtual void callback(p2p_connection_context& context); + //----------------- i_p2p_endpoint ------------------------------------------------------------- + virtual bool relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context); + virtual bool invoke_command_to_peer(int command, const std::string& req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context); + virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const epee::net_utils::connection_context_base& context); + virtual bool drop_connection(const epee::net_utils::connection_context_base& context); + virtual void request_callback(const epee::net_utils::connection_context_base& context); + virtual void for_each_connection(std::function<bool(typename t_payload_net_handler::connection_context&)> f); + //----------------------------------------------------------------------------------------------- + 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 get_local_node_data(basic_node_data& node_data); + //bool get_local_handshake_data(handshake_data& hshd); + + bool merge_peerlist_with_local(const std::list<peerlist_entry>& bs); + bool fix_time_delta(std::list<peerlist_entry>& local_peerlist, time_t local_time, int64_t& delta); + + 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 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); + size_t get_random_index_with_fixed_probability(size_t max_index); + bool is_peer_used(const peerlist_entry& peer); + bool is_addr_connected(const net_address& peer); + 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); + + //debug functions + std::string print_connections_container(); + + + typedef net_utils::boosted_tcp_server<levin::async_protocol_handler<p2p_connection_context> > net_server; + + struct config + { + network_config m_net_config; + uint64_t m_peer_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(m_net_config) + KV_SERIALIZE(m_peer_id) + END_KV_SERIALIZE_MAP() + }; + + config m_config; + std::string m_config_folder; + + bool m_have_address; + bool m_first_connection_maker_call; + uint32_t m_listenning_port; + uint32_t m_external_port; + uint32_t m_ip_address; + bool m_allow_local_ip; + bool m_hide_my_port; + + //critical_section m_connections_lock; + //connections_indexed_container m_connections; + + 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; + + std::string m_bind_ip; + std::string m_port; +#ifdef ALLOW_DEBUG_COMMANDS + uint64_t m_last_stat_request_time; +#endif + std::list<net_address> m_priority_peers; + std::vector<net_address> m_seed_nodes; + std::list<nodetool::peerlist_entry> m_command_line_peers; + uint64_t m_peer_livetime; + //keep connections to initiate some interactions + net_server m_net_server; + }; +} + +#include "net_node.inl" + +POP_WARNINGS diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl new file mode 100644 index 000000000..a5e534f8a --- /dev/null +++ b/src/p2p/net_node.inl @@ -0,0 +1,1020 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include "version.h" +#include "string_tools.h" +#include "common/command_line.h" +#include "common/util.h" +#include "net/net_helper.h" +#include "math_helper.h" +#include "p2p_protocol_defs.h" +#include "net_peerlist_boost_serialization.h" +#include "net/local_ip.h" +#include "crypto/crypto.h" +#include "storages/levin_abstract_invoke2.h" +#define NET_MAKE_IP(b1,b2,b3,b4) ((LPARAM)(((DWORD)(b1)<<24)+((DWORD)(b2)<<16)+((DWORD)(b3)<<8)+((DWORD)(b4)))) + + +namespace nodetool +{ + namespace + { + const command_line::arg_descriptor<std::string> arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol", "0.0.0.0"}; + const command_line::arg_descriptor<std::string> arg_p2p_bind_port = {"p2p-bind-port", "Port for p2p network protocol", boost::to_string(P2P_DEFAULT_PORT)}; + const command_line::arg_descriptor<uint32_t> arg_p2p_external_port = {"p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0}; + 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_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}; } + + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + void node_server<t_payload_net_handler>::init_options(boost::program_options::options_description& desc) + { + command_line::add_arg(desc, arg_p2p_bind_ip); + command_line::add_arg(desc, arg_p2p_bind_port); + command_line::add_arg(desc, arg_p2p_external_port); + 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_seed_node); + command_line::add_arg(desc, arg_p2p_hide_my_port); } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::init_config() + { + // + TRY_ENTRY(); + std::string state_file_path = m_config_folder + "/" + P2P_NET_DATA_FILENAME; + std::ifstream p2p_data; + p2p_data.open( state_file_path , std::ios_base::binary | std::ios_base::in); + if(!p2p_data.fail()) + { + boost::archive::binary_iarchive a(p2p_data); + a >> *this; + }else + { + make_default_config(); + } + + //at this moment we have hardcoded config + m_config.m_net_config.handshake_interval = P2P_DEFAULT_HANDSHAKE_INTERVAL; + m_config.m_net_config.connections_count = P2P_DEFAULT_CONNECTIONS_COUNT; + m_config.m_net_config.packet_max_size = P2P_DEFAULT_PACKET_MAX_SIZE; //20 MB limit + m_config.m_net_config.config_id = 0; // initial config + m_config.m_net_config.connection_timeout = P2P_DEFAULT_CONNECTION_TIMEOUT; + m_config.m_net_config.ping_connection_timeout = P2P_DEFAULT_PING_CONNECTION_TIMEOUT; + m_config.m_net_config.send_peerlist_sz = P2P_DEFAULT_PEERS_IN_HANDSHAKE; + + m_first_connection_maker_call = true; + CATCH_ENTRY_L0("node_server::init_config", false); + return true; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + void node_server<t_payload_net_handler>::for_each_connection(std::function<bool(typename t_payload_net_handler::connection_context&)> f) + { + m_net_server.get_config_object().foreach_connection([&](p2p_connection_context& cntx){ + return f(cntx); + }); + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::make_default_config() + { + m_config.m_peer_id = crypto::rand<uint64_t>(); + return true; + } + //----------------------------------------------------------------------------------- + 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); + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::handle_command_line(const boost::program_options::variables_map& vm) + { + m_bind_ip = command_line::get_arg(vm, arg_p2p_bind_ip); + m_port = command_line::get_arg(vm, arg_p2p_bind_port); + m_external_port = command_line::get_arg(vm, arg_p2p_external_port); + m_allow_local_ip = command_line::get_arg(vm, arg_p2p_allow_local_ip); + + if (command_line::has_arg(vm, arg_p2p_add_peer)) + { + std::vector<std::string> perrs = command_line::get_arg(vm, arg_p2p_add_peer); + for(const std::string& pr_str: perrs) + { + nodetool::peerlist_entry pe = AUTO_VAL_INIT(pe); + pe.id = crypto::rand<uint64_t>(); + bool r = parse_peer_from_string(pe.adr, pr_str); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str); + m_command_line_peers.push_back(pe); + } + } + + 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_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(command_line::has_arg(vm, arg_p2p_hide_my_port)) + m_hide_my_port = true; return true; + } + //----------------------------------------------------------------------------------- +#define ADD_HARDCODED_SEED_NODE(addr_str) { nodetool::net_address na = AUTO_VAL_INIT(na);bool r = parse_peer_from_string(na, addr_str); \ + CHECK_AND_ASSERT_MES(r, false, "Failed to parse seed address from string: " << addr_str); m_seed_nodes.push_back(na); } + + + 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("85.25.201.95:8080"); + ADD_HARDCODED_SEED_NODE("85.25.196.145:8080"); + ADD_HARDCODED_SEED_NODE("85.25.196.146:8080"); + ADD_HARDCODED_SEED_NODE("85.25.196.144:8080"); + ADD_HARDCODED_SEED_NODE("5.199.168.138:8080"); + ADD_HARDCODED_SEED_NODE("62.75.236.152:8080"); + ADD_HARDCODED_SEED_NODE("85.25.194.245:8080"); + ADD_HARDCODED_SEED_NODE("95.211.224.160:8080"); + ADD_HARDCODED_SEED_NODE("144.76.200.44:8080"); + + bool res = handle_command_line(vm); + CHECK_AND_ASSERT_MES(res, false, "Failed to handle command line"); + m_config_folder = command_line::get_arg(vm, command_line::arg_data_dir); + + res = init_config(); + CHECK_AND_ASSERT_MES(res, false, "Failed to init config."); + + res = m_peerlist.init(m_allow_local_ip); + CHECK_AND_ASSERT_MES(res, false, "Failed to init peerlist."); + + + for(auto& p: m_command_line_peers) + m_peerlist.append_with_peer_white(p); + + //only in case if we really sure that we have external visible ip + m_have_address = true; + m_ip_address = 0; + m_last_stat_request_time = 0; + + //configure self + m_net_server.set_threads_prefix("P2P"); + m_net_server.get_config_object().m_pcommands_handler = this; + m_net_server.get_config_object().m_invoke_timeout = P2P_DEFAULT_INVOKE_TIMEOUT; + + //try to bind + LOG_PRINT_L0("Binding on " << m_bind_ip << ":" << m_port); + res = m_net_server.init_server(m_port, m_bind_ip); + CHECK_AND_ASSERT_MES(res, false, "Failed to bind server"); + + m_listenning_port = m_net_server.get_binded_port(); + LOG_PRINT_GREEN("Net service binded on " << m_bind_ip << ":" << m_listenning_port, LOG_LEVEL_0); + if(m_external_port) + LOG_PRINT_L0("External port defined as " << m_external_port); + return res; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + typename node_server<t_payload_net_handler>::payload_net_handler& node_server<t_payload_net_handler>::get_payload_object() + { + return m_payload_handler; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::run() + { + //here you can set worker threads count + int thrds_count = 10; + + m_net_server.add_idle_handler(boost::bind(&node_server<t_payload_net_handler>::idle_worker, this), 1000); + m_net_server.add_idle_handler(boost::bind(&t_payload_net_handler::on_idle, &m_payload_handler), 1000); + + //go to loop + LOG_PRINT("Run net_service loop( " << thrds_count << " threads)...", LOG_LEVEL_0); + if(!m_net_server.run_server(thrds_count)) + { + LOG_ERROR("Failed to run net tcp server!"); + } + + LOG_PRINT("net_service loop stopped.", LOG_LEVEL_0); + return true; + } + + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + uint64_t node_server<t_payload_net_handler>::get_connections_count() + { + return m_net_server.get_config_object().get_connections_count(); + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::deinit() + { + m_peerlist.deinit(); + m_net_server.deinit_server(); + return store_config(); + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::store_config() + { + + TRY_ENTRY(); + if (!tools::create_directories_if_necessary(m_config_folder)) + { + LOG_PRINT_L0("Failed to create data directory: " << m_config_folder); + return false; + } + + std::string state_file_path = m_config_folder + "/" + P2P_NET_DATA_FILENAME; + std::ofstream p2p_data; + p2p_data.open( state_file_path , std::ios_base::binary | std::ios_base::out| std::ios::trunc); + if(p2p_data.fail()) + { + LOG_PRINT_L0("Failed to save config to file " << state_file_path); + return false; + }; + + boost::archive::binary_oarchive a(p2p_data); + a << *this; + return true; + CATCH_ENTRY_L0("blockchain_storage::save", false); + + return true; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::send_stop_signal() + { + m_net_server.send_stop_signal(); + LOG_PRINT_L0("[node] Stop signal sent"); + return true; + } + //----------------------------------------------------------------------------------- + + + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::do_handshake_with_peer(peerid_type& pi, p2p_connection_context& context_, bool just_take_peerlist) + { + typename COMMAND_HANDSHAKE::request arg; + typename COMMAND_HANDSHAKE::response rsp; + get_local_node_data(arg.node_data); + m_payload_handler.get_payload_sync_data(arg.payload_data); + + 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(), + [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.rise();}); + + if(code < 0) + { + LOG_PRINT_CC_RED(context, "COMMAND_HANDSHAKE invoke failed. (" << code << ", " << 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."); + return; + } + + if(!handle_remote_peerlist(rsp.local_peerlist, rsp.node_data.local_time, context)) + { + LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE: failed to handle_remote_peerlist(...), closing connection."); + return; + } + hsh_result = true; + if(!just_take_peerlist) + { + if(!m_payload_handler.process_payload_sync_data(rsp.payload_data, context, true)) + { + LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE invoked, but process_payload_sync_data returned false, dropping connection."); + hsh_result = false; + return; + } + + pi = context.peer_id = rsp.node_data.peer_id; + m_peerlist.set_peer_just_seen(rsp.node_data.peer_id, context.m_remote_ip, context.m_remote_port); + + if(rsp.node_data.peer_id == m_config.m_peer_id) + { + LOG_PRINT_CCONTEXT_L2("Connection to self detected, dropping connection"); + hsh_result = false; + return; + } + LOG_PRINT_CCONTEXT_L0(" COMMAND_HANDSHAKE INVOKED OK"); + }else + { + LOG_PRINT_CCONTEXT_L0(" COMMAND_HANDSHAKE(AND CLOSE) INVOKED OK"); + } + }, P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT); + + if(r) + { + ev.wait(); + } + + if(!hsh_result) + { + LOG_PRINT_CC_L0(context_, "COMMAND_HANDSHAKE Failed"); + m_net_server.get_config_object().close(context_.m_connection_id); + } + + return hsh_result; + } + //----------------------------------------------------------------------------------- + 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) + { + 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(), + [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); + return; + } + + if(!handle_remote_peerlist(rsp.local_peerlist, rsp.local_time, context)) + { + LOG_ERROR_CCONTEXT("COMMAND_TIMED_SYNC: failed to handle_remote_peerlist(...), closing connection."); + m_net_server.get_config_object().close(context.m_connection_id ); + } + if(!context.m_is_income) + m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_ip, context.m_remote_port); + m_payload_handler.process_payload_sync_data(rsp.payload_data, context, false); + }); + + if(!r) + { + LOG_PRINT_CC_L2(context_, "COMMAND_TIMED_SYNC Failed"); + return false; + } + return true; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + size_t node_server<t_payload_net_handler>::get_random_index_with_fixed_probability(size_t max_index) + { + //divide by zero workaround + if(!max_index) + return 0; + + size_t x = crypto::rand<size_t>()%(max_index+1); + size_t res = (x*x*x)/(max_index*max_index); //parabola \/ + LOG_PRINT_L3("Random connection index=" << res << "(x="<< x << ", max_index=" << max_index << ")"); + return res; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::is_peer_used(const peerlist_entry& peer) + { + + if(m_config.m_peer_id == peer.id) + return true;//dont make connections to ourself + + bool used = false; + m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + if(cntxt.peer_id == peer.id || (!cntxt.m_is_income && peer.adr.ip == cntxt.m_remote_ip && peer.adr.port == cntxt.m_remote_port)) + { + used = true; + return false;//stop enumerating + } + return true; + }); + + return used; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::is_addr_connected(const net_address& peer) + { + bool connected = false; + m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + if(!cntxt.m_is_income && peer.ip == cntxt.m_remote_ip && peer.port == cntxt.m_remote_port) + { + connected = true; + return false;//stop enumerating + } + return true; + }); + + return connected; + } + + //----------------------------------------------------------------------------------- + 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" ) << ")..."); + + 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), + 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) + /*<< ", 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) + /*<< ", try " << try_count*/); + return false; + } + if(just_take_peerlist) + { + m_net_server.get_config_object().close(con.m_connection_id); + LOG_PRINT_CC_GREEN(con, "CONNECTION HANDSHAKED OK AND CLOSED.", LOG_LEVEL_2); + return true; + } + + peerlist_entry pe_local = AUTO_VAL_INIT(pe_local); + pe_local.adr = na; + pe_local.id = pi; + time(&pe_local.last_seen); + m_peerlist.append_with_peer_white(pe_local); + //update last seen and push it to peerlist manager + + LOG_PRINT_CC_GREEN(con, "CONNECTION HANDSHAKED OK.", LOG_LEVEL_2); + return true; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::make_new_connection_from_peerlist(bool use_white_list) + { + size_t local_peers_count = use_white_list ? m_peerlist.get_white_peers_count():m_peerlist.get_gray_peers_count(); + if(!local_peers_count) + return false;//no peers + + size_t max_random_index = std::min<uint64_t>(local_peers_count -1, 20); + + std::set<size_t> tried_peers; + + size_t try_count = 0; + size_t rand_count = 0; + while(rand_count < (max_random_index+1)*3 && try_count < 10 && !m_net_server.is_stop_signal_sent()) + { + ++rand_count; + size_t random_index = get_random_index_with_fixed_probability(max_random_index); + CHECK_AND_ASSERT_MES(random_index < local_peers_count, false, "random_starter_index < peers_local.size() failed!!"); + + if(tried_peers.count(random_index)) + continue; + + tried_peers.insert(random_index); + peerlist_entry pe = AUTO_VAL_INIT(pe); + bool r = use_white_list ? m_peerlist.get_white_peer_by_index(pe, random_index):m_peerlist.get_gray_peer_by_index(pe, random_index); + CHECK_AND_ASSERT_MES(r, false, "Failed to get random peer from peerlist(white:" << use_white_list << ")"); + + ++try_count; + + 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")); + + if(!try_to_connect_and_handshake_with_new_peer(pe.adr, false, pe.last_seen, use_white_list)) + continue; + + return true; + } + return false; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::connections_maker() + { + if(!m_peerlist.get_white_peers_count() && m_seed_nodes.size()) + { + size_t try_count = 0; + size_t current_index = crypto::rand<size_t>()%m_seed_nodes.size(); + while(true) + { + if(m_net_server.is_stop_signal_sent()) + return false; + + if(try_to_connect_and_handshake_with_new_peer(m_seed_nodes[current_index], true)) + break; + if(++try_count > m_seed_nodes.size()) + { + LOG_PRINT_RED_L0("Failed to connect to any of seed peers, continuing without seeds"); + break; + } + if(++current_index > m_seed_nodes.size()) + current_index = 0; + } + } + + 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); + } + + size_t expected_white_connections = (m_config.m_net_config.connections_count*P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT)/100; + + size_t conn_count = get_outgoing_connections_count(); + if(conn_count < m_config.m_net_config.connections_count) + { + if(conn_count < expected_white_connections) + { + //start from white list + if(!make_expected_connections_count(true, expected_white_connections)) + return false; + //and then do grey list + if(!make_expected_connections_count(false, m_config.m_net_config.connections_count)) + return false; + }else + { + //start from grey list + if(!make_expected_connections_count(false, m_config.m_net_config.connections_count)) + return false; + //and then do white list + if(!make_expected_connections_count(true, m_config.m_net_config.connections_count)) + return false; + } + } + + return true; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::make_expected_connections_count(bool white_list, size_t expected_connections) + { + size_t conn_count = get_outgoing_connections_count(); + //add new connections from white peers + while(conn_count < expected_connections) + { + if(m_net_server.is_stop_signal_sent()) + return false; + + if(!make_new_connection_from_peerlist(white_list)) + break; + conn_count = get_outgoing_connections_count(); + } + return true; + } + + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + size_t node_server<t_payload_net_handler>::get_outgoing_connections_count() + { + size_t count = 0; + m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + if(!cntxt.m_is_income) + ++count; + return true; + }); + + return count; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::idle_worker() + { + m_peer_handshake_idle_maker_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::peer_sync_idle_maker, this)); + m_connections_maker_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::connections_maker, this)); + m_peerlist_store_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::store_config, this)); + return true; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + 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; + local_connects_type cncts; + m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + if(cntxt.peer_id) + cncts.push_back(local_connects_type::value_type(cntxt, cntxt.peer_id));//do idle sync only with handshaked connections + return true; + }); + + std::for_each(cncts.begin(), cncts.end(), [&](const typename local_connects_type::value_type& vl){do_peer_timed_sync(vl.first, vl.second);}); + + LOG_PRINT_L2("FINISHED PEERLIST IDLE HANDSHAKE"); + return true; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::fix_time_delta(std::list<peerlist_entry>& local_peerlist, time_t local_time, int64_t& delta) + { + //fix time delta + time_t now = 0; + time(&now); + delta = now - local_time; + + BOOST_FOREACH(peerlist_entry& be, local_peerlist) + { + 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); + return false; + } + be.last_seen += delta; + } + return true; + } + //----------------------------------------------------------------------------------- + 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) + { + int64_t delta = 0; + std::list<peerlist_entry> peerlist_ = peerlist; + if(!fix_time_delta(peerlist_, local_time, delta)) + return false; + LOG_PRINT_CCONTEXT_L2("REMOTE PEERLIST: TIME_DELTA: " << delta << ", remote peerlist size=" << peerlist_.size()); + LOG_PRINT_CCONTEXT_L3("REMOTE PEERLIST: " << print_peerlist_to_string(peerlist_)); + return m_peerlist.merge_peerlist(peerlist_); + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::get_local_node_data(basic_node_data& node_data) + { + time(&node_data.local_time); + node_data.peer_id = m_config.m_peer_id; + if(!m_hide_my_port) + node_data.my_port = m_external_port ? m_external_port : m_listenning_port; + else + node_data.my_port = 0; + node_data.network_id = BYTECOIN_NETWORK; + return true; + } + //----------------------------------------------------------------------------------- +#ifdef ALLOW_DEBUG_COMMANDS + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::check_trust(const proof_of_trust& tr) + { + uint64_t local_time = time(NULL); + uint64_t time_delata = local_time > tr.time ? local_time - tr.time: tr.time - local_time; + if(time_delata > 24*60*60 ) + { + LOG_ERROR("check_trust failed to check time conditions, local_time=" << local_time << ", proof_time=" << tr.time); + return false; + } + if(m_last_stat_request_time >= tr.time ) + { + LOG_ERROR("check_trust failed to check time conditions, last_stat_request_time=" << m_last_stat_request_time << ", proof_time=" << tr.time); + return false; + } + if(m_config.m_peer_id != tr.peer_id) + { + LOG_ERROR("check_trust failed: peer_id mismatch (passed " << tr.peer_id << ", expected " << m_config.m_peer_id<< ")"); + return false; + } + crypto::public_key pk = AUTO_VAL_INIT(pk); + 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)) + { + LOG_ERROR("check_trust failed: sign check failed"); + return false; + } + //update last request time + m_last_stat_request_time = tr.time; + return true; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + int node_server<t_payload_net_handler>::handle_get_stat_info(int command, typename COMMAND_REQUEST_STAT_INFO::request& arg, typename COMMAND_REQUEST_STAT_INFO::response& rsp, p2p_connection_context& context) + { + if(!check_trust(arg.tr)) + { + drop_connection(context); + return 1; + } + rsp.connections_count = m_net_server.get_config_object().get_connections_count(); + rsp.incoming_connections_count = rsp.connections_count - get_outgoing_connections_count(); + rsp.version = PROJECT_VERSION_LONG; + rsp.os_version = tools::get_os_version_string(); + m_payload_handler.get_stat_info(rsp.payload_info); + return 1; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + int node_server<t_payload_net_handler>::handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, p2p_connection_context& context) + { + if(!check_trust(arg.tr)) + { + drop_connection(context); + return 1; + } + m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + connection_entry ce; + ce.adr.ip = cntxt.m_remote_ip; + ce.adr.port = cntxt.m_remote_port; + ce.id = cntxt.peer_id; + ce.is_income = cntxt.m_is_income; + rsp.connections_list.push_back(ce); + return true; + }); + + m_peerlist.get_peerlist_full(rsp.local_peerlist_gray, rsp.local_peerlist_white); + rsp.my_id = m_config.m_peer_id; + rsp.local_time = time(NULL); + return 1; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + int node_server<t_payload_net_handler>::handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, p2p_connection_context& context) + { + rsp.my_id = m_config.m_peer_id; + return 1; + } +#endif + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + void node_server<t_payload_net_handler>::request_callback(const epee::net_utils::connection_context_base& context) + { + m_net_server.get_config_object().request_callback(context.m_connection_id); + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context) + { + std::list<boost::uuids::uuid> connections; + m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + if(cntxt.peer_id && context.m_connection_id != cntxt.m_connection_id) + connections.push_back(cntxt.m_connection_id); + return true; + }); + + BOOST_FOREACH(const auto& c_id, connections) + { + m_net_server.get_config_object().notify(command, data_buff, c_id); + } + return true; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + void node_server<t_payload_net_handler>::callback(p2p_connection_context& context) + { + m_payload_handler.on_callback(context); + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::invoke_notify_to_peer(int command, const std::string& req_buff, const epee::net_utils::connection_context_base& context) + { + int res = m_net_server.get_config_object().notify(command, req_buff, context.m_connection_id); + return res > 0; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::invoke_command_to_peer(int command, const std::string& req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context) + { + int res = m_net_server.get_config_object().invoke(command, req_buff, resp_buff, context.m_connection_id); + return res > 0; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::drop_connection(const epee::net_utils::connection_context_base& context) + { + m_net_server.get_config_object().close(context.m_connection_id); + return true; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> template<class t_callback> + bool node_server<t_payload_net_handler>::try_ping(basic_node_data& node_data, p2p_connection_context& context, t_callback cb) + { + if(!node_data.my_port) + return false; + + 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); + 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, + const boost::system::error_code& ec)->bool + { + if(ec) + { + LOG_PRINT_CC_L2(ping_context, "back ping connect failed to " << ip << ":" << port); + return false; + } + COMMAND_PING::request req; + COMMAND_PING::response rsp; + //vc2010 workaround + /*std::string ip_ = ip; + 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(), + [=](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) << ")"); + return; + } + + if(rsp.status != PING_OK_RESPONSE_STATUS_TEXT || pr != rsp.peer_id) + { + LOG_PRINT_CC_L2(ping_context, "back ping invoke wrong response \"" << rsp.status << "\" from" << ip << ":" << port << ", hsh_peer_id=" << pr << ", rsp.peer_id=" << rsp.peer_id); + return; + } + m_net_server.get_config_object().close(ping_context.m_connection_id); + cb(); + }); + + if(!inv_call_res) + { + LOG_PRINT_CC_L2(ping_context, "back ping invoke failed to " << ip << ":" << port); + m_net_server.get_config_object().close(ping_context.m_connection_id); + return false; + } + return true; + }); + if(!r) + { + LOG_ERROR("Failed to call connect_async, network error."); + } + return r; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + int node_server<t_payload_net_handler>::handle_timed_sync(int command, typename COMMAND_TIMED_SYNC::request& arg, typename COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context) + { + if(!m_payload_handler.process_payload_sync_data(arg.payload_data, context, false)) + { + LOG_ERROR_CCONTEXT("Failed to process_payload_sync_data(), dropping connection"); + drop_connection(context); + return 1; + } + + //fill response + rsp.local_time = time(NULL); + m_peerlist.get_peerlist_head(rsp.local_peerlist); + m_payload_handler.get_payload_sync_data(rsp.payload_data); + LOG_PRINT_CCONTEXT_L2("COMMAND_TIMED_SYNC"); + return 1; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + int node_server<t_payload_net_handler>::handle_handshake(int command, typename COMMAND_HANDSHAKE::request& arg, typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context) + { + 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)); + drop_connection(context); + return 1; + } + + if(!context.m_is_income) + { + LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE came not from incoming connection"); + drop_connection(context); + return 1; + } + + if(context.peer_id) + { + LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE came, but seems that connection already have associated peer_id (double COMMAND_HANDSHAKE?)"); + drop_connection(context); + return 1; + } + + if(!m_payload_handler.process_payload_sync_data(arg.payload_data, context, true)) + { + LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE came, but process_payload_sync_data returned false, dropping connection."); + drop_connection(context); + return 1; + } + //associate peer_id with this connection + context.peer_id = arg.node_data.peer_id; + + if(arg.node_data.peer_id != m_config.m_peer_id && arg.node_data.my_port) + { + peerid_type peer_id_l = arg.node_data.peer_id; + boost::uint32_t port_l = arg.node_data.my_port; + //try ping to be sure that we can add this peer to peer_list + try_ping(arg.node_data, context, [peer_id_l, port_l, context, this]() + { + //called only(!) if success pinged, update local peerlist + peerlist_entry pe; + pe.adr.ip = context.m_remote_ip; + pe.adr.port = port_l; + 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); + }); + } + + //fill response + m_peerlist.get_peerlist_head(rsp.local_peerlist); + get_local_node_data(rsp.node_data); + m_payload_handler.get_payload_sync_data(rsp.payload_data); + LOG_PRINT_CCONTEXT_GREEN("COMMAND_HANDSHAKE", LOG_LEVEL_1); + return 1; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + int node_server<t_payload_net_handler>::handle_ping(int command, COMMAND_PING::request& arg, COMMAND_PING::response& rsp, p2p_connection_context& context) + { + LOG_PRINT_CCONTEXT_L2("COMMAND_PING"); + rsp.status = PING_OK_RESPONSE_STATUS_TEXT; + rsp.peer_id = m_config.m_peer_id; + return 1; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::log_peerlist() + { + std::list<peerlist_entry> pl_wite; + std::list<peerlist_entry> pl_gray; + m_peerlist.get_peerlist_full(pl_gray, pl_wite); + LOG_PRINT_L0(ENDL << "Peerlist white:" << ENDL << print_peerlist_to_string(pl_wite) << ENDL << "Peerlist gray:" << ENDL << print_peerlist_to_string(pl_gray) ); + return true; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::log_connections() + { + LOG_PRINT_L0("Connections: \r\n" << print_connections_container() ); + return true; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + std::string node_server<t_payload_net_handler>::print_connections_container() + { + + 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 + << " \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") + << std::endl; + return true; + }); + std::string s = ss.str(); + return s; + } + //----------------------------------------------------------------------------------- + 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"); + } + //----------------------------------------------------------------------------------- + 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"); + } + //----------------------------------------------------------------------------------- +} diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h new file mode 100644 index 000000000..db8b63095 --- /dev/null +++ b/src/p2p/net_node_common.h @@ -0,0 +1,62 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <boost/uuid/uuid.hpp> +#include "net/net_utils_base.h" + + +namespace nodetool +{ + + typedef boost::uuids::uuid uuid; + typedef boost::uuids::uuid net_connection_id; + + template<class t_connection_context> + struct i_p2p_endpoint + { + virtual bool relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context)=0; + virtual bool invoke_command_to_peer(int command, const std::string& req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context)=0; + virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const epee::net_utils::connection_context_base& context)=0; + virtual bool drop_connection(const epee::net_utils::connection_context_base& context)=0; + virtual void request_callback(const epee::net_utils::connection_context_base& context)=0; + virtual uint64_t get_connections_count()=0; + virtual void for_each_connection(std::function<bool(t_connection_context&)> f)=0; + }; + + template<class t_connection_context> + struct p2p_endpoint_stub: public i_p2p_endpoint<t_connection_context> + { + virtual bool relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context) + { + return false; + } + virtual bool invoke_command_to_peer(int command, const std::string& req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context) + { + return false; + } + virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const epee::net_utils::connection_context_base& context) + { + return true; + } + virtual bool drop_connection(const epee::net_utils::connection_context_base& context) + { + return false; + } + virtual void request_callback(const epee::net_utils::connection_context_base& context) + { + + } + virtual void for_each_connection(std::function<bool(t_connection_context&)> f) + { + + } + + virtual uint64_t get_connections_count() + { + return false; + } + }; +} diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h new file mode 100644 index 000000000..65dfd011a --- /dev/null +++ b/src/p2p/net_peerlist.h @@ -0,0 +1,375 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <list> +#include <set> +#include <map> +#include <boost/foreach.hpp> +//#include <boost/bimap.hpp> +//#include <boost/bimap/multiset_of.hpp> +#include <boost/archive/binary_oarchive.hpp> +#include <boost/archive/binary_iarchive.hpp> +#include <boost/serialization/version.hpp> + +#include <boost/multi_index_container.hpp> +#include <boost/multi_index/ordered_index.hpp> +#include <boost/multi_index/identity.hpp> +#include <boost/multi_index/member.hpp> + + +#include "syncobj.h" +#include "net/local_ip.h" +#include "p2p_protocol_defs.h" +#include "cryptonote_config.h" +#include "net_peerlist_boost_serialization.h" + + + +namespace nodetool +{ + + + /************************************************************************/ + /* */ + /************************************************************************/ + class peerlist_manager + { + public: + bool init(bool allow_local_ip); + bool deinit(); + size_t get_white_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_white.size();} + size_t get_gray_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_gray.size();} + bool merge_peerlist(const std::list<peerlist_entry>& outer_bs); + bool get_peerlist_head(std::list<peerlist_entry>& bs_head, uint32_t depth = P2P_DEFAULT_PEERS_IN_HANDSHAKE); + bool get_peerlist_full(std::list<peerlist_entry>& pl_gray, std::list<peerlist_entry>& pl_white); + bool get_white_peer_by_index(peerlist_entry& p, size_t i); + bool get_gray_peer_by_index(peerlist_entry& p, size_t i); + bool append_with_peer_white(const peerlist_entry& pr); + bool append_with_peer_gray(const peerlist_entry& pr); + bool set_peer_just_seen(peerid_type peer, boost::uint32_t ip, boost::uint32_t port); + bool set_peer_just_seen(peerid_type peer, const net_address& addr); + bool set_peer_unreachable(const peerlist_entry& pr); + bool is_ip_allowed(uint32_t ip); + void trim_white_peerlist(); + void trim_gray_peerlist(); + + + private: + struct by_time{}; + struct by_id{}; + struct by_addr{}; + + struct modify_all_but_id + { + modify_all_but_id(const peerlist_entry& ple):m_ple(ple){} + void operator()(peerlist_entry& e) + { + e.id = m_ple.id; + } + private: + const peerlist_entry& m_ple; + }; + + struct modify_all + { + modify_all(const peerlist_entry& ple):m_ple(ple){} + void operator()(peerlist_entry& e) + { + e = m_ple; + } + private: + const peerlist_entry& m_ple; + }; + + struct modify_last_seen + { + modify_last_seen(time_t last_seen):m_last_seen(last_seen){} + void operator()(peerlist_entry& e) + { + e.last_seen = m_last_seen; + } + private: + time_t m_last_seen; + }; + + + typedef boost::multi_index_container< + peerlist_entry, + boost::multi_index::indexed_by< + // access by peerlist_entry::net_adress + boost::multi_index::ordered_unique<boost::multi_index::tag<by_addr>, boost::multi_index::member<peerlist_entry,net_address,&peerlist_entry::adr> >, + // sort by peerlist_entry::last_seen< + boost::multi_index::ordered_non_unique<boost::multi_index::tag<by_time>, boost::multi_index::member<peerlist_entry,time_t,&peerlist_entry::last_seen> > + > + > peers_indexed; + + typedef boost::multi_index_container< + peerlist_entry, + boost::multi_index::indexed_by< + // access by peerlist_entry::id< + boost::multi_index::ordered_unique<boost::multi_index::tag<by_id>, boost::multi_index::member<peerlist_entry,uint64_t,&peerlist_entry::id> >, + // access by peerlist_entry::net_adress + boost::multi_index::ordered_unique<boost::multi_index::tag<by_addr>, boost::multi_index::member<peerlist_entry,net_address,&peerlist_entry::adr> >, + // sort by peerlist_entry::last_seen< + boost::multi_index::ordered_non_unique<boost::multi_index::tag<by_time>, boost::multi_index::member<peerlist_entry,time_t,&peerlist_entry::last_seen> > + > + > peers_indexed_old; + public: + + template <class Archive, class t_version_type> + void serialize(Archive &a, const t_version_type ver) + { + if(ver < 3) + return; + CRITICAL_REGION_LOCAL(m_peerlist_lock); + if(ver < 4) + { + //loading data from old storage + peers_indexed_old pio; + a & pio; + peers_indexed_from_old(pio, m_peers_white); + return; + } + a & m_peers_white; + a & m_peers_gray; + } + + private: + bool peers_indexed_from_old(const peers_indexed_old& pio, peers_indexed& pi); + + friend class boost::serialization::access; + epee::critical_section m_peerlist_lock; + std::string m_config_folder; + bool m_allow_local_ip; + + + peers_indexed m_peers_gray; + peers_indexed m_peers_white; + }; + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::init(bool allow_local_ip) + { + m_allow_local_ip = allow_local_ip; + return true; + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::deinit() + { + return true; + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::peers_indexed_from_old(const peers_indexed_old& pio, peers_indexed& pi) + { + for(auto x: pio) + { + auto by_addr_it = pi.get<by_addr>().find(x.adr); + if(by_addr_it == pi.get<by_addr>().end()) + { + pi.insert(x); + } + } + + return true; + } + //-------------------------------------------------------------------------------------------------- + inline void peerlist_manager::trim_white_peerlist() + { + while(m_peers_gray.size() > P2P_LOCAL_GRAY_PEERLIST_LIMIT) + { + peers_indexed::index<by_time>::type& sorted_index=m_peers_gray.get<by_time>(); + sorted_index.erase(sorted_index.begin()); + } + } + //-------------------------------------------------------------------------------------------------- + inline void peerlist_manager::trim_gray_peerlist() + { + while(m_peers_white.size() > P2P_LOCAL_WHITE_PEERLIST_LIMIT) + { + peers_indexed::index<by_time>::type& sorted_index=m_peers_white.get<by_time>(); + sorted_index.erase(sorted_index.begin()); + } + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::merge_peerlist(const std::list<peerlist_entry>& outer_bs) + { + CRITICAL_REGION_LOCAL(m_peerlist_lock); + BOOST_FOREACH(const peerlist_entry& be, outer_bs) + { + append_with_peer_gray(be); + } + // delete extra elements + trim_gray_peerlist(); + return true; + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::get_white_peer_by_index(peerlist_entry& p, size_t i) + { + CRITICAL_REGION_LOCAL(m_peerlist_lock); + if(i >= m_peers_white.size()) + return false; + + peers_indexed::index<by_time>::type& by_time_index = m_peers_white.get<by_time>(); + p = *epee::misc_utils::move_it_backward(--by_time_index.end(), i); + return true; + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::get_gray_peer_by_index(peerlist_entry& p, size_t i) + { + CRITICAL_REGION_LOCAL(m_peerlist_lock); + if(i >= m_peers_gray.size()) + return false; + + peers_indexed::index<by_time>::type& by_time_index = m_peers_gray.get<by_time>(); + p = *epee::misc_utils::move_it_backward(--by_time_index.end(), i); + return true; + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::is_ip_allowed(uint32_t ip) + { + //never allow loopback ip + if(epee::net_utils::is_ip_loopback(ip)) + return false; + + if(!m_allow_local_ip && epee::net_utils::is_ip_local(ip)) + return false; + + return true; + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::get_peerlist_head(std::list<peerlist_entry>& bs_head, uint32_t depth) + { + + CRITICAL_REGION_LOCAL(m_peerlist_lock); + peers_indexed::index<by_time>::type& by_time_index=m_peers_white.get<by_time>(); + uint32_t cnt = 0; + BOOST_REVERSE_FOREACH(const peers_indexed::value_type& vl, by_time_index) + { + if(!vl.last_seen) + continue; + bs_head.push_back(vl); + if(cnt++ > depth) + break; + } + return true; + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::get_peerlist_full(std::list<peerlist_entry>& pl_gray, std::list<peerlist_entry>& pl_white) + { + CRITICAL_REGION_LOCAL(m_peerlist_lock); + peers_indexed::index<by_time>::type& by_time_index_gr=m_peers_gray.get<by_time>(); + BOOST_REVERSE_FOREACH(const peers_indexed::value_type& vl, by_time_index_gr) + { + pl_gray.push_back(vl); + } + + peers_indexed::index<by_time>::type& by_time_index_wt=m_peers_white.get<by_time>(); + BOOST_REVERSE_FOREACH(const peers_indexed::value_type& vl, by_time_index_wt) + { + pl_white.push_back(vl); + } + + return true; + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::set_peer_just_seen(peerid_type peer, boost::uint32_t ip, boost::uint32_t port) + { + net_address addr; + addr.ip = ip; + addr.port = port; + return set_peer_just_seen(peer, addr); + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::set_peer_just_seen(peerid_type peer, const net_address& addr) + { + TRY_ENTRY(); + CRITICAL_REGION_LOCAL(m_peerlist_lock); + //find in white list + peerlist_entry ple; + ple.adr = addr; + ple.id = peer; + ple.last_seen = time(NULL); + return append_with_peer_white(ple); + CATCH_ENTRY_L0("peerlist_manager::set_peer_just_seen()", false); + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::append_with_peer_white(const peerlist_entry& ple) + { + TRY_ENTRY(); + if(!is_ip_allowed(ple.adr.ip)) + return true; + + CRITICAL_REGION_LOCAL(m_peerlist_lock); + //find in white list + auto by_addr_it_wt = m_peers_white.get<by_addr>().find(ple.adr); + if(by_addr_it_wt == m_peers_white.get<by_addr>().end()) + { + //put new record into white list + m_peers_white.insert(ple); + trim_white_peerlist(); + }else + { + //update record in white list + m_peers_white.replace(by_addr_it_wt, ple); + } + //remove from gray list, if need + auto by_addr_it_gr = m_peers_gray.get<by_addr>().find(ple.adr); + if(by_addr_it_gr != m_peers_gray.get<by_addr>().end()) + { + m_peers_gray.erase(by_addr_it_gr); + } + return true; + CATCH_ENTRY_L0("peerlist_manager::append_with_peer_white()", false); + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::append_with_peer_gray(const peerlist_entry& ple) + { + TRY_ENTRY(); + if(!is_ip_allowed(ple.adr.ip)) + return true; + + if(ple.adr.port != 8080) + assert(false); + + + CRITICAL_REGION_LOCAL(m_peerlist_lock); + //find in white list + auto by_addr_it_wt = m_peers_white.get<by_addr>().find(ple.adr); + if(by_addr_it_wt != m_peers_white.get<by_addr>().end()) + return true; + + //update gray list + auto by_addr_it_gr = m_peers_gray.get<by_addr>().find(ple.adr); + if(by_addr_it_gr == m_peers_gray.get<by_addr>().end()) + { + //put new record into white list + m_peers_gray.insert(ple); + trim_gray_peerlist(); + }else + { + //update record in white list + m_peers_gray.replace(by_addr_it_gr, ple); + } + return true; + CATCH_ENTRY_L0("peerlist_manager::append_with_peer_gray()", false); + return true; + } + //-------------------------------------------------------------------------------------------------- +} + +BOOST_CLASS_VERSION(nodetool::peerlist_manager, 4) diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h new file mode 100644 index 000000000..23a253f25 --- /dev/null +++ b/src/p2p/net_peerlist_boost_serialization.h @@ -0,0 +1,28 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +namespace boost +{ + namespace serialization + { + //BOOST_CLASS_VERSION(odetool::net_adress, 1) + template <class Archive, class ver_type> + inline void serialize(Archive &a, nodetool::net_address& na, const ver_type ver) + { + a & na.ip; + a & na.port; + } + + + template <class Archive, class ver_type> + inline void serialize(Archive &a, nodetool::peerlist_entry& pl, const ver_type ver) + { + a & pl.adr; + a & pl.id; + a & pl.last_seen; + } + } +} diff --git a/src/p2p/p2p_networks.h b/src/p2p/p2p_networks.h new file mode 100644 index 000000000..3fa409006 --- /dev/null +++ b/src/p2p/p2p_networks.h @@ -0,0 +1,10 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +namespace nodetool +{ + const static boost::uuids::uuid BYTECOIN_NETWORK = { { 0x11 ,0x10, 0x01, 0x11 , 0x11, 0x00 , 0x01, 0x01, 0x10, 0x11, 0x00, 0x12, 0x10, 0x11, 0x01, 0x10} }; //Bender's nightmare +} diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h new file mode 100644 index 000000000..7ae6d08f3 --- /dev/null +++ b/src/p2p/p2p_protocol_defs.h @@ -0,0 +1,315 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <boost/uuid/uuid.hpp> +#include "serialization/keyvalue_serialization.h" +#include "misc_language.h" +#include "cryptonote_config.h" +#include "crypto/crypto.h" + +namespace nodetool +{ + typedef boost::uuids::uuid uuid; + typedef uint64_t peerid_type; + +#pragma pack (push, 1) + + struct net_address + { + boost::uint32_t ip; + boost::uint32_t port; + }; + + struct peerlist_entry + { + net_address adr; + peerid_type id; + time_t last_seen; + }; + + struct connection_entry + { + net_address adr; + peerid_type id; + bool is_income; + }; + +#pragma pack(pop) + + inline + bool operator < (const net_address& a, const net_address& b) + { + return epee::misc_utils::is_less_as_pod(a, b); + } + + inline + bool operator == (const net_address& a, const net_address& b) + { + return memcmp(&a, &b, sizeof(a)) == 0; + } + inline + std::string print_peerlist_to_string(const std::list<peerlist_entry>& pl) + { + time_t now_time = 0; + time(&now_time); + std::stringstream ss; + ss << std::setfill ('0') << std::setw (8) << std::hex << std::noshowbase; + BOOST_FOREACH(const peerlist_entry& pe, pl) + { + ss << pe.id << "\t" << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << boost::lexical_cast<std::string>(pe.adr.port) << " \tlast_seen: " << epee::misc_utils::get_time_interval_string(now_time - pe.last_seen) << std::endl; + } + return ss.str(); + } + + + struct network_config + { + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(connections_count) + KV_SERIALIZE(handshake_interval) + KV_SERIALIZE(packet_max_size) + KV_SERIALIZE(config_id) + END_KV_SERIALIZE_MAP() + + boost::uint32_t connections_count; + boost::uint32_t connection_timeout; + boost::uint32_t ping_connection_timeout; + boost::uint32_t handshake_interval; + boost::uint32_t packet_max_size; + boost::uint32_t config_id; + boost::uint32_t send_peerlist_sz; + }; + + struct basic_node_data + { + uuid network_id; + time_t local_time; + uint32_t my_port; + peerid_type peer_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_VAL_POD_AS_BLOB(network_id) + KV_SERIALIZE(peer_id) + KV_SERIALIZE(local_time) + KV_SERIALIZE(my_port) + END_KV_SERIALIZE_MAP() + }; + + +#define P2P_COMMANDS_POOL_BASE 1000 + + /************************************************************************/ + /* */ + /************************************************************************/ + template<class t_playload_type> + struct COMMAND_HANDSHAKE_T + { + const static int ID = P2P_COMMANDS_POOL_BASE + 1; + + struct request + { + basic_node_data node_data; + t_playload_type payload_data; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(node_data) + KV_SERIALIZE(payload_data) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + basic_node_data node_data; + t_playload_type payload_data; + std::list<peerlist_entry> local_peerlist; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(node_data) + KV_SERIALIZE(payload_data) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist) + END_KV_SERIALIZE_MAP() + }; + }; + + + /************************************************************************/ + /* */ + /************************************************************************/ + template<class t_playload_type> + struct COMMAND_TIMED_SYNC_T + { + const static int ID = P2P_COMMANDS_POOL_BASE + 2; + + struct request + { + t_playload_type payload_data; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(payload_data) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + time_t local_time; + t_playload_type payload_data; + std::list<peerlist_entry> local_peerlist; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(local_time) + KV_SERIALIZE(payload_data) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist) + END_KV_SERIALIZE_MAP() + }; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + + struct COMMAND_PING + { + /* + Used to make "callback" connection, to be sure that opponent node + have accessible connection point. Only other nodes can add peer to peerlist, + and ONLY in case when peer has accepted connection and answered to ping. + */ + const static int ID = P2P_COMMANDS_POOL_BASE + 3; + +#define PING_OK_RESPONSE_STATUS_TEXT "OK" + + struct request + { + /*actually we don't need to send any real data*/ + + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + peerid_type peer_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(peer_id) + END_KV_SERIALIZE_MAP() + }; + }; + + +#ifdef ALLOW_DEBUG_COMMANDS + //These commands are considered as insecure, and made in debug purposes for a limited lifetime. + //Anyone who feel unsafe with this commands can disable the ALLOW_GET_STAT_COMMAND macro. + + struct proof_of_trust + { + peerid_type peer_id; + uint64_t time; + crypto::signature sign; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(peer_id) + KV_SERIALIZE(time) + KV_SERIALIZE_VAL_POD_AS_BLOB(sign) + END_KV_SERIALIZE_MAP() + }; + + + template<class payload_stat_info> + struct COMMAND_REQUEST_STAT_INFO_T + { + const static int ID = P2P_COMMANDS_POOL_BASE + 4; + + struct request + { + proof_of_trust tr; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tr) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string version; + std::string os_version; + uint64_t connections_count; + uint64_t incoming_connections_count; + payload_stat_info payload_info; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(version) + KV_SERIALIZE(os_version) + KV_SERIALIZE(connections_count) + KV_SERIALIZE(incoming_connections_count) + KV_SERIALIZE(payload_info) + END_KV_SERIALIZE_MAP() + }; + }; + + + /************************************************************************/ + /* */ + /************************************************************************/ + struct COMMAND_REQUEST_NETWORK_STATE + { + const static int ID = P2P_COMMANDS_POOL_BASE + 5; + + struct request + { + proof_of_trust tr; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tr) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::list<peerlist_entry> local_peerlist_white; + std::list<peerlist_entry> local_peerlist_gray; + std::list<connection_entry> connections_list; + peerid_type my_id; + uint64_t local_time; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist_white) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist_gray) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(connections_list) + KV_SERIALIZE(my_id) + KV_SERIALIZE(local_time) + END_KV_SERIALIZE_MAP() + }; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + struct COMMAND_REQUEST_PEER_ID + { + const static int ID = P2P_COMMANDS_POOL_BASE + 6; + + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + peerid_type my_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(my_id) + END_KV_SERIALIZE_MAP() + }; + }; + +#endif + + +} + + + diff --git a/src/p2p/stdafx.h b/src/p2p/stdafx.h new file mode 100644 index 000000000..1cc72f811 --- /dev/null +++ b/src/p2p/stdafx.h @@ -0,0 +1,28 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include "targetver.h" + + +#if !defined(__GNUC__) +#define _CRTDBG_MAP_ALLOC +#include <stdlib.h> +#include <crtdbg.h> +#endif + + + +#include <stdio.h> + + +#define BOOST_FILESYSTEM_VERSION 3 +#define ENABLE_RELEASE_LOGGING +#include "log_opt_defs.h" +#include "misc_log_ex.h" + + + diff --git a/src/platform/mingw/alloca.h b/src/platform/mingw/alloca.h new file mode 100644 index 000000000..9e18ae9e2 --- /dev/null +++ b/src/platform/mingw/alloca.h @@ -0,0 +1,7 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <malloc.h> diff --git a/src/platform/msc/alloca.h b/src/platform/msc/alloca.h new file mode 100644 index 000000000..4e8c89ea3 --- /dev/null +++ b/src/platform/msc/alloca.h @@ -0,0 +1,7 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#define alloca(size) _alloca(size) diff --git a/src/platform/msc/inline_c.h b/src/platform/msc/inline_c.h new file mode 100644 index 000000000..c2ba06b61 --- /dev/null +++ b/src/platform/msc/inline_c.h @@ -0,0 +1,9 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#ifndef __cplusplus +#define inline __inline +#endif diff --git a/src/platform/msc/stdbool.h b/src/platform/msc/stdbool.h new file mode 100644 index 000000000..2e1a06732 --- /dev/null +++ b/src/platform/msc/stdbool.h @@ -0,0 +1,13 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#if !defined(__cplusplus) + +typedef int bool; +#define true 1 +#define false 0 + +#endif diff --git a/src/platform/msc/sys/param.h b/src/platform/msc/sys/param.h new file mode 100644 index 000000000..b44de84b4 --- /dev/null +++ b/src/platform/msc/sys/param.h @@ -0,0 +1,10 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#define LITTLE_ENDIAN 1234 +#define BIG_ENDIAN 4321 +#define PDP_ENDIAN 3412 +#define BYTE_ORDER LITTLE_ENDIAN diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp new file mode 100644 index 000000000..984d9d8cd --- /dev/null +++ b/src/rpc/core_rpc_server.cpp @@ -0,0 +1,391 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include <boost/foreach.hpp> +#include "include_base_utils.h" +using namespace epee; + +#include "core_rpc_server.h" +#include "common/command_line.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/account.h" +#include "misc_language.h" +#include "crypto/hash.h" +#include "core_rpc_server_error_codes.h" + +namespace cryptonote +{ + namespace + { + const command_line::arg_descriptor<std::string> arg_rpc_bind_ip = {"rpc-bind-ip", "", "127.0.0.1"}; + const command_line::arg_descriptor<std::string> arg_rpc_bind_port = {"rpc-bind-port", "", std::to_string(RPC_DEFAULT_PORT)}; + } + + //----------------------------------------------------------------------------------- + void core_rpc_server::init_options(boost::program_options::options_description& desc) + { + command_line::add_arg(desc, arg_rpc_bind_ip); + command_line::add_arg(desc, arg_rpc_bind_port); + } + //------------------------------------------------------------------------------------------------------------------------------ + core_rpc_server::core_rpc_server(core& cr, nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >& p2p):m_core(cr), m_p2p(p2p) + {} + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::handle_command_line(const boost::program_options::variables_map& vm) + { + m_bind_ip = command_line::get_arg(vm, arg_rpc_bind_ip); + m_port = command_line::get_arg(vm, arg_rpc_bind_port); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::init(const boost::program_options::variables_map& vm) + { + m_net_server.set_threads_prefix("RPC"); + bool r = handle_command_line(vm); + CHECK_AND_ASSERT_MES(r, false, "Failed to process command line in core_rpc_server"); + return epee::http_server_impl_base<core_rpc_server>::init(m_port, m_bind_ip); + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res) + { + res.height = m_core.get_current_blockchain_height(); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res) + { + res.height = m_core.get_current_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(); + res.alt_blocks_count = m_core.get_blockchain_storage().get_alternative_blocks_count(); + uint64_t total_conn = m_p2p.get_connections_count(); + res.outgoing_connections_count = m_p2p.get_outgoing_connections_count(); + res.incoming_connections_count = total_conn - res.outgoing_connections_count; + res.white_peerlist_size = m_p2p.get_peerlist_manager().get_white_peers_count(); + res.grey_peerlist_size = m_p2p.get_peerlist_manager().get_gray_peers_count(); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_known_block_ids(const COMMAND_RPC_GET_KNOWN_BLOCK_IDS::request& req, COMMAND_RPC_GET_KNOWN_BLOCK_IDS::response& res) + { + std::list<crypto::hash> main, alt, invalid; + m_core.get_all_known_block_ids(main, alt, invalid); + BOOST_FOREACH(crypto::hash &h, main) + res.main.push_back(string_tools::pod_to_hex(h)); + + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res) + { + 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)) + { + res.status = "Failed"; + return false; + } + + BOOST_FOREACH(auto& b, bs) + { + res.blocks.resize(res.blocks.size()+1); + res.blocks.back().block = block_to_blob(b.first); + BOOST_FOREACH(auto& t, b.second) + { + res.blocks.back().txs.push_back(tx_to_blob(t)); + } + } + + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + 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) + { + res.status = "Failed"; + if(!m_core.get_random_outs_for_amounts(req, res)) + { + return true; + } + + res.status = CORE_RPC_STATUS_OK; + std::stringstream ss; + typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount outs_for_amount; + typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry; + std::for_each(res.outs.begin(), res.outs.end(), [&](outs_for_amount& ofa) + { + ss << "[" << ofa.amount << "]:"; + CHECK_AND_ASSERT_MES(ofa.outs.size(), ;, "internal error: ofa.outs.size() is empty for amount " << ofa.amount); + std::for_each(ofa.outs.begin(), ofa.outs.end(), [&](out_entry& oe) + { + ss << oe.global_amount_index << " "; + }); + ss << ENDL; + }); + std::string s = ss.str(); + LOG_PRINT_L2("COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: " << ENDL << s); + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + 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) + { + bool r = m_core.get_tx_outputs_gindexs(req.txid, res.o_indexes); + if(!r) + { + res.status = "Failed"; + return true; + } + res.status = CORE_RPC_STATUS_OK; + LOG_PRINT_L2("COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES: [" << res.o_indexes.size() << "]"); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res) + { + std::vector<crypto::hash> vh; + BOOST_FOREACH(const auto& tx_hex_str, req.txs_hashes) + { + blobdata b; + if(!string_tools::parse_hexstr_to_binbuff(tx_hex_str, b)) + { + res.status = "Failed to parse hex representation of transaction hash"; + return true; + } + if(b.size() != sizeof(crypto::hash)) + { + res.status = "Failed, size of data mismatch"; + } + vh.push_back(*reinterpret_cast<const crypto::hash*>(b.data())); + } + std::list<crypto::hash> missed_txs; + std::list<transaction> txs; + bool r = m_core.get_transactions(vh, txs, missed_txs); + if(!r) + { + res.status = "Failed"; + return true; + } + + BOOST_FOREACH(auto& tx, txs) + { + blobdata blob = t_serializable_object_to_blob(tx); + res.txs_as_hex.push_back(string_tools::buff_to_hex_nodelimer(blob)); + } + + BOOST_FOREACH(const auto& miss_tx, missed_txs) + { + res.missed_tx.push_back(string_tools::pod_to_hex(miss_tx)); + } + + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + /*bool core_rpc_server::on_get_outputs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) + { + return true; + }*/ + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res) + { + std::string tx_blob; + if(!string_tools::parse_hexstr_to_binbuff(req.tx_as_hex, tx_blob)) + { + LOG_PRINT_L0("[on_send_raw_tx]: Failed to parse tx from hexbuff: " << req.tx_as_hex); + res.status = "Failed"; + return true; + } + + cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); + tx_verification_context tvc = AUTO_VAL_INIT(tvc); + if(!m_core.handle_incoming_tx(tx_blob, tvc, false)) + { + LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx"); + res.status = "Failed"; + return true; + } + + if(tvc.m_verifivation_failed) + { + LOG_PRINT_L0("[on_send_raw_tx]: tx verification failed"); + res.status = "Failed"; + return true; + } + + if(!tvc.m_should_be_relayed) + { + LOG_PRINT_L0("[on_send_raw_tx]: tx accepted, but not relayed"); + res.status = "Not relayed"; + return true; + } + + + NOTIFY_NEW_TRANSACTIONS::request r; + r.txs.push_back(tx_blob); + m_core.get_protocol()->relay_transactions(r, fake_context); + //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res) + { + account_public_address adr; + if(!get_account_address_from_str(adr, req.miner_address)) + { + res.status = "Failed, wrong address"; + return true; + } + + if(!m_core.get_miner().start(adr, static_cast<size_t>(req.threads_count))) + { + res.status = "Failed, mining not started"; + return true; + } + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res) + { + + if(!m_core.get_miner().stop()) + { + res.status = "Failed, mining not stopped"; + 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) + { + res = m_core.get_current_blockchain_height(); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp) + { + if(req.size() != 1) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; + error_resp.message = "Wrong parameters, expected height"; + return false; + } + uint64_t h = req[0]; + if(m_core.get_current_blockchain_height() <= h) + { + error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT; + error_resp.message = std::string("To big height: ") + std::to_string(h) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height()); + } + res = string_tools::pod_to_hex(m_core.get_block_id_by_height(h)); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + uint64_t slow_memmem(void* start_buff, size_t buflen,void* pat,size_t patlen) + { + void* buf = start_buff; + void* end=(char*)buf+buflen-patlen; + while((buf=memchr(buf,((char*)pat)[0],buflen))) + { + if(buf>end) + return 0; + if(memcmp(buf,pat,patlen)==0) + return (char*)buf - (char*)start_buff; + buf=(char*)buf+1; + } + return 0; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp) + { + if(req.reserve_size > 255) + { + error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_RESERVE_SIZE; + error_resp.message = "To big reserved size, maximum 255"; + return false; + } + + cryptonote::account_public_address acc = AUTO_VAL_INIT(acc); + + if(!req.wallet_address.size() || !cryptonote::get_account_address_from_str(acc, req.wallet_address)) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS; + error_resp.message = "Failed to parse wallet address"; + return false; + } + + block b = AUTO_VAL_INIT(b); + cryptonote::blobdata blob_reserve; + blob_reserve.resize(req.reserve_size, 0); + if(!m_core.get_block_template(b, acc, res.difficulty, res.height, blob_reserve)) + { + error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + error_resp.message = "Internal error: failed to create block template"; + LOG_ERROR("Failed to create block template"); + return false; + } + blobdata block_blob = t_serializable_object_to_blob(b); + crypto::public_key tx_pub_key = null_pkey; + cryptonote::parse_and_validate_tx_extra(b.miner_tx, tx_pub_key); + if(tx_pub_key == null_pkey) + { + error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + error_resp.message = "Internal error: failed to create block template"; + LOG_ERROR("Failed to tx pub key in coinbase extra"); + return false; + } + res.reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key)); + if(!res.reserved_offset) + { + error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + error_resp.message = "Internal error: failed to create block template"; + LOG_ERROR("Failed to find tx pub key in blockblob"); + return false; + } + res.reserved_offset += sizeof(tx_pub_key) + 3; //3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) + if(res.reserved_offset + req.reserve_size > block_blob.size()) + { + error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + error_resp.message = "Internal error: failed to create block template"; + LOG_ERROR("Failed to calculate offset for "); + return false; + } + res.blocktemplate_blob = string_tools::buff_to_hex_nodelimer(block_blob); + + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp) + { + if(req.size()!=1) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; + error_resp.message = "Wrong param"; + return false; + } + blobdata blockblob; + if(!string_tools::parse_hexstr_to_binbuff(req[0], blockblob)) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB; + 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) + { + error_resp.code = CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED; + error_resp.message = "Block not accepted"; + return false; + } + res.status = "OK"; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + + +} diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h new file mode 100644 index 000000000..4425a1ce5 --- /dev/null +++ b/src/rpc/core_rpc_server.h @@ -0,0 +1,75 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/variables_map.hpp> + +#include "net/http_server_impl_base.h" +#include "core_rpc_server_commands_defs.h" +#include "cryptonote_core/cryptonote_core.h" +#include "p2p/net_node.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" + +namespace cryptonote +{ + /************************************************************************/ + /* */ + /************************************************************************/ + class core_rpc_server: public epee::http_server_impl_base<core_rpc_server> + { + public: + core_rpc_server(core& cr, nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >& p2p); + + static void init_options(boost::program_options::options_description& desc); + bool init(const boost::program_options::variables_map& vm); + private: + + CHAIN_HTTP_TO_MAP2(); //forward http requests to uri map + + BEGIN_URI_MAP2() + MAP_URI_AUTO_JON2("/getheight", on_get_height, COMMAND_RPC_GET_HEIGHT) + MAP_URI_AUTO_JON2("/getknownblockids", on_get_known_block_ids, COMMAND_RPC_GET_KNOWN_BLOCK_IDS) + MAP_URI_AUTO_BIN2("/getblocks.bin", on_get_blocks, COMMAND_RPC_GET_BLOCKS_FAST) + MAP_URI_AUTO_BIN2("/get_o_indexes.bin", on_get_indexes, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES) + MAP_URI_AUTO_BIN2("/getrandom_outs.bin", on_get_random_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS) + MAP_URI_AUTO_JON2("/gettransactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS) + 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("/getinfo", on_get_info, COMMAND_RPC_GET_INFO) + BEGIN_JSON_RPC_MAP("/json_rpc") + MAP_JON_RPC("getblockcount", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT) + MAP_JON_RPC_WE("on_getblockhash", on_getblockhash, COMMAND_RPC_GETBLOCKHASH) + MAP_JON_RPC_WE("getblocktemplate",on_getblocktemplate, COMMAND_RPC_GETBLOCKTEMPLATE) + MAP_JON_RPC_WE("submitblock", on_submitblock, COMMAND_RPC_SUBMITBLOCK) + END_JSON_RPC_MAP() + END_URI_MAP2() + + bool on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res); + bool on_get_known_block_ids(const COMMAND_RPC_GET_KNOWN_BLOCK_IDS::request& req, COMMAND_RPC_GET_KNOWN_BLOCK_IDS::response& res); + bool on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res); + bool on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res); + bool on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res); + bool on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res); + bool on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res); + bool on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res); + bool on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res); + bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res); + + //json_rpc + bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res); + bool on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp); + bool on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp); + bool on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp); + //----------------------- + bool handle_command_line(const boost::program_options::variables_map& vm); + + core& m_core; + nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >& m_p2p; + std::string m_port; + std::string m_bind_ip; + }; +} diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h new file mode 100644 index 000000000..fbb7171a1 --- /dev/null +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -0,0 +1,343 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include "cryptonote_protocol/cryptonote_protocol_defs.h" +#include "cryptonote_core/cryptonote_basic.h" +#include "crypto/hash.h" + +namespace cryptonote +{ + //----------------------------------------------- +#define CORE_RPC_STATUS_OK "OK" + + struct COMMAND_RPC_GET_HEIGHT + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + uint64_t height; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(height) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_GET_KNOWN_BLOCK_IDS + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::list<std::string> main; + std::list<std::string> alt; + std::list<std::string> invalid; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(main) + KV_SERIALIZE(alt) + KV_SERIALIZE(invalid) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_GET_BLOCKS_FAST + { + + struct request + { + std::list<crypto::hash> block_ids; //*first 10 blocks id goes sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::list<block_complete_entry> blocks; + uint64_t start_height; + uint64_t current_height; + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(blocks) + KV_SERIALIZE(start_height) + KV_SERIALIZE(current_height) + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + //----------------------------------------------- + struct COMMAND_RPC_GET_TRANSACTIONS + { + struct request + { + std::list<std::string> txs_hashes; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(txs_hashes) + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + std::list<std::string> txs_as_hex; //transactions blobs as hex + std::list<std::string> missed_tx; //not found transactions + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(txs_as_hex) + KV_SERIALIZE(missed_tx) + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + + //----------------------------------------------- + struct COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES + { + struct request + { + crypto::hash txid; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_VAL_POD_AS_BLOB(txid) + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + std::vector<uint64_t> o_indexes; + std::string status; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(o_indexes) + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + //----------------------------------------------- + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS + { + struct request + { + std::list<uint64_t> amounts; + uint64_t outs_count; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amounts) + KV_SERIALIZE(outs_count) + END_KV_SERIALIZE_MAP() + }; + +#pragma pack (push, 1) + struct out_entry + { + uint64_t global_amount_index; + crypto::public_key out_key; + }; +#pragma pack(pop) + + struct outs_for_amount + { + uint64_t amount; + std::list<out_entry> outs; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amount) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(outs) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::vector<outs_for_amount> outs; + std::string status; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(outs) + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + //----------------------------------------------- + struct COMMAND_RPC_SEND_RAW_TX + { + struct request + { + std::string tx_as_hex; + + request() {} + explicit request(const transaction &); + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tx_as_hex) + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + //----------------------------------------------- + struct COMMAND_RPC_START_MINING + { + struct request + { + std::string miner_address; + uint64_t threads_count; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(miner_address) + KV_SERIALIZE(threads_count) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + //----------------------------------------------- + struct COMMAND_RPC_GET_INFO + { + struct request + { + + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + uint64_t height; + uint64_t difficulty; + uint64_t tx_count; + uint64_t tx_pool_size; + uint64_t alt_blocks_count; + uint64_t outgoing_connections_count; + uint64_t incoming_connections_count; + uint64_t white_peerlist_size; + uint64_t grey_peerlist_size; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(height) + KV_SERIALIZE(difficulty) + KV_SERIALIZE(tx_count) + KV_SERIALIZE(tx_pool_size) + KV_SERIALIZE(alt_blocks_count) + KV_SERIALIZE(outgoing_connections_count) + KV_SERIALIZE(incoming_connections_count) + KV_SERIALIZE(white_peerlist_size) + KV_SERIALIZE(grey_peerlist_size) + END_KV_SERIALIZE_MAP() + }; + }; + + + //----------------------------------------------- + struct COMMAND_RPC_STOP_MINING + { + 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 + { + typedef std::list<std::string> request; + + typedef uint64_t response; + }; + + struct COMMAND_RPC_GETBLOCKHASH + { + typedef std::vector<uint64_t> request; + + typedef std::string response; + }; + + + struct COMMAND_RPC_GETBLOCKTEMPLATE + { + struct request + { + uint64_t reserve_size; //max 255 bytes + std::string wallet_address; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(reserve_size) + KV_SERIALIZE(wallet_address) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + uint64_t difficulty; + uint64_t height; + uint64_t reserved_offset; + blobdata blocktemplate_blob; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(difficulty) + KV_SERIALIZE(height) + KV_SERIALIZE(reserved_offset) + KV_SERIALIZE(blocktemplate_blob) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_SUBMITBLOCK + { + typedef std::vector<std::string> request; + + struct response + { + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + + + +} + diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h new file mode 100644 index 000000000..5e3296d05 --- /dev/null +++ b/src/rpc/core_rpc_server_error_codes.h @@ -0,0 +1,14 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + + +#define CORE_RPC_ERROR_CODE_WRONG_PARAM -1 +#define CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT -2 +#define CORE_RPC_ERROR_CODE_TOO_BIG_RESERVE_SIZE -3 +#define CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS -4 +#define CORE_RPC_ERROR_CODE_INTERNAL_ERROR -5 +#define CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB -6 +#define CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED -7 diff --git a/src/serialization/binary_archive.h b/src/serialization/binary_archive.h new file mode 100644 index 000000000..f28e45c0c --- /dev/null +++ b/src/serialization/binary_archive.h @@ -0,0 +1,169 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +/* binary_archive.h + * + * Portable (low-endian) binary archive */ +#pragma once + +#include <cassert> +#include <iostream> +#include <iterator> +#include <boost/type_traits/make_unsigned.hpp> + +#include "common/varint.h" +#include "warnings.h" + +PUSH_WARNINGS +DISABLE_VS_WARNINGS(4244) + +//TODO: fix size_t warning in x32 platform + +template <class Stream, bool IsSaving> +struct binary_archive_base +{ + typedef Stream stream_type; + typedef binary_archive_base<Stream, IsSaving> base_type; + typedef boost::mpl::bool_<IsSaving> is_saving; + + typedef uint8_t variant_tag_type; + + explicit binary_archive_base(stream_type &s) : stream_(s) { } + + void tag(const char *) { } + void begin_object() { } + void end_object() { } + void begin_variant() { } + void end_variant() { } + stream_type &stream() { return stream_; } +protected: + stream_type &stream_; +}; + +template <bool W> +struct binary_archive; + +template <> +struct binary_archive<false> : public binary_archive_base<std::istream, false> +{ + explicit binary_archive(stream_type &s) : base_type(s) { + stream_type::streampos pos = stream_.tellg(); + stream_.seekg(0, std::ios_base::end); + eof_pos_ = stream_.tellg(); + stream_.seekg(pos); + } + + template <class T> + void serialize_int(T &v) + { + serialize_uint(*(typename boost::make_unsigned<T>::type *)&v); + } + + template <class T> + void serialize_uint(T &v, size_t width = sizeof(T)) + { + T ret = 0; + unsigned shift = 0; + for (size_t i = 0; i < width; i++) { + //std::cerr << "tell: " << stream_.tellg() << " value: " << ret << std::endl; + char c; + stream_.get(c); + T b = (unsigned char)c; + ret += (b << shift); + shift += 8; + } + v = ret; + } + void serialize_blob(void *buf, size_t len, const char *delimiter="") { stream_.read((char *)buf, len); } + + template <class T> + void serialize_varint(T &v) + { + serialize_uvarint(*(typename boost::make_unsigned<T>::type *)(&v)); + } + + template <class T> + void serialize_uvarint(T &v) + { + typedef std::istreambuf_iterator<char> it; + tools::read_varint(it(stream_), it(), v); // XXX handle failure + } + void begin_array(size_t &s) + { + serialize_varint(s); + } + void begin_array() { } + + void delimit_array() { } + void end_array() { } + + void begin_string(const char *delimiter="\"") { } + void end_string(const char *delimiter="\"") { } + + void read_variant_tag(variant_tag_type &t) { + serialize_int(t); + } + + size_t remaining_bytes() { + if (!stream_.good()) + return 0; + //std::cerr << "tell: " << stream_.tellg() << std::endl; + assert(stream_.tellg() <= eof_pos_); + return eof_pos_ - stream_.tellg(); + } +protected: + std::streamoff eof_pos_; +}; + +template <> +struct binary_archive<true> : public binary_archive_base<std::ostream, true> +{ + explicit binary_archive(stream_type &s) : base_type(s) { } + + template <class T> + void serialize_int(T v) + { + serialize_uint(static_cast<typename boost::make_unsigned<T>::type>(v)); + } + template <class T> + void serialize_uint(T v) + { + for (size_t i = 0; i < sizeof(T); i++) { + stream_.put((char)(v & 0xff)); + if (1 < sizeof(T)) { + v >>= 8; + } + } + } + void serialize_blob(void *buf, size_t len, const char *delimiter="") { stream_.write((char *)buf, len); } + + template <class T> + void serialize_varint(T &v) + { + serialize_uvarint(*(typename boost::make_unsigned<T>::type *)(&v)); + } + + template <class T> + void serialize_uvarint(T &v) + { + typedef std::ostreambuf_iterator<char> it; + tools::write_varint(it(stream_), v); + } + void begin_array(size_t s) + { + serialize_varint(s); + } + void begin_array() { } + void delimit_array() { } + void end_array() { } + + void begin_string(const char *delimiter="\"") { } + void end_string(const char *delimiter="\"") { } + + void write_variant_tag(variant_tag_type t) { + serialize_int(t); + } +}; + +POP_WARNINGS diff --git a/src/serialization/binary_utils.h b/src/serialization/binary_utils.h new file mode 100644 index 000000000..d06e8a22a --- /dev/null +++ b/src/serialization/binary_utils.h @@ -0,0 +1,28 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <sstream> +#include "binary_archive.h" + +namespace serialization { + +template <class T> +bool parse_binary(const std::string &blob, T &v) +{ + std::istringstream istr(blob); + binary_archive<false> iar(istr); + return ::serialization::serialize(iar, v); +} + +template<class T> +bool dump_binary(T& v, std::string& blob) +{ + std::stringstream ostr; + binary_archive<true> oar(ostr); + bool success = ::serialization::serialize(oar, v); + blob = ostr.str(); + return success && ostr.good(); +}; + +} // namespace serialization diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h new file mode 100644 index 000000000..89d3c9885 --- /dev/null +++ b/src/serialization/crypto.h @@ -0,0 +1,62 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "serialization.h" +#include "debug_archive.h" +#include "crypto/chacha8.h" +#include "crypto/crypto.h" +#include "crypto/hash.h" + +// read +template <template <bool> class Archive> +bool do_serialize(Archive<false> &ar, std::vector<crypto::signature> &v) +{ + size_t cnt = v.size(); + v.clear(); + + // very basic sanity check + if (ar.remaining_bytes() < cnt*sizeof(crypto::signature)) { + ar.stream().setstate(std::ios::failbit); + return false; + } + + v.reserve(cnt); + for (size_t i = 0; i < cnt; i++) { + v.resize(i+1); + ar.serialize_blob(&(v[i]), sizeof(crypto::signature), ""); + if (!ar.stream().good()) + return false; + } + return true; +} + +// write +template <template <bool> class Archive> +bool do_serialize(Archive<true> &ar, std::vector<crypto::signature> &v) +{ + if (0 == v.size()) return true; + ar.begin_string(); + size_t cnt = v.size(); + for (size_t i = 0; i < cnt; i++) { + ar.serialize_blob(&(v[i]), sizeof(crypto::signature), ""); + if (!ar.stream().good()) + return false; + } + ar.end_string(); + return true; +} + +BLOB_SERIALIZER(crypto::chacha8_iv); +BLOB_SERIALIZER(crypto::hash); +BLOB_SERIALIZER(crypto::public_key); +BLOB_SERIALIZER(crypto::secret_key); +BLOB_SERIALIZER(crypto::key_derivation); +BLOB_SERIALIZER(crypto::key_image); +BLOB_SERIALIZER(crypto::signature); +VARIANT_TAG(debug_archive, crypto::hash, "hash"); +VARIANT_TAG(debug_archive, crypto::public_key, "public_key"); +VARIANT_TAG(debug_archive, crypto::secret_key, "secret_key"); +VARIANT_TAG(debug_archive, crypto::key_derivation, "key_derivation"); +VARIANT_TAG(debug_archive, crypto::key_image, "key_image"); +VARIANT_TAG(debug_archive, crypto::signature, "signature"); diff --git a/src/serialization/debug_archive.h b/src/serialization/debug_archive.h new file mode 100644 index 000000000..08baee016 --- /dev/null +++ b/src/serialization/debug_archive.h @@ -0,0 +1,28 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include "json_archive.h" +#include "variant.h" + +template <bool W> +struct debug_archive : public json_archive<W> { + typedef typename json_archive<W>::stream_type stream_type; + + debug_archive(stream_type &s) : json_archive<W>(s) { } +}; + +template <class T> +struct serializer<debug_archive<true>, T> +{ + static void serialize(debug_archive<true> &ar, T &v) + { + ar.begin_object(); + ar.tag(variant_serialization_traits<debug_archive<true>, T>::get_tag()); + serializer<json_archive<true>, T>::serialize(ar, v); + ar.end_object(); + ar.stream() << std::endl; + } +}; diff --git a/src/serialization/json_archive.h b/src/serialization/json_archive.h new file mode 100644 index 000000000..49ad74d41 --- /dev/null +++ b/src/serialization/json_archive.h @@ -0,0 +1,145 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +/* json_archive.h + * + * JSON archive */ + +#pragma once + +#include "serialization.h" +#include <cassert> +#include <iostream> +#include <iomanip> + +template <class Stream, bool IsSaving> +struct json_archive_base +{ + typedef Stream stream_type; + typedef json_archive_base<Stream, IsSaving> base_type; + typedef boost::mpl::bool_<IsSaving> is_saving; + + typedef const char *variant_tag_type; + + json_archive_base(stream_type &s, bool indent = false) : stream_(s), indent_(indent), object_begin(false), depth_(0) { } + + void tag(const char *tag) { + if (!object_begin) + stream_ << ", "; + make_indent(); + stream_ << '"' << tag << "\": "; + object_begin = false; + } + + void begin_object() + { + stream_ << "{"; + ++depth_; + object_begin = true; + } + + void end_object() + { + --depth_; + make_indent(); + stream_ << "}"; + } + + void begin_variant() { begin_object(); } + void end_variant() { end_object(); } + Stream &stream() { return stream_; } + +protected: + void make_indent() + { + if (indent_) + { + stream_ << '\n' << std::string(2 * depth_, ' '); + } + } + +protected: + stream_type &stream_; + bool indent_; + bool object_begin; + size_t depth_; +}; + +template <bool W> +struct json_archive; + +template <> +struct json_archive<true> : public json_archive_base<std::ostream, true> +{ + json_archive(stream_type &s, bool indent = false) : base_type(s, indent) { } + + template<typename T> + static auto promote_to_printable_integer_type(T v) -> decltype(+v) + { + // Unary operator '+' performs integral promotion on type T [expr.unary.op]. + // If T is signed or unsigned char, it's promoted to int and printed as number. + return +v; + } + + template <class T> + void serialize_int(T v) + { + stream_ << std::dec << promote_to_printable_integer_type(v); + } + + void serialize_blob(void *buf, size_t len, const char *delimiter="\"") { + begin_string(delimiter); + for (size_t i = 0; i < len; i++) { + unsigned char c = ((unsigned char *)buf)[i]; + stream_ << std::hex << std::setw(2) << std::setfill('0') << (int)c; + } + end_string(delimiter); + } + + template <class T> + void serialize_varint(T &v) + { + stream_ << std::dec << promote_to_printable_integer_type(v); + } + + void begin_string(const char *delimiter="\"") + { + stream_ << delimiter; + } + + void end_string(const char *delimiter="\"") + { + stream_ << delimiter; + } + + void begin_array(size_t s=0) + { + inner_array_size_ = s; + ++depth_; + stream_ << "[ "; + } + + void delimit_array() + { + stream_ << ", "; + } + + void end_array() + { + --depth_; + if (0 < inner_array_size_) + { + make_indent(); + } + stream_ << "]"; + } + + void write_variant_tag(const char *t) + { + tag(t); + } + +private: + size_t inner_array_size_; +}; diff --git a/src/serialization/json_utils.h b/src/serialization/json_utils.h new file mode 100644 index 000000000..35bcc0335 --- /dev/null +++ b/src/serialization/json_utils.h @@ -0,0 +1,19 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <sstream> +#include "json_archive.h" + +namespace serialization { + +template<class T> +std::string dump_json(T &v) +{ + std::stringstream ostr; + json_archive<true> oar(ostr); + assert(serialization::serialize(oar, v)); + return ostr.str(); +}; + +} // namespace serialization diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h new file mode 100644 index 000000000..d1c6f7f63 --- /dev/null +++ b/src/serialization/serialization.h @@ -0,0 +1,146 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +/* serialization.h + * + * Simple templated serialization API */ + +#pragma once +#include <vector> +#include <string> +#include <boost/type_traits/is_integral.hpp> + +template <class T> +struct is_blob_type { typedef boost::false_type type; }; +template <class T> +struct has_free_serializer { typedef boost::true_type type; }; + +template <class Archive, class T> +struct serializer +{ + static bool serialize(Archive &ar, T &v) { + return serialize(ar, v, typename boost::is_integral<T>::type(), typename is_blob_type<T>::type()); + } + static bool serialize(Archive &ar, T &v, boost::false_type, boost::true_type) { + ar.serialize_blob(&v, sizeof(v)); + return true; + } + static bool serialize(Archive &ar, T &v, boost::true_type, boost::false_type) { + ar.serialize_int(v); + return true; + } + static bool serialize(Archive &ar, T &v, boost::false_type, boost::false_type) { + //serialize_custom(ar, v, typename has_free_serializer<T>::type()); + return v.do_serialize(ar); + } + static void serialize_custom(Archive &ar, T &v, boost::true_type) { + } +}; + +template <class Archive, class T> +inline bool do_serialize(Archive &ar, T &v) +{ + return ::serializer<Archive, T>::serialize(ar, v); +} + +#ifndef __GNUC__ +#ifndef constexpr +#define constexpr +#endif +#endif + +#define BLOB_SERIALIZER(T) \ + template<> struct is_blob_type<T> { typedef boost::true_type type; } +#define FREE_SERIALIZER(T) \ + template<> struct has_free_serializer<T> { typedef boost::true_type type; } +#define VARIANT_TAG(A, T, Tg) \ + template <bool W> struct variant_serialization_traits<A<W>, T> { static inline typename A<W>::variant_tag_type get_tag() { return Tg; } } +#define BEGIN_SERIALIZE() \ + template <bool W, template <bool> class Archive> bool do_serialize(Archive<W> &ar) { +#define BEGIN_SERIALIZE_OBJECT() \ + template <bool W, template <bool> class Archive> bool do_serialize(Archive<W> &ar) { ar.begin_object(); bool r = do_serialize_object(ar); ar.end_object(); return r; } \ + template <bool W, template <bool> class Archive> bool do_serialize_object(Archive<W> &ar){ +#define PREPARE_CUSTOM_VECTOR_SERIALIZATION(size, vec) ::serialization::detail::prepare_custom_vector_serialization(size, vec, typename Archive<W>::is_saving()) + +#define END_SERIALIZE() return true;} + + +#define VALUE(f) \ + do { \ + ar.tag(#f); \ + bool r = ::do_serialize(ar, f); \ + if (!r || !ar.stream().good()) return false; \ + } while(0); +#define FIELD_N(t, f) \ + do { \ + ar.tag(t); \ + bool r = ::do_serialize(ar, f); \ + if (!r || !ar.stream().good()) return false; \ + } while(0); +#define FIELDS(f) \ + bool r = ::do_serialize(ar, f); \ + if (!r || !ar.stream().good()) return false; +#define FIELD(f) \ + do { \ + ar.tag(#f); \ + bool r = ::do_serialize(ar, f); \ + if (!r || !ar.stream().good()) return false; \ + } while(0); +#define VARINT_FIELD(f) \ + do { \ + ar.tag(#f); \ + ar.serialize_varint(f); \ + if (!ar.stream().good()) return false; \ + } while(0); + +namespace serialization { + namespace detail + { + template <typename T> + void prepare_custom_vector_serialization(size_t size, std::vector<T>& vec, const boost::mpl::bool_<true>& /*is_saving*/) + { + } + + template <typename T> + void prepare_custom_vector_serialization(size_t size, std::vector<T>& vec, const boost::mpl::bool_<false>& /*is_saving*/) + { + vec.resize(size); + } + + template<class Stream> + bool do_check_stream_state(Stream& s, boost::mpl::bool_<true>) + { + return s.good(); + } + + template<class Stream> + bool do_check_stream_state(Stream& s, boost::mpl::bool_<false>) + { + bool result = false; + if (s.good()) + { + std::ios_base::iostate state = s.rdstate(); + result = EOF == s.peek(); + s.setstate(state); + } + return result; + } + } + + template<class Archive> + bool check_stream_state(Archive& ar) + { + return detail::do_check_stream_state(ar.stream(), typename Archive::is_saving()); + } + + template <class Archive, class T> + inline bool serialize(Archive &ar, T &v) + { + bool r = do_serialize(ar, v); + return r && check_stream_state(ar); + } +} + +#include "string.h" +#include "vector.h" diff --git a/src/serialization/string.h b/src/serialization/string.h new file mode 100644 index 000000000..437cf1c77 --- /dev/null +++ b/src/serialization/string.h @@ -0,0 +1,35 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include <memory> +#include "serialization.h" + +template <template <bool> class Archive> +inline bool do_serialize(Archive<false>& ar, std::string& str) +{ + size_t size = 0; + ar.serialize_varint(size); + if (ar.remaining_bytes() < size) + { + ar.stream().setstate(std::ios::failbit); + return false; + } + + std::unique_ptr<std::string::value_type[]> buf(new std::string::value_type[size]); + ar.serialize_blob(buf.get(), size); + str.erase(); + str.append(buf.get(), size); + return true; +} + + +template <template <bool> class Archive> +inline bool do_serialize(Archive<true>& ar, std::string& str) +{ + size_t size = str.size(); + ar.serialize_varint(size); + ar.serialize_blob(const_cast<std::string::value_type*>(str.c_str()), size); + return true; +} diff --git a/src/serialization/variant.h b/src/serialization/variant.h new file mode 100644 index 000000000..3b92fde20 --- /dev/null +++ b/src/serialization/variant.h @@ -0,0 +1,110 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <boost/variant/variant.hpp> +#include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> +#include <boost/mpl/empty.hpp> +#include <boost/mpl/if.hpp> +#include <boost/mpl/front.hpp> +#include <boost/mpl/pop_front.hpp> +#include "serialization.h" + +template <class Archive, class T> +struct variant_serialization_traits +{ +}; + +template <class Archive, class Variant, class TBegin, class TEnd> +struct variant_reader +{ + typedef typename Archive::variant_tag_type variant_tag_type; + typedef typename boost::mpl::next<TBegin>::type TNext; + typedef typename boost::mpl::deref<TBegin>::type current_type; + + static inline bool read(Archive &ar, Variant &v, variant_tag_type t) + { + if (variant_serialization_traits<Archive, current_type>::get_tag() == t) { + current_type x; + if(!::do_serialize(ar, x)) + { + ar.stream().setstate(std::ios::failbit); + return false; + } + v = x; + } else { + return variant_reader<Archive, Variant, TNext, TEnd>::read(ar, v, t); + } + return true; + } +}; + +template <class Archive, class Variant, class TBegin> +struct variant_reader<Archive, Variant, TBegin, TBegin> +{ + typedef typename Archive::variant_tag_type variant_tag_type; + + static inline bool read(Archive &ar, Variant &v, variant_tag_type t) + { + ar.stream().setstate(std::ios::failbit); + return false; + } + +}; + + +template <template <bool> class Archive, BOOST_VARIANT_ENUM_PARAMS(typename T)> +struct serializer<Archive<false>, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>> +{ + typedef boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> variant_type; + typedef typename Archive<false>::variant_tag_type variant_tag_type; + typedef typename variant_type::types types; + + static bool serialize(Archive<false> &ar, variant_type &v) { + variant_tag_type t; + ar.begin_variant(); + ar.read_variant_tag(t); + if(!variant_reader<Archive<false>, variant_type, typename boost::mpl::begin<types>::type, typename boost::mpl::end<types>::type>::read(ar, v, t)) + { + ar.stream().setstate(std::ios::failbit); + return false; + } + ar.end_variant(); + return true; + } +}; + +template <template <bool> class Archive, BOOST_VARIANT_ENUM_PARAMS(typename T)> +struct serializer<Archive<true>, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>> +{ + typedef boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> variant_type; + //typedef typename Archive<true>::variant_tag_type variant_tag_type; + + struct visitor : public boost::static_visitor<bool> + { + Archive<true> &ar; + + visitor(Archive<true> &a) : ar(a) { } + + template <class T> + bool operator ()(T &rv) const + { + ar.begin_variant(); + ar.write_variant_tag(variant_serialization_traits<Archive<true>, T>::get_tag()); + if(!::do_serialize(ar, rv)) + { + ar.stream().setstate(std::ios::failbit); + return false; + } + ar.end_variant(); + return true; + } + }; + + static bool serialize(Archive<true> &ar, variant_type &v) { + return boost::apply_visitor(visitor(ar), v); + } +}; diff --git a/src/serialization/vector.h b/src/serialization/vector.h new file mode 100644 index 000000000..9a0c0ee56 --- /dev/null +++ b/src/serialization/vector.h @@ -0,0 +1,74 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include "serialization.h" + +namespace serialization +{ + namespace detail + { + template <typename Archive, class T> + bool serialize_vector_element(Archive& ar, T& e) + { + return ::do_serialize(ar, e); + } + + template <typename Archive> + bool serialize_vector_element(Archive& ar, uint64_t& e) + { + ar.serialize_varint(e); + return true; + } + } +} + +template <template <bool> class Archive, class T> +bool do_serialize(Archive<false> &ar, std::vector<T> &v) +{ + size_t cnt; + ar.begin_array(cnt); + if (!ar.stream().good()) + return false; + v.clear(); + + // very basic sanity check + if (ar.remaining_bytes() < cnt) { + ar.stream().setstate(std::ios::failbit); + return false; + } + + v.reserve(cnt); + for (size_t i = 0; i < cnt; i++) { + if (i > 0) + ar.delimit_array(); + v.resize(i+1); + if (!::serialization::detail::serialize_vector_element(ar, v[i])) + return false; + if (!ar.stream().good()) + return false; + } + ar.end_array(); + return true; +} + +template <template <bool> class Archive, class T> +bool do_serialize(Archive<true> &ar, std::vector<T> &v) +{ + size_t cnt = v.size(); + ar.begin_array(cnt); + for (size_t i = 0; i < cnt; i++) { + if (!ar.stream().good()) + return false; + if (i > 0) + ar.delimit_array(); + if(!::serialization::detail::serialize_vector_element(ar, v[i])) + return false; + if (!ar.stream().good()) + return false; + } + ar.end_array(); + return true; +} diff --git a/src/simplewallet/password_container.cpp b/src/simplewallet/password_container.cpp new file mode 100644 index 000000000..0b9dc1cdf --- /dev/null +++ b/src/simplewallet/password_container.cpp @@ -0,0 +1,231 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "password_container.h" + +#include <iostream> +#include <memory.h> +#include <stdio.h> + +#if defined(_WIN32) +#include <io.h> +#include <windows.h> +#else +#include <termios.h> +#include <unistd.h> +#endif + +namespace tools +{ + namespace + { + bool is_cin_tty(); + } + + password_container::password_container() + : m_empty(true) + { + } + + password_container::password_container(std::string&& password) + : m_empty(false) + , m_password(std::move(password)) + { + } + + password_container::password_container(password_container&& rhs) + : m_empty(std::move(rhs.m_empty)) + , m_password(std::move(rhs.m_password)) + { + } + + password_container::~password_container() + { + clear(); + } + + void password_container::clear() + { + if (0 < m_password.capacity()) + { + m_password.replace(0, m_password.capacity(), m_password.capacity(), '\0'); + m_password.resize(0); + } + m_empty = true; + } + + bool password_container::read_password() + { + clear(); + + bool r; + if (is_cin_tty()) + { + std::cout << "password: "; + r = read_from_tty(); + } + else + { + r = read_from_file(); + } + + if (r) + { + m_empty = false; + } + else + { + clear(); + } + + return r; + } + + bool password_container::read_from_file() + { + m_password.reserve(max_password_size); + for (size_t i = 0; i < max_password_size; ++i) + { + char ch = static_cast<char>(std::cin.get()); + if (std::cin.eof() || ch == '\n' || ch == '\r') + { + break; + } + else if (std::cin.fail()) + { + return false; + } + else + { + m_password.push_back(ch); + } + } + + return true; + } + +#if defined(_WIN32) + + namespace + { + bool is_cin_tty() + { + return 0 != _isatty(_fileno(stdin)); + } + } + + bool password_container::read_from_tty() + { + const char BACKSPACE = 8; + + HANDLE h_cin = ::GetStdHandle(STD_INPUT_HANDLE); + + DWORD mode_old; + ::GetConsoleMode(h_cin, &mode_old); + DWORD mode_new = mode_old & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT); + ::SetConsoleMode(h_cin, mode_new); + + bool r = true; + m_password.reserve(max_password_size); + while (m_password.size() < max_password_size) + { + DWORD read; + char ch; + r = (TRUE == ::ReadConsoleA(h_cin, &ch, 1, &read, NULL)); + r &= (1 == read); + if (!r) + { + break; + } + else if (ch == '\n' || ch == '\r') + { + std::cout << std::endl; + break; + } + else if (ch == BACKSPACE) + { + if (!m_password.empty()) + { + m_password.back() = '\0'; + m_password.resize(m_password.size() - 1); + std::cout << "\b \b"; + } + } + else + { + m_password.push_back(ch); + std::cout << '*'; + } + } + + ::SetConsoleMode(h_cin, mode_old); + + return r; + } + +#else + + namespace + { + bool is_cin_tty() + { + return 0 != isatty(fileno(stdin)); + } + + int getch() + { + struct termios tty_old; + tcgetattr(STDIN_FILENO, &tty_old); + + struct termios tty_new; + tty_new = tty_old; + tty_new.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &tty_new); + + int ch = getchar(); + + tcsetattr(STDIN_FILENO, TCSANOW, &tty_old); + + return ch; + } + } + + bool password_container::read_from_tty() + { + const char BACKSPACE = 127; + + m_password.reserve(max_password_size); + while (m_password.size() < max_password_size) + { + int ch = getch(); + if (EOF == ch) + { + return false; + } + else if (ch == '\n' || ch == '\r') + { + std::cout << std::endl; + break; + } + else if (ch == BACKSPACE) + { + if (!m_password.empty()) + { + m_password.back() = '\0'; + m_password.resize(m_password.size() - 1); + std::cout << "\b \b"; + } + } + else + { + m_password.push_back(ch); + std::cout << '*'; + } + } + + return true; + } + +#endif +} diff --git a/src/simplewallet/password_container.h b/src/simplewallet/password_container.h new file mode 100644 index 000000000..2e99d9a62 --- /dev/null +++ b/src/simplewallet/password_container.h @@ -0,0 +1,36 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include <string> + +namespace tools +{ + class password_container + { + public: + static const size_t max_password_size = 1024; + + password_container(); + password_container(std::string&& password); + password_container(password_container&& rhs); + ~password_container(); + + void clear(); + bool empty() const { return m_empty; } + const std::string& password() const { return m_password; } + void password(std::string&& val) { m_password = std::move(val); m_empty = false; } + bool read_password(); + + private: + bool read_from_file(); + bool read_from_tty(); + + private: + bool m_empty; + std::string m_password; + }; +} diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp new file mode 100644 index 000000000..bda47019b --- /dev/null +++ b/src/simplewallet/simplewallet.cpp @@ -0,0 +1,523 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <thread> +#include <boost/lexical_cast.hpp> +#include <boost/program_options.hpp> +#include <boost/algorithm/string.hpp> +#include "include_base_utils.h" +#include "common/command_line.h" +#include "p2p/net_node.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "simplewallet.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "storages/http_abstract_invoke.h" +#include "rpc/core_rpc_server_commands_defs.h" +#include "version.h" + +#if defined(WIN32) +#include <crtdbg.h> +#endif + +using namespace std; +using namespace epee; +using namespace cryptonote; +using boost::lexical_cast; +namespace po = boost::program_options; + +#define EXTENDED_LOGS_FILE "wallet_details.log" + + +namespace +{ + const command_line::arg_descriptor<std::string> arg_wallet_file = {"wallet-file", "Use wallet <arg>", ""}; + const command_line::arg_descriptor<std::string> arg_generate_new_wallet = {"generate-new-wallet", "Generate new wallet and save it to <arg> or <address>.wallet by default", ""}; + 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<int> arg_daemon_port = {"daemon-port", "Use daemon instance at port <arg> instead of 8080", 0}; + const command_line::arg_descriptor<uint32_t> arg_log_level = {"set_log", "", 0, true}; + + const command_line::arg_descriptor< std::vector<std::string> > arg_command = {"command", ""}; +} + +/*const char *commands_help = + "Commands:\n" + " help Show this help\n" + " address Show current account public address\n" + " exit\n" + " refresh\n" + " start_mining Start mining\n" + " set_log\n" + " show_balance Show current account balance\n" + " show_bc_height Show blockchain height\n" + " show_incoming_transfers Show coins\n" + " transfer <mixin_count> (<addr> <amount>)... Transfer <amount> to <addr>\n";*/ + + + +std::string simple_wallet::get_commands_str() +{ + std::stringstream ss; + ss << "Commands: " << ENDL; + std::string usage = m_cmd_binder.get_usage(); + boost::replace_all(usage, "\n", "\n "); + usage.insert(0, " "); + ss << usage << ENDL; + return ss.str(); +} + +bool simple_wallet::help(const std::vector<std::string> &args) +{ + std::cout << get_commands_str(); + return true; +} + +simple_wallet::simple_wallet() + : m_daemon_port(0) + , m_tried_to_connect(false) +{ + m_cmd_binder.set_handler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), "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("refresh", boost::bind(&simple_wallet::refresh, this, _1), "Resynchronize transactions and balance"); + m_cmd_binder.set_handler("show_balance", boost::bind(&simple_wallet::show_balance, this, _1), "Show current wallet balance"); + m_cmd_binder.set_handler("show_incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), "Show incoming transfers"); + m_cmd_binder.set_handler("show_bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), "Show blockchain height"); + m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), "transfer <mixin_count> <<addr> <amount>> Transfer <amount> to <address>. <mixin_count> is the number of transactions yours is indistinguishable from (from 0 to maximum available)"); + m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), "Change current log detalization level, <level> is a number 0-4"); + m_cmd_binder.set_handler("address", boost::bind(&simple_wallet::print_address, this, _1), "Show current wallet public address"); + m_cmd_binder.set_handler("save", boost::bind(&simple_wallet::save, this, _1), "Save wallet synchronized data"); + m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), "Show this help"); +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::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; + } + int 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(l < 0 || l > LOG_LEVEL_4) + { + 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; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::init(const boost::program_options::variables_map& vm) +{ + handle_command_line(vm); + + CHECK_AND_ASSERT_MES(m_daemon_address.empty() || (m_daemon_host.empty() && !m_daemon_port), false, "you can't specify daemon host or port several times"); + + size_t c = 0; + if(!m_generate_new.empty()) ++c; + if(!m_wallet_file.empty()) ++c; + CHECK_AND_ASSERT_MES(c == 1, false, "you must specify --wallet-file or --generate-new-wallet params"); + + if (m_daemon_host.empty()) + m_daemon_host = "localhost"; + if (!m_daemon_port) + m_daemon_port = RPC_DEFAULT_PORT; + if (m_daemon_address.empty()) + m_daemon_address = string("http://") + m_daemon_host + ":" + lexical_cast<string>(m_daemon_port); + + tools::password_container pwd_container; + if (command_line::has_arg(vm, arg_password)) + { + pwd_container.password(command_line::get_arg(vm, arg_password)); + } + else + { + bool r = pwd_container.read_password(); + CHECK_AND_ASSERT_MES(r, false, "failed to read wallet password"); + } + + if (!m_generate_new.empty()) + { + bool r = new_wallet(m_generate_new, pwd_container.password()); + CHECK_AND_ASSERT_MES(r, false, "account creation failed"); + } + else + { + bool r = open_wallet(m_wallet_file, pwd_container.password()); + CHECK_AND_ASSERT_MES(r, false, "could not open account"); + } + + return true; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::deinit() +{ + if (!m_wallet.get()) + return true; + + return close_wallet(); +} +//---------------------------------------------------------------------------------------------------- +bool 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); + + return true; +} +//---------------------------------------------------------------------------------------------------- +void simple_wallet::try_connect_to_daemon() +{ + if (!m_tried_to_connect) + { + m_tried_to_connect = true; + + if(!m_wallet->check_connection()) + { + std::cout << + "**********************************************************************" << ENDL << + "Wallet failed to connect to daemon. Daemon either is not started or passed wrong port. Please, make sure that daemon is running or restart the wallet with correct daemon address." << ENDL << + "**********************************************************************" << ENDL; + } + } +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::new_wallet(const string &wallet_file, const std::string& password) +{ + m_wallet_file = wallet_file; + if(boost::filesystem::exists(wallet_file)) + { + std::cout << "wallet creation failed, file " << wallet_file << " already exists" << std::endl; + return false; + } + + m_wallet.reset(new tools::wallet2()); + bool r = m_wallet->generate(wallet_file, password); + if(!r) + return false; + + cout << "Generated new wallet" << ENDL; + print_address(std::vector<std::string>()); + r = m_wallet->init(m_daemon_address); + CHECK_AND_ASSERT_MES(r, false, "failed to init wallet"); + std::cout << "**********************************************************************" << ENDL + << "Your wallet has been generated. " << ENDL + << "To start synchronizing with the daemon use \"refresh\" command." << ENDL + << "Use \"help\" command to see the list of available commands." << ENDL + << "Always use \"exit\" command when closing simplewallet to save " + << "current session's state. Otherwise, you will possibly need to synchronize " << ENDL + << "your wallet again. Your wallet key is NOT under risk anyway." << ENDL + << "**********************************************************************" << ENDL; + return true; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::open_wallet(const string &wallet_file, const std::string& password) +{ + m_wallet_file = wallet_file; + m_wallet.reset(new tools::wallet2()); + + bool r = m_wallet->load(m_wallet_file, password); + CHECK_AND_ASSERT_MES(r, false, "failed to load wallet " + m_wallet_file); + r = m_wallet->init(m_daemon_address); + CHECK_AND_ASSERT_MES(r, false, "failed to init wallet"); + + refresh(vector<string>()); + std::cout << "**********************************************************************" << ENDL + << "Use \"help\" command to see the list of available commands." << ENDL + << "**********************************************************************" << ENDL ; + return true; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::close_wallet() +{ + bool r = m_wallet->deinit(); + CHECK_AND_ASSERT_MES(r, false, "failed to deinit wallet"); + r = m_wallet->store(); + CHECK_AND_ASSERT_MES(r, false, "failed to store wallet " + m_wallet_file); + return true; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::save(const std::vector<std::string> &args) +{ + bool r = m_wallet->store(); + CHECK_AND_ASSERT_MES(r, false, "failed to store wallet " + m_wallet_file); + std::cout << "Wallet data saved" << ENDL; + return true; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::start_mining(const vector<string>& args) +{ + try_connect_to_daemon(); + + COMMAND_RPC_START_MINING::request req; + req.miner_address = m_wallet->get_account().get_public_address_str(); + req.threads_count = 1; + if(args.size() == 1) + { + if(!string_tools::get_xtype_from_string(req.threads_count, args[0])) + { + std::cout << "Threads count value invalid \"" << args[0] << "\"" << ENDL; + return false; + } + } + COMMAND_RPC_START_MINING::response res; + bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/start_mining", req, res, m_http_client); + if (!r) + std::cout << "Mining has NOT been started" << std::endl; + CHECK_AND_ASSERT_MES(r, EXIT_FAILURE, "failed to invoke http request"); + std::cout << "Mining started in daemon." << ENDL; + return true; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::stop_mining(const vector<string>& args) +{ + try_connect_to_daemon(); + + COMMAND_RPC_STOP_MINING::request req; + COMMAND_RPC_STOP_MINING::response res; + bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/stop_mining", req, res, m_http_client); + if (!r) + std::cout << "Mining has NOT been stopped" << std::endl; + CHECK_AND_ASSERT_MES(r, EXIT_FAILURE, "failed to invoke http request"); + std::cout << "Mining stopped in daemon." << ENDL; + return true; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::refresh(const vector<string>& args) +{ + try_connect_to_daemon(); + + std::cout << "Starting refresh..." << endl; + std::atomic<bool> refresh_is_done(false); + std::thread th([&]() + { + epee::misc_utils::sleep_no_w(1000); + while(!refresh_is_done) + { + bool ok; + uint64_t bc_height = get_daemon_blockchain_height(ok); + if (ok) + cout << "Height " << m_wallet->get_blockchain_current_height() << " of " << bc_height << endl; + epee::misc_utils::sleep_no_w(1000); + } + }); + uint64_t fetched_blocks = 0; + bool money_received = false; + bool ok = m_wallet->refresh(fetched_blocks, money_received); + refresh_is_done = true; + th.join(); + if (ok) + std::cout << "Refresh done, blocks received: " << fetched_blocks << endl; + else + std::cout << "Refresh failed, no blocks received" << std::endl; + show_balance(vector<string>()); + return true; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::show_balance(const vector<string>& args) +{ + cout << "balance: " << print_money(m_wallet->balance()) << ", unlocked balance: " << print_money(m_wallet->unlocked_balance()) << endl; + return true; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::show_incoming_transfers(const vector<string>& args) +{ + m_wallet->show_incoming_transfers(); + return true; +} +//---------------------------------------------------------------------------------------------------- +uint64_t simple_wallet::get_daemon_blockchain_height(bool& ok) +{ + COMMAND_RPC_GET_HEIGHT::request req; + COMMAND_RPC_GET_HEIGHT::response res = boost::value_initialized<COMMAND_RPC_GET_HEIGHT::response>(); + ok = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/getheight", req, res, m_http_client); + CHECK_AND_ASSERT_MES(ok, 0, "failed to invoke http request"); + return res.height; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::show_blockchain_height(const vector<string>& args) +{ + try_connect_to_daemon(); + + bool ok; + uint64_t bc_height = get_daemon_blockchain_height(ok); + if (ok) + cout << "core returned height: " << bc_height << endl; + else + std::cout << "Failed to get blockchain height" << std::endl; + return true; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::transfer(const vector<string> &args_) +{ + try_connect_to_daemon(); + + vector<string> local_args = args_; + if(local_args.size() < 3) + { + std::cout << "wrong transfer arguments" << std::endl; + help(vector<string>()); + return true; + } + size_t fake_outs_count; + if(!string_tools::get_xtype_from_string(fake_outs_count, local_args[0])) + { + std::cout << " ambiguity_degree set wrong" << std::endl; + help(vector<string>()); + return true; + } + local_args.erase(local_args.begin()); + if(local_args.size() % 2 != 0) + { + cout << "wrong transfer arguments" << endl; + help(vector<string>()); + return true; + } + + vector<cryptonote::tx_destination_entry> dsts; + uint64_t summary_amount = 0; + for (size_t i = 0; i < local_args.size(); i += 2) + { + cryptonote::tx_destination_entry de; + if(!cryptonote::parse_amount(de.amount, local_args[i+1])) + { + cout << "Wrong transfer arguments" << endl;; + help(vector<string>()); + return true; + } + if(de.amount <= 0) + { + cout << "Wrong transfer amount: " << de.amount << endl;; + help(vector<string>()); + return true; + } + summary_amount += de.amount; + if(!get_account_address_from_str(de.addr, local_args[i])) + { + cout << "Wrong address: " << local_args[i] << endl; + help(vector<string>()); + return true; + } + dsts.push_back(de); + } + + if(summary_amount > m_wallet->unlocked_balance()) + { + cout << "Not enough money to transfer " << print_money(summary_amount) << ", available(unlocked) only " << print_money(m_wallet->unlocked_balance()) << endl; + return true; + } + + m_wallet->transfer(dsts, fake_outs_count, 0, DEFAULT_FEE); + return true; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::run() +{ + m_cmd_binder.run_handling(""); + return true; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::print_address(const std::vector<std::string> &args) +{ + std::cout << "Public address: " << m_wallet->get_account().get_public_address_str() << ENDL; + return true; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::process_command(const std::vector<std::string> &args) +{ + return m_cmd_binder.process_command_vec(args); +} +//---------------------------------------------------------------------------------------------------- +int main(int argc, char* argv[]) +{ + +#ifdef WIN32 + _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); +#endif + + //TRY_ENTRY(); + + string_tools::set_module_name_and_folder(argv[0]); + + po::options_description desc_general("General options"); + command_line::add_arg(desc_general, command_line::arg_help); + command_line::add_arg(desc_general, command_line::arg_version); + + po::options_description desc_params("Wallet options"); + command_line::add_arg(desc_params, arg_wallet_file); + command_line::add_arg(desc_params, arg_generate_new_wallet); + command_line::add_arg(desc_params, arg_password); + command_line::add_arg(desc_params, arg_daemon_address); + command_line::add_arg(desc_params, arg_daemon_host); + 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); + + po::positional_options_description positional_options; + positional_options.add(arg_command.name, -1); + + po::options_description desc_all; + desc_all.add(desc_general).add(desc_params); + cryptonote::simple_wallet w; + po::variables_map vm; + bool r = command_line::handle_error_helper(desc_all, [&]() + { + po::store(command_line::parse_command_line(argc, argv, desc_general, true), vm); + + if (command_line::get_arg(vm, command_line::arg_help)) + { + std::cout << "Usage: simplewallet [--wallet-file=<file>|--generate-new-wallet=<file>] [--daemon-address=<host>:<port>] [<COMMAND>]\n"; + std::cout << desc_all << '\n' << w.get_commands_str() << std::endl; + return false; + } + else if (command_line::get_arg(vm, command_line::arg_version)) + { + std::cout << "BYTECOIN WALLET v" << PROJECT_VERSION_LONG << ENDL; + return false; + } + + auto parser = po::command_line_parser(argc, argv).options(desc_params).positional(positional_options); + po::store(parser.run(), vm); + po::notify(vm); + return true; + }); + if (!r) + return 1; + + //set up logging options + log_space::get_set_log_detalisation_level(true, LOG_LEVEL_1); + log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); + log_space::log_singletone::add_logger(LOGGER_FILE, + log_space::log_singletone::get_default_log_file().c_str(), + log_space::log_singletone::get_default_log_folder().c_str()); + + if(command_line::has_arg(vm, arg_log_level)) + { + LOG_PRINT_L0("Setting log level = " << command_line::get_arg(vm, arg_log_level)); + log_space::get_set_log_detalisation_level(true, command_line::get_arg(vm, arg_log_level)); + } + LOG_PRINT("simplewallet starting", LOG_LEVEL_0); + + r = w.init(vm); + CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize wallet"); + + std::vector<std::string> command = command_line::get_arg(vm, arg_command); + if (!command.empty()) + w.process_command(command); + w.run(); + + w.deinit(); + + return 1; + //CATCH_ENTRY_L0("main", 1); +} diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h new file mode 100644 index 000000000..01d3ac4a7 --- /dev/null +++ b/src/simplewallet/simplewallet.h @@ -0,0 +1,74 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <memory> + +#include <boost/program_options/variables_map.hpp> + +#include "cryptonote_core/account.h" +#include "cryptonote_core/cryptonote_basic_impl.h" +#include "wallet/wallet2.h" +#include "console_handler.h" +#include "password_container.h" + + +namespace cryptonote +{ + /************************************************************************/ + /* */ + /************************************************************************/ + class simple_wallet + { + public: + typedef std::vector<std::string> command_type; + + simple_wallet(); + bool init(const boost::program_options::variables_map& vm); + bool deinit(); + bool run(); + + //wallet *create_wallet(); + bool process_command(const std::vector<std::string> &args); + std::string get_commands_str(); + private: + bool handle_command_line(const boost::program_options::variables_map& vm); + + bool run_console_handler(); + + bool new_wallet(const std::string &wallet_file, const std::string& password); + bool open_wallet(const std::string &wallet_file, const std::string& password); + bool close_wallet(); + + bool help(const std::vector<std::string> &args); + bool start_mining(const std::vector<std::string> &args); + bool stop_mining(const std::vector<std::string> &args); + bool refresh(const std::vector<std::string> &args); + bool show_balance(const std::vector<std::string> &args); + bool show_incoming_transfers(const std::vector<std::string> &args); + bool show_blockchain_height(const std::vector<std::string> &args); + bool transfer(const std::vector<std::string> &args); + bool print_address(const std::vector<std::string> &args); + bool save(const std::vector<std::string> &args); + bool set_log(const std::vector<std::string> &args); + + uint64_t get_daemon_blockchain_height(bool& ok); + void try_connect_to_daemon(); + + std::string m_wallet_file; + std::string m_generate_new; + std::string m_import_path; + + std::string m_daemon_address; + std::string m_daemon_host; + int m_daemon_port; + bool m_tried_to_connect; + + epee::console_handlers_binder m_cmd_binder; + + std::auto_ptr<tools::wallet2> m_wallet; + net_utils::http::http_simple_client m_http_client; + }; +} diff --git a/src/version.cmake b/src/version.cmake new file mode 100644 index 000000000..880224dc6 --- /dev/null +++ b/src/version.cmake @@ -0,0 +1,11 @@ +execute_process(COMMAND "${GIT}" describe --dirty --match "v${VERSION}" RESULT_VARIABLE RET OUTPUT_VARIABLE DESCRIPTION OUTPUT_STRIP_TRAILING_WHITESPACE) +if(RET) + message(WARNING "Cannot determine current revision. Make sure that you are building either from a Git working tree or from a source archive.") + set(VERSION "${COMMIT}") + configure_file("src/version.h.in" "${TO}") +else() + string(REGEX MATCH "([0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f])?(-dirty)? $" COMMIT "${DESCRIPTION} ") + string(STRIP "${COMMIT}" COMMIT) + set(VERSION "${COMMIT}") + configure_file("src/version.h.in" "${TO}") +endif()
\ No newline at end of file diff --git a/src/version.h.in b/src/version.h.in new file mode 100644 index 000000000..0e64c7a11 --- /dev/null +++ b/src/version.h.in @@ -0,0 +1,4 @@ +#define BUILD_COMMIT_ID "@VERSION@" +#define PROJECT_VERSION "0.8.2" +#define PROJECT_VERSION_BUILD_NO "279" +#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 new file mode 100644 index 000000000..aa4e2f518 --- /dev/null +++ b/src/wallet/wallet2.cpp @@ -0,0 +1,595 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include <boost/archive/binary_oarchive.hpp> +#include <boost/archive/binary_iarchive.hpp> + +#include <boost/utility/value_init.hpp> +#include "include_base_utils.h" +using namespace epee; + +#include "wallet2.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "rpc/core_rpc_server_commands_defs.h" +#include "misc_language.h" +#include "cryptonote_core/cryptonote_basic_impl.h" +#include "common/boost_serialization_helper.h" +#include "profile_tools.h" +#include "crypto/crypto.h" +#include "serialization/binary_utils.h" + +using namespace cryptonote; + +namespace tools +{ +//---------------------------------------------------------------------------------------------------- +bool wallet2::init(const std::string& daemon_address, uint64_t upper_transaction_size_limit) +{ + m_upper_transaction_size_limit = upper_transaction_size_limit; + m_daemon_address = daemon_address; + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::process_new_transaction(cryptonote::transaction& tx, uint64_t height) +{ + std::vector<size_t> outs; + uint64_t tx_money_got_in_outs = 0; + crypto::public_key tx_pub_key = null_pkey; + bool r = parse_and_validate_tx_extra(tx, tx_pub_key); + CHECK_AND_ASSERT_MES(r && tx_pub_key != null_pkey, false, "process_new_transaction failed."); + r = lookup_acc_outs(m_account.get_keys(), tx, tx_pub_key, outs, tx_money_got_in_outs); + CHECK_AND_ASSERT_MES(r, false, "call lookup_acc_outs failed"); + if(outs.size() && tx_money_got_in_outs) + { + //good news - got money! take care about it + //usually we have only one transfer for user in transaction + cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request req = AUTO_VAL_INIT(req); + cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response res = AUTO_VAL_INIT(res); + req.txid = get_transaction_hash(tx); + bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/get_o_indexes.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT); + CHECK_AND_ASSERT_MES(r, false, "failed to get_o_indexes.bin"); + if(res.status != CORE_RPC_STATUS_OK) + return false;// in case of split while lookup_acc_outs, transaction could be lost (especially if it is coinbase tx) + CHECK_AND_ASSERT_MES(res.o_indexes.size() == tx.vout.size(), false, "internal error: transactions outputs size=" << tx.vout.size() + << " not match with COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES response size=" << res.o_indexes.size()); + + BOOST_FOREACH(size_t o, outs) + { + CHECK_AND_ASSERT_MES(o < tx.vout.size(), false, "wrong out in transaction: internal index=" << o << ", total_outs" << tx.vout.size()); + m_transfers.push_back(boost::value_initialized<transfer_details>()); + transfer_details& td = m_transfers.back(); + td.m_block_height = height; + td.m_internal_output_index = o; + td.m_global_output_index = res.o_indexes[o]; + td.m_tx = tx; + td.m_spent = false; + cryptonote::keypair in_ephemeral; + cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, o, in_ephemeral, td.m_key_image); + CHECK_AND_ASSERT_MES(in_ephemeral.pub == boost::get<cryptonote::txout_to_key>(tx.vout[o].target).key, + false, "internal error: at key_image generating ephemeral public key not matched with output_key"); + m_key_images[td.m_key_image] = m_transfers.size()-1; + LOG_PRINT_L1("Received money: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx)); + } + } + // check all outputs for spending (compare key images) + BOOST_FOREACH(auto& in, tx.vin) + { + if(in.type() != typeid(cryptonote::txin_to_key)) + continue; + auto it = m_key_images.find(boost::get<cryptonote::txin_to_key>(in).k_image); + if(it != m_key_images.end()) + { + LOG_PRINT_L1("Spent money: " << print_money(boost::get<cryptonote::txin_to_key>(in).amount) << ", with tx: " << get_transaction_hash(tx)); + m_transfers[it->second].m_spent = true; + } + } + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::process_new_blockchain_entry(cryptonote::block& b, cryptonote::block_complete_entry& bche, crypto::hash& bl_id, uint64_t height) +{ + //handle transactions from new block + CHECK_AND_ASSERT_MES(height == m_blockchain.size(), false, "internal error: current_index=" << height << ", m_blockchain.size()=" << m_blockchain.size()); + //optimization: seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup + if(b.timestamp + 60*60*24 > m_account.get_createtime()) + { + TIME_MEASURE_START(miner_tx_handle_time); + bool r = process_new_transaction(b.miner_tx, height); + TIME_MEASURE_FINISH(miner_tx_handle_time); + CHECK_AND_NO_ASSERT_MES(r, false, "failed to process transaction"); + + TIME_MEASURE_START(txs_handle_time); + BOOST_FOREACH(auto& txblob, bche.txs) + { + cryptonote::transaction tx; + r = parse_and_validate_tx_from_blob(txblob, tx); + CHECK_AND_ASSERT_MES(r, false, "failed to parse and validate transaction from blob"); + r = process_new_transaction(tx, height); + CHECK_AND_ASSERT_MES(r, false, "failed to process transaction"); + } + TIME_MEASURE_FINISH(txs_handle_time); + LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms"); + }else + { + LOG_PRINT_L2( "Skipped block by timestamp, height: " << height << ", block time " << b.timestamp << ", account time " << m_account.get_createtime()); + } + m_blockchain.push_back(bl_id); + ++m_local_bc_height; + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::get_short_chain_history(std::list<crypto::hash>& ids) +{ + size_t i = 0; + size_t current_multiplier = 1; + size_t sz = m_blockchain.size(); + if(!sz) + return true; + size_t current_back_offset = 1; + bool genesis_included = false; + while(current_back_offset < sz) + { + ids.push_back(m_blockchain[sz-current_back_offset]); + if(sz-current_back_offset == 0) + genesis_included = true; + if(i < 10) + { + ++current_back_offset; + }else + { + current_back_offset += current_multiplier *= 2; + } + ++i; + } + if(!genesis_included) + ids.push_back(m_blockchain[0]); + + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::pull_blocks(size_t& blocks_added) +{ + blocks_added = 0; + cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req); + cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res); + get_short_chain_history(req.block_ids); + bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getblocks.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT); + CHECK_AND_ASSERT_MES(r, false, "failed to get blocks"); + CHECK_AND_ASSERT_MES(res.status == CORE_RPC_STATUS_OK, false, "failed to get blocks"); + + //find split position, if split happened + + CHECK_AND_ASSERT_MES(res.start_height < m_blockchain.size(), false, "wrong daemon response: m_start_height=" + << res.start_height << " not less than local blockchain size=" << m_blockchain.size()); + + size_t current_index = res.start_height; + BOOST_FOREACH(auto& bl_entry, res.blocks) + { + cryptonote::block bl; + r = cryptonote::parse_and_validate_block_from_blob(bl_entry.block, bl); + CHECK_AND_ASSERT_MES(r, false, "failed to parse/validate block"); + crypto::hash bl_id = get_block_hash(bl); + if(current_index >= m_blockchain.size()) + { + r = process_new_blockchain_entry(bl, bl_entry, bl_id, current_index); + if(!r) return false; + ++blocks_added; + }else + { + if(bl_id != m_blockchain[current_index]) + { + //split detected here !!! + CHECK_AND_ASSERT_MES(current_index != res.start_height, false, "wrong daemon response: first block in response " << string_tools::pod_to_hex(bl_id) + << "\nnot match with local block id " << string_tools::pod_to_hex(m_blockchain[current_index])); + detach_blockchain(current_index); + r = process_new_blockchain_entry(bl, bl_entry, bl_id, current_index); + if(!r) return false; + + } + } + ++current_index; + } + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::refresh() +{ + size_t blocks_fetched = 0; + return refresh(blocks_fetched); +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::refresh(size_t & blocks_fetched) +{ + bool received_money = false; + return refresh(blocks_fetched, received_money); +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::refresh(size_t & blocks_fetched, bool& received_money) +{ + received_money = false; + blocks_fetched = 0; + size_t added_blocks = 0; + size_t try_count = 0; + crypto::hash last_tx_hash_id = m_transfers.size() ? get_transaction_hash(m_transfers.back().m_tx) : null_hash; + + while(true) + { + bool res = pull_blocks(added_blocks); + if(!res) + { + if(try_count < 3) + { + LOG_PRINT_L0("Another try pull_blocks(try_count=" << try_count << ")..."); + ++try_count; + continue; + }else + { + LOG_PRINT_L0("pull_blocks failed, try_count=" << try_count); + return false; + } + } + blocks_fetched+=added_blocks; + + if(!added_blocks) + break; + } + if(last_tx_hash_id != (m_transfers.size() ? get_transaction_hash(m_transfers.back().m_tx) : null_hash)) + received_money = true; + + LOG_PRINT_L2( "Refresh done, blocks received: " << blocks_fetched << ", balance: " << print_money(balance()) << ", unlocked: " << print_money(unlocked_balance())); + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::detach_blockchain(uint64_t height) +{ + LOG_PRINT_L0("Detaching blockchain on height " << height); + size_t transfers_detached = 0; + + auto it = std::find_if(m_transfers.begin(), m_transfers.end(), [&](const transfer_details& td){return td.m_block_height >= height;}); + size_t i_start = it - m_transfers.begin(); + + for(size_t i = i_start; i!= m_transfers.size();i++) + { + auto it_ki = m_key_images.find(m_transfers[i].m_key_image); + CHECK_AND_ASSERT_MES(it_ki != m_key_images.end(), false, "key image not found"); + m_key_images.erase(it_ki); + ++transfers_detached; + } + m_transfers.erase(it, m_transfers.end()); + + size_t blocks_detached = m_blockchain.end() - (m_blockchain.begin()+height); + m_blockchain.erase(m_blockchain.begin()+height, m_blockchain.end()); + m_local_bc_height -= blocks_detached; + + LOG_PRINT_L0("Detached blockchain on height " << height << ", transfers detached " << transfers_detached << ", blocks detached " << blocks_detached); + + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::deinit() +{ + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::clear() +{ + m_blockchain.clear(); + m_transfers.clear(); + cryptonote::block b; + cryptonote::generate_genesis_block(b); + m_blockchain.push_back(get_block_hash(b)); + m_local_bc_height = 1; + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::store_keys(const std::string& keys_file_name, const std::string& password) +{ + std::string account_data; + bool r = epee::serialization::store_t_to_binary(m_account, account_data); + CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet keys"); + wallet2::keys_file_data keys_file_data = boost::value_initialized<wallet2::keys_file_data>(); + + crypto::chacha8_key key; + crypto::generate_chacha8_key(password, key); + std::string cipher; + cipher.resize(account_data.size()); + keys_file_data.iv = crypto::rand<crypto::chacha8_iv>(); + crypto::chacha8(account_data.data(), account_data.size(), key, keys_file_data.iv, &cipher[0]); + keys_file_data.account_data = cipher; + + std::string buf; + r = ::serialization::dump_binary(keys_file_data, buf); + r = r && epee::file_io_utils::save_string_to_file(keys_file_name, buf); //and never touch wallet_keys_file again, only read + CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << keys_file_name); + + return true; +} +//---------------------------------------------------------------------------------------------------- +namespace +{ + bool verify_keys(const crypto::secret_key& sec, const crypto::public_key& expected_pub) + { + crypto::public_key pub; + bool r = crypto::secret_key_to_public_key(sec, pub); + return r && expected_pub == pub; + } +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::load_keys(const std::string& keys_file_name, const std::string& password) +{ + wallet2::keys_file_data keys_file_data; + std::string buf; + bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf); + r &= ::serialization::parse_binary(buf, keys_file_data); + CHECK_AND_ASSERT_MES(r, false, "failed to load wallet keys file: " << keys_file_name); + + crypto::chacha8_key key; + crypto::generate_chacha8_key(password, key); + std::string account_data; + account_data.resize(keys_file_data.account_data.size()); + crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); + + const cryptonote::account_keys& keys = m_account.get_keys(); + r = epee::serialization::load_t_from_binary(m_account, account_data); + r = r && verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); + r = r && verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); + if (!r) + { + LOG_ERROR("invalid password"); + return false; + } + + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::generate(const std::string& wallet_, const std::string& password) +{ + clear(); + prepare_file_names(wallet_); + boost::system::error_code e; + if(boost::filesystem::exists(m_wallet_file, e) || boost::filesystem::exists(m_keys_file, e)) + { + LOG_PRINT_RED_L0("failed to generate wallet, file already exist or wrong path: " << wallet_); + return false; + } + + m_account.generate(); + m_account_public_address = m_account.get_keys().m_account_address; + + bool r = store_keys(m_keys_file, password); + CHECK_AND_ASSERT_MES(r, false, "Failed to store wallet key files!"); + r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str()); + if(!r) LOG_PRINT_RED_L0("String with address text not saved"); + return store(); +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::prepare_file_names(const std::string& file_path) +{ + m_keys_file = file_path; + m_wallet_file = file_path; + boost::system::error_code e; + if(string_tools::get_extension(m_keys_file) == "keys") + {//provided keys file name + m_wallet_file = string_tools::cut_off_extension(m_wallet_file); + }else + {//provided wallet file name + m_keys_file += ".keys"; + } + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::check_connection() +{ + if(m_http_client.is_connected()) + return true; + + net_utils::http::url_content u; + net_utils::parse_url(m_daemon_address, u); + if(!u.port) + u.port = 8081; + return m_http_client.connect(u.host, std::to_string(u.port), WALLET_RCP_CONNECTION_TIMEOUT); +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::load(const std::string& wallet_, const std::string& password) +{ + clear(); + prepare_file_names(wallet_); + boost::system::error_code e; + if(!boost::filesystem::exists(m_keys_file, e) || e) + { + LOG_PRINT_L0("file not found: " << m_keys_file); + return false; + } + + bool r = load_keys(m_keys_file, password); + if (!r) + return false; + + LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str()); + //keys loaded ok! + //try to load wallet file. but even if we failed, it is not big problem + if(!boost::filesystem::exists(m_wallet_file, e) || e) + { + LOG_PRINT_L0("file not found: " << m_wallet_file << ", starting with empty blockchain"); + m_account_public_address = m_account.get_keys().m_account_address; + return true; + } + r = tools::unserialize_obj_from_file(*this, m_wallet_file); + CHECK_AND_ASSERT_MES(r, false, "failed to load wallet from file " << m_wallet_file); + CHECK_AND_ASSERT_MES(m_account_public_address.m_spend_public_key == m_account.get_keys().m_account_address.m_spend_public_key && + m_account_public_address.m_view_public_key == m_account.get_keys().m_account_address.m_view_public_key, + false, "addresses of wallet keys file and wallet data file are mismatched"); + if(!m_blockchain.size()) + { + cryptonote::block b; + cryptonote::generate_genesis_block(b); + m_blockchain.push_back(get_block_hash(b)); + } + m_local_bc_height = m_blockchain.size(); + + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::store() +{ + bool r = tools::serialize_obj_to_file(*this, m_wallet_file); + CHECK_AND_ASSERT_MES(r, false, "failed to save wallet to file " << m_wallet_file); + return r; +} +//---------------------------------------------------------------------------------------------------- +void wallet2::show_incoming_transfers() +{ + uint64_t amount = 0; + if(!m_transfers.size()) + { + LOG_PRINT_L0("No incoming transfers"); + return; + } + BOOST_FOREACH(transfer_details& td, m_transfers) + { + LOG_PRINT_L0("transfer: " << print_money(td.amount()) + << ", spent: " << td.m_spent + << ", global_index: " << td.m_global_output_index + << ", tx_id: " << get_transaction_hash(td.m_tx)); + if(!td.m_spent) + amount += td.amount(); + } +} +//---------------------------------------------------------------------------------------------------- +uint64_t wallet2::unlocked_balance() +{ + uint64_t amount = 0; + BOOST_FOREACH(transfer_details& td, m_transfers) + if(!td.m_spent && is_transfer_unlocked(td)) + amount += td.amount(); + + return amount; +} +//---------------------------------------------------------------------------------------------------- +uint64_t wallet2::balance() +{ + uint64_t amount = 0; + BOOST_FOREACH(auto& td, m_transfers) + if(!td.m_spent) + amount += td.amount(); + + return amount; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::get_transfers(wallet2::transfer_container& incoming_transfers) +{ + incoming_transfers = m_transfers; + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::is_transfer_unlocked(const transfer_details& td) const +{ + if(!is_tx_spendtime_unlocked(td.m_tx.unlock_time)) + return false; + + if(td.m_block_height + DEFAULT_TX_SPENDABLE_AGE > m_blockchain.size()) + return false; + + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::is_tx_spendtime_unlocked(uint64_t unlock_time) const +{ + if(unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER) + { + //interpret as block index + if(m_blockchain.size()-1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time) + return true; + else + return false; + }else + { + //interpret as time + uint64_t current_time = static_cast<uint64_t>(time(NULL)); + if(current_time + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS >= unlock_time) + return true; + else + return false; + } + return false; +} +//---------------------------------------------------------------------------------------------------- +namespace +{ + template<typename T> + T pop_random_value(std::vector<T>& vec) + { + CHECK_AND_ASSERT_MES(!vec.empty(), T(), "Vector must be non-empty"); + + size_t idx = crypto::rand<size_t>() % vec.size(); + T res = vec[idx]; + if (idx + 1 != vec.size()) + { + vec[idx] = vec.back(); + } + vec.resize(vec.size() - 1); + + return res; + } +} +//---------------------------------------------------------------------------------------------------- +uint64_t wallet2::select_transfers(uint64_t needed_money, uint64_t dust, std::list<transfer_container::iterator>& selected_transfers) +{ + std::vector<size_t> unused_transfers_indices; + std::vector<size_t> unused_dust_indices; + for (size_t i = 0; i < m_transfers.size(); ++i) + { + const transfer_details& td = m_transfers[i]; + if (!td.m_spent && is_transfer_unlocked(td)) + { + if (dust < td.amount()) + unused_transfers_indices.push_back(i); + else + unused_dust_indices.push_back(i); + } + } + + bool at_least_one_dust_selected = unused_dust_indices.empty(); + uint64_t found_money = 0; + while (found_money < needed_money && (!unused_transfers_indices.empty() || !unused_dust_indices.empty())) + { + size_t idx; + if (at_least_one_dust_selected) + { + idx = !unused_transfers_indices.empty() ? pop_random_value(unused_transfers_indices) : pop_random_value(unused_dust_indices); + } + else + { + idx = pop_random_value(unused_dust_indices); + at_least_one_dust_selected = true; + } + + transfer_container::iterator it = m_transfers.begin() + idx; + selected_transfers.push_back(it); + found_money += it->amount(); + } + + return found_money; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, + uint64_t unlock_time, uint64_t fee, cryptonote::transaction& tx) +{ + return transfer(dsts, fake_outputs_count, unlock_time, fee, detail::digit_split_strategy, tx_dust_policy(fee), tx); +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, cryptonote::transaction& tx, transafer_fail_details& tfd) +{ + return transfer(dsts, fake_outputs_count, unlock_time, fee, detail::digit_split_strategy, tx_dust_policy(fee), tx, tfd); +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, + uint64_t unlock_time, uint64_t fee) +{ + cryptonote::transaction tx; + return transfer(dsts, fake_outputs_count, unlock_time, fee, tx); +} +//---------------------------------------------------------------------------------------------------- +} diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h new file mode 100644 index 000000000..27799e2da --- /dev/null +++ b/src/wallet/wallet2.h @@ -0,0 +1,408 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include <memory> +#include <boost/serialization/list.hpp> +#include <boost/serialization/vector.hpp> +#include <atomic> +#include "include_base_utils.h" +#include "cryptonote_core/account.h" +#include "cryptonote_core/account_boost_serialization.h" +#include "cryptonote_core/cryptonote_basic_impl.h" +#include "net/http_client.h" +#include "storages/http_abstract_invoke.h" +#include "rpc/core_rpc_server_commands_defs.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "common/unordered_containers_boost_serialization.h" +#include "crypto/chacha8.h" +#include "crypto/hash.h" + +#define DEFAULT_TX_SPENDABLE_AGE 10 +#define WALLET_RCP_CONNECTION_TIMEOUT 200000 + +namespace tools +{ + class wallet2 + { + wallet2(const wallet2&){}; + public: + wallet2(){}; + struct transfer_details + { + uint64_t m_block_height; + cryptonote::transaction m_tx; + size_t m_internal_output_index; + uint64_t m_global_output_index; + bool m_spent; + crypto::key_image m_key_image; //TODO: key_image stored twice :( + + uint64_t amount() const { return m_tx.vout[m_internal_output_index].amount; } + }; + typedef std::vector<transfer_details> transfer_container; + + struct tx_dust_policy + { + uint64_t dust_threshold; + bool add_to_fee; + cryptonote::account_public_address addr_for_dust; + + tx_dust_policy(uint64_t a_dust_threshold = 0, bool an_add_to_fee = true, cryptonote::account_public_address an_addr_for_dust = cryptonote::account_public_address()) + : dust_threshold(a_dust_threshold) + , add_to_fee(an_add_to_fee) + , addr_for_dust(an_addr_for_dust) + { + } + }; + + struct keys_file_data + { + crypto::chacha8_iv iv; + std::string account_data; + + BEGIN_SERIALIZE_OBJECT() + FIELD(iv) + FIELD(account_data) + END_SERIALIZE() + }; + + struct transafer_fail_details + { + enum fail_reason + { + error_ok = 0, + error_not_connected, + error_rejected_by_daemon, + error_too_big_transaction, + error_not_enough_money, + error_internal_error + }; + fail_reason reason; + uint64_t tx_blob_size; + uint64_t max_expected_tx_blob_size; + }; + + bool generate(const std::string& wallet, const std::string& password); + bool load(const std::string& wallet, const std::string& password); + bool store(); + cryptonote::account_base& get_account(){return m_account;} + + bool 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); + + bool refresh(); + bool refresh(size_t & blocks_fetched); + bool refresh(size_t & blocks_fetched, bool& received_money); + bool deinit(); + + uint64_t balance(); + uint64_t unlocked_balance(); + void show_incoming_transfers(); + template<typename T> + bool transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, T destination_split_strategy, const tx_dust_policy& dust_policy); + template<typename T> + bool transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction &tx, transafer_fail_details& tfd); + template<typename T> + bool transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction &tx); + bool transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee); + bool transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, cryptonote::transaction& tx); + bool transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, cryptonote::transaction& tx, transafer_fail_details& tfd); + bool check_connection(); + bool get_transfers(wallet2::transfer_container& incoming_transfers); + uint64_t get_blockchain_current_height() const { return m_local_bc_height; } + template <class t_archive> + inline void serialize(t_archive &a, const unsigned int ver) + { + if(ver < 5) + return; + a & m_blockchain; + a & m_transfers; + a & m_account_public_address; + a & m_key_images; + } + + private: + bool store_keys(const std::string& keys_file_name, const std::string& password); + bool load_keys(const std::string& keys_file_name, const std::string& password); + bool process_new_transaction(cryptonote::transaction& tx, uint64_t height); + bool process_new_blockchain_entry(cryptonote::block& b, cryptonote::block_complete_entry& bche, crypto::hash& bl_id, uint64_t height); + bool detach_blockchain(uint64_t height); + bool get_short_chain_history(std::list<crypto::hash>& ids); + bool is_tx_spendtime_unlocked(uint64_t unlock_time) const; + bool is_transfer_unlocked(const transfer_details& td) const; + bool clear(); + bool pull_blocks(size_t& blocks_added); + uint64_t select_transfers(uint64_t needed_money, uint64_t dust, std::list<transfer_container::iterator>& selected_transfers); + bool prepare_file_names(const std::string& file_path); + + cryptonote::account_base m_account; + std::string m_daemon_address; + std::string m_wallet_file; + std::string m_keys_file; + epee::net_utils::http::http_simple_client m_http_client; + std::vector<crypto::hash> m_blockchain; + std::atomic<uint64_t> m_local_bc_height; //temporary workaround + + transfer_container m_transfers; + std::unordered_map<crypto::key_image, size_t> m_key_images; + cryptonote::account_public_address m_account_public_address; + uint64_t m_upper_transaction_size_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value + }; +} +BOOST_CLASS_VERSION(tools::wallet2, 5) + +namespace boost +{ + namespace serialization + { + template <class Archive> + inline void serialize(Archive &a, tools::wallet2::transfer_details &x, const boost::serialization::version_type ver) + { + a & x.m_block_height; + a & x.m_global_output_index; + a & x.m_internal_output_index; + a & x.m_tx; + a & x.m_spent; + a & x.m_key_image; + } + } +} + +namespace tools +{ + namespace detail + { + //---------------------------------------------------------------------------------------------------- + inline void digit_split_strategy(const std::vector<cryptonote::tx_destination_entry>& dsts, + const cryptonote::tx_destination_entry& change_dst, uint64_t dust_threshold, + std::vector<cryptonote::tx_destination_entry>& splitted_dsts, uint64_t& dust) + { + splitted_dsts.clear(); + dust = 0; + + BOOST_FOREACH(auto& de, dsts) + { + cryptonote::decompose_amount_into_digits(de.amount, dust_threshold, + [&](uint64_t chunk) { splitted_dsts.push_back(cryptonote::tx_destination_entry(chunk, de.addr)); }, + [&](uint64_t a_dust) { splitted_dsts.push_back(cryptonote::tx_destination_entry(a_dust, de.addr)); } ); + } + + cryptonote::decompose_amount_into_digits(change_dst.amount, dust_threshold, + [&](uint64_t chunk) { splitted_dsts.push_back(cryptonote::tx_destination_entry(chunk, change_dst.addr)); }, + [&](uint64_t a_dust) { dust = a_dust; } ); + } + //---------------------------------------------------------------------------------------------------- + inline void null_split_strategy(const std::vector<cryptonote::tx_destination_entry>& dsts, + const cryptonote::tx_destination_entry& change_dst, uint64_t dust_threshold, + std::vector<cryptonote::tx_destination_entry>& splitted_dsts, uint64_t& dust) + { + splitted_dsts = dsts; + + dust = 0; + uint64_t change = change_dst.amount; + if (0 < dust_threshold) + { + for (uint64_t order = 10; order <= 10 * dust_threshold; order *= 10) + { + uint64_t dust_candidate = change_dst.amount % order; + uint64_t change_candidate = (change_dst.amount / order) * order; + if (dust_candidate <= dust_threshold) + { + dust = dust_candidate; + change = change_candidate; + } + else + { + break; + } + } + } + + if (0 != change) + { + splitted_dsts.push_back(cryptonote::tx_destination_entry(change, change_dst.addr)); + } + } + //---------------------------------------------------------------------------------------------------- + inline void print_source_entry(const cryptonote::tx_source_entry& src) + { + std::string indexes; + std::for_each(src.outputs.begin(), src.outputs.end(), [&](const cryptonote::tx_source_entry::output_entry& s_e) { indexes += boost::to_string(s_e.first) + " "; }); + std::cout << "amount=" << cryptonote::print_money(src.amount) << ", real_output=" <<src.real_output << ", real_output_in_tx_index=" << src.real_output_in_tx_index << ", indexes: " << indexes << ENDL; + } + //---------------------------------------------------------------------------------------------------- + } + //---------------------------------------------------------------------------------------------------- + template<typename T> + bool wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, + uint64_t unlock_time, uint64_t fee, T destination_split_strategy, const tx_dust_policy& dust_policy) + { + cryptonote::transaction tx; + return transfer(dsts, fake_outputs_count, unlock_time, fee, destination_split_strategy, dust_policy, tx); + } + + template<typename T> + bool wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, + uint64_t unlock_time, uint64_t fee, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction &tx) + { + transafer_fail_details stub = AUTO_VAL_INIT(stub); + return transfer(dsts, fake_outputs_count, unlock_time, fee, destination_split_strategy, dust_policy, tx, stub); + } + + template<typename T> + bool wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, + uint64_t unlock_time, uint64_t fee, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction &tx, transafer_fail_details& tfd) + { + using namespace cryptonote; + + uint64_t needed_money = fee; + BOOST_FOREACH(auto& dt, dsts) + { + CHECK_AND_ASSERT_MES(dt.amount > 0, false, "Wrong destination amount value: " << dt.amount); + needed_money += dt.amount; + } + + std::list<transfer_container::iterator> selected_transfers; + uint64_t found_money = select_transfers(needed_money, dust_policy.dust_threshold, selected_transfers); + + if(found_money < needed_money) + { + LOG_ERROR("not enough money, available only " << print_money(found_money) << ", expected " << print_money(needed_money) ); + tfd.reason = transafer_fail_details::error_not_enough_money; + return false; + } + //typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount outs_for_amount; + typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry; + typedef cryptonote::tx_source_entry::output_entry tx_output_entry; + + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response daemon_resp = AUTO_VAL_INIT(daemon_resp); + if(fake_outputs_count) + { + + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req = AUTO_VAL_INIT(req); + req.outs_count = fake_outputs_count + 1;// add one to make possible (if need) to skip real output key + BOOST_FOREACH(transfer_container::iterator it, selected_transfers) + { + CHECK_AND_ASSERT_MES(it->m_tx.vout.size() > it->m_internal_output_index, false, "internal error: m_internal_output_index = " + << it->m_internal_output_index << " more than " << it->m_tx.vout.size()); + 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); + tfd.reason = transafer_fail_details::error_not_connected; + CHECK_AND_ASSERT_MES(r, false, "failed to get getrandom_outs"); + tfd.reason = transafer_fail_details::error_internal_error; + CHECK_AND_ASSERT_MES(daemon_resp.status == CORE_RPC_STATUS_OK, false, "failed to getrandom_outs.bin"); + CHECK_AND_ASSERT_MES(daemon_resp.outs.size() == selected_transfers.size(), false, "internal error: daemon returned wrong response for getrandom_outs, wrong amounts count = " + << daemon_resp.outs.size() << ", expected " << selected_transfers.size()); + } + tfd.reason = transafer_fail_details::error_ok; + + //prepare inputs + size_t i = 0; + std::vector<cryptonote::tx_source_entry> sources; + BOOST_FOREACH(transfer_container::iterator it, selected_transfers) + { + sources.resize(sources.size()+1); + cryptonote::tx_source_entry& src = sources.back(); + transfer_details& td = *it; + src.amount = td.amount(); + //paste mixin transaction + if(daemon_resp.outs.size()) + { + daemon_resp.outs[i].outs.sort([](const out_entry& a, const out_entry& b){return a.global_amount_index < b.global_amount_index;}); + BOOST_FOREACH(out_entry& daemon_oe, daemon_resp.outs[i].outs) + { + if(td.m_global_output_index == daemon_oe.global_amount_index) + continue; + tx_output_entry oe; + oe.first = daemon_oe.global_amount_index; + oe.second = daemon_oe.out_key; + src.outputs.push_back(oe); + if(src.outputs.size() >= fake_outputs_count) + break; + } + } + + //paste real transaction to the random index + auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry& a) + { + return a.first >= td.m_global_output_index; + }); + //size_t real_index = src.outputs.size() ? (rand() % src.outputs.size() ):0; + tx_output_entry real_oe; + real_oe.first = td.m_global_output_index; + real_oe.second = boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key; + auto interted_it = src.outputs.insert(it_to_insert, real_oe); + src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx); + src.real_output = interted_it - src.outputs.begin(); + src.real_output_in_tx_index = td.m_internal_output_index; + detail::print_source_entry(src); + ++i; + } + + cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts); + if (needed_money < found_money) + { + change_dts.addr = m_account.get_keys().m_account_address; + change_dts.amount = found_money - needed_money; + } + + uint64_t dust = 0; + std::vector<cryptonote::tx_destination_entry> splitted_dsts; + destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, splitted_dsts, dust); + CHECK_AND_ASSERT_MES(dust <= dust_policy.dust_threshold, false, "internal error: invalid dust value"); + if (0 != dust && !dust_policy.add_to_fee) + { + splitted_dsts.push_back(cryptonote::tx_destination_entry(dust, dust_policy.addr_for_dust)); + } + + tfd.reason = transafer_fail_details::error_internal_error; + bool r = cryptonote::construct_tx(m_account.get_keys(), sources, splitted_dsts, tx, unlock_time); + CHECK_AND_ASSERT_MES(r, false, "Transaction construction failed"); + + //check transaction size + if(get_object_blobsize(tx) >= m_upper_transaction_size_limit) + { + LOG_PRINT_RED("Transaction size is too big: " << get_object_blobsize(tx) << ", expected size < " << m_upper_transaction_size_limit, LOG_LEVEL_2); + tfd.reason = transafer_fail_details::error_too_big_transaction; + tfd.tx_blob_size = get_object_blobsize(tx); + tfd.max_expected_tx_blob_size = m_upper_transaction_size_limit; + return false; + } + + + 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; + tfd.reason = transafer_fail_details::error_not_connected; + r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/sendrawtransaction", req, daemon_send_resp, m_http_client, 200000); + CHECK_AND_ASSERT_MES(r, false, "failed to send transaction"); + if(daemon_send_resp.status != CORE_RPC_STATUS_OK) + { + tfd.reason = transafer_fail_details::error_rejected_by_daemon; + LOG_ERROR("daemon failed to accept generated transaction, id: " << get_transaction_hash(tx) ); + return false; + } + std::string key_images; + std::for_each(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool + { + CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false); + key_images += boost::to_string(in.k_image) + " "; + return true; + }); + LOG_PRINT_L2("transaction " << get_transaction_hash(tx) << " generated ok and sent to daemon, key_images: [" << key_images << "]"); + + BOOST_FOREACH(transfer_container::iterator it, selected_transfers) + it->m_spent = true; + + LOG_PRINT_L0("Transaction successfully sent. <" << get_transaction_hash(tx) << ">" << ENDL + << "Commission: " << print_money(fee+dust) << "(dust: " << print_money(dust) << ")" << ENDL + << "Balance: " << print_money(balance()) << ENDL + << "Unlocked: " << print_money(unlocked_balance()) << ENDL + << "Please, wait for confirmation for your balance to be unlocked."); + + tfd.reason = transafer_fail_details::error_ok; + return true; + } +} |