forked from lthn/blockchain
Merge branch 'master' into offsig
# Conflicts: # src/currency_core/currency_format_utils.h # tests/unit_tests/pow_hash_test.cpp
This commit is contained in:
commit
b08905c1cc
155 changed files with 11122 additions and 1816 deletions
|
|
@ -157,7 +157,7 @@ endif()
|
|||
message(STATUS "Boost: ${Boost_VERSION} from ${Boost_LIBRARY_DIRS}")
|
||||
|
||||
|
||||
include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
|
||||
include_directories(SYSTEM ${Boost_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/contrib/ethereum/libethash)
|
||||
if(MINGW)
|
||||
set(Boost_LIBRARIES "${Boost_LIBRARIES};ws2_32;mswsock")
|
||||
elseif(NOT MSVC)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,11 @@ Building
|
|||
|
||||
Recommended OS version: Ubuntu 17.04 LTS.
|
||||
|
||||
1. `$ sudo apt install git g++ cmake unzip libicu-dev mesa-common-dev libglu1-mesa-dev qt5-default qtwebengine5-dev`
|
||||
1. For server version: \
|
||||
`$ sudo apt-get install -y build-essential g++ python-dev autotools-dev libicu-dev libbz2-dev cmake git libboost-all-dev screen`\
|
||||
For GUI version:\
|
||||
`$ sudo apt-get install -y build-essential g++ python-dev autotools-dev libicu-dev libbz2-dev cmake git libboost-all-dev screen mesa-common-dev libglu1-mesa-dev qt5-default qtwebengine5-dev`
|
||||
|
||||
2. `$ cd zano/ && make -j$(nproc) gui`
|
||||
3. Look for the binaries, including the `Zano` GUI, in the build directory
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ set(UPNPC_BUILD_TESTS OFF CACHE BOOL "Build test executables")
|
|||
add_subdirectory(miniupnpc)
|
||||
add_subdirectory(zlib)
|
||||
add_subdirectory(db)
|
||||
|
||||
add_subdirectory(ethereum)
|
||||
|
||||
|
||||
set_property(TARGET upnpc-static PROPERTY FOLDER "contrib")
|
||||
|
|
@ -13,6 +13,7 @@ set_property(TARGET lmdb PROPERTY FOLDER "contrib")
|
|||
|
||||
|
||||
|
||||
|
||||
if(MSVC)
|
||||
set_property(TARGET upnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -wd4244 -wd4267")
|
||||
else()
|
||||
|
|
|
|||
10
contrib/ethereum/CMakeLists.txt
Normal file
10
contrib/ethereum/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
add_subdirectory(libethash)
|
||||
|
||||
set_property(TARGET ethash PROPERTY FOLDER "contrib")
|
||||
|
||||
if(MSVC)
|
||||
target_compile_options(ethash PRIVATE /wd4477 /wd4267)
|
||||
else()
|
||||
target_compile_options(ethash PRIVATE -Wno-format -Wno-aggregate-return -Wno-empty-body)
|
||||
endif()
|
||||
|
||||
33
contrib/ethereum/libethash/CMakeLists.txt
Normal file
33
contrib/ethereum/libethash/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
# Copyright 2018 Pawel Bylica.
|
||||
# Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
|
||||
# set(include_dir ${PROJECT_SOURCE_DIR}/include)
|
||||
|
||||
|
||||
add_library(
|
||||
ethash
|
||||
bit_manipulation.h
|
||||
builtins.h
|
||||
endianness.hpp
|
||||
ethash/ethash.h
|
||||
ethash/ethash.hpp
|
||||
ethash-internal.hpp
|
||||
ethash.cpp
|
||||
ethash/hash_types.h
|
||||
managed.cpp
|
||||
ethash/keccak.h
|
||||
ethash/keccak.hpp
|
||||
keccak.c
|
||||
keccakf800.c
|
||||
keccakf1600.c
|
||||
kiss99.hpp
|
||||
primes.h
|
||||
primes.c
|
||||
ethash/progpow.hpp
|
||||
progpow.cpp
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
81
contrib/ethereum/libethash/bit_manipulation.h
Normal file
81
contrib/ethereum/libethash/bit_manipulation.h
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
* Copyright 2018 Pawel Bylica.
|
||||
* Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "builtins.h"
|
||||
#include "support/attributes.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static inline uint32_t rotl32(uint32_t n, unsigned int c)
|
||||
{
|
||||
const unsigned int mask = 31;
|
||||
|
||||
c &= mask;
|
||||
unsigned int neg_c = (unsigned int)(-(int)c);
|
||||
return (n << c) | (n >> (neg_c & mask));
|
||||
}
|
||||
|
||||
static inline uint32_t rotr32(uint32_t n, unsigned int c)
|
||||
{
|
||||
const unsigned int mask = 31;
|
||||
|
||||
c &= mask;
|
||||
unsigned int neg_c = (unsigned int)(-(int)c);
|
||||
return (n >> c) | (n << (neg_c & mask));
|
||||
}
|
||||
|
||||
static inline uint32_t clz32(uint32_t x)
|
||||
{
|
||||
return x ? (uint32_t)__builtin_clz(x) : 32;
|
||||
}
|
||||
|
||||
static inline uint32_t popcount32(uint32_t x)
|
||||
{
|
||||
return (uint32_t)__builtin_popcount(x);
|
||||
}
|
||||
|
||||
static inline uint32_t mul_hi32(uint32_t x, uint32_t y)
|
||||
{
|
||||
return (uint32_t)(((uint64_t)x * (uint64_t)y) >> 32);
|
||||
}
|
||||
|
||||
|
||||
/** FNV 32-bit prime. */
|
||||
static const uint32_t fnv_prime = 0x01000193;
|
||||
|
||||
/** FNV 32-bit offset basis. */
|
||||
static const uint32_t fnv_offset_basis = 0x811c9dc5;
|
||||
|
||||
/**
|
||||
* The implementation of FNV-1 hash.
|
||||
*
|
||||
* See https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1_hash.
|
||||
*/
|
||||
NO_SANITIZE("unsigned-integer-overflow")
|
||||
static inline uint32_t fnv1(uint32_t u, uint32_t v) noexcept
|
||||
{
|
||||
return (u * fnv_prime) ^ v;
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of FNV-1a hash.
|
||||
*
|
||||
* See https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash.
|
||||
*/
|
||||
NO_SANITIZE("unsigned-integer-overflow")
|
||||
static inline uint32_t fnv1a(uint32_t u, uint32_t v) noexcept
|
||||
{
|
||||
return (u ^ v) * fnv_prime;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
43
contrib/ethereum/libethash/builtins.h
Normal file
43
contrib/ethereum/libethash/builtins.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
* Copyright 2018 Pawel Bylica.
|
||||
* Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Implementation of GCC/clang builtins for MSVC compiler.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns the number of leading 0-bits in `x`, starting at the most significant bit position.
|
||||
* If `x` is 0, the result is undefined.
|
||||
*/
|
||||
static inline int __builtin_clz(unsigned int x)
|
||||
{
|
||||
unsigned long most_significant_bit;
|
||||
_BitScanReverse(&most_significant_bit, x);
|
||||
return 31 - (int)most_significant_bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of 1-bits in `x`.
|
||||
*/
|
||||
static inline int __builtin_popcount(unsigned int x)
|
||||
{
|
||||
return (int)__popcnt(x);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
98
contrib/ethereum/libethash/endianness.hpp
Normal file
98
contrib/ethereum/libethash/endianness.hpp
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
// Copyright 2018 Pawel Bylica.
|
||||
// Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
|
||||
/// @file
|
||||
/// This file contains helper functions to handle big-endian architectures.
|
||||
/// The Ethash algorithm is naturally defined for little-endian architectures
|
||||
/// so for those the helpers are just no-op empty functions.
|
||||
/// For big-endian architectures we need 32-bit and 64-bit byte swapping in
|
||||
/// some places.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ethash/ethash.hpp>
|
||||
|
||||
#if _WIN32
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define bswap32 _byteswap_ulong
|
||||
#define bswap64 _byteswap_uint64
|
||||
|
||||
// On Windows assume little endian.
|
||||
#define __LITTLE_ENDIAN 1234
|
||||
#define __BIG_ENDIAN 4321
|
||||
#define __BYTE_ORDER __LITTLE_ENDIAN
|
||||
|
||||
#elif __APPLE__
|
||||
|
||||
#include <machine/endian.h>
|
||||
|
||||
#define bswap32 __builtin_bswap32
|
||||
#define bswap64 __builtin_bswap64
|
||||
|
||||
#else
|
||||
|
||||
#include <endian.h>
|
||||
|
||||
#define bswap32 __builtin_bswap32
|
||||
#define bswap64 __builtin_bswap64
|
||||
|
||||
#endif
|
||||
|
||||
namespace ethash
|
||||
{
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
|
||||
struct le
|
||||
{
|
||||
static uint32_t uint32(uint32_t x) noexcept { return x; }
|
||||
static uint64_t uint64(uint64_t x) noexcept { return x; }
|
||||
|
||||
static const hash1024& uint32s(const hash1024& h) noexcept { return h; }
|
||||
static const hash512& uint32s(const hash512& h) noexcept { return h; }
|
||||
static const hash256& uint32s(const hash256& h) noexcept { return h; }
|
||||
};
|
||||
|
||||
struct be
|
||||
{
|
||||
static uint64_t uint64(uint64_t x) noexcept { return bswap64(x); }
|
||||
};
|
||||
|
||||
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
|
||||
struct le
|
||||
{
|
||||
static uint32_t uint32(uint32_t x) noexcept { return bswap32(x); }
|
||||
static uint64_t uint64(uint64_t x) noexcept { return bswap64(x); }
|
||||
|
||||
static hash1024 uint32s(hash1024 h) noexcept
|
||||
{
|
||||
for (auto& w : h.word32s)
|
||||
w = uint32(w);
|
||||
return h;
|
||||
}
|
||||
|
||||
static hash512 uint32s(hash512 h) noexcept
|
||||
{
|
||||
for (auto& w : h.word32s)
|
||||
w = uint32(w);
|
||||
return h;
|
||||
}
|
||||
|
||||
static hash256 uint32s(hash256 h) noexcept
|
||||
{
|
||||
for (auto& w : h.word32s)
|
||||
w = uint32(w);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
struct be
|
||||
{
|
||||
static uint64_t uint64(uint64_t x) noexcept { return x; }
|
||||
};
|
||||
|
||||
#endif
|
||||
} // namespace ethash
|
||||
69
contrib/ethereum/libethash/ethash-internal.hpp
Normal file
69
contrib/ethereum/libethash/ethash-internal.hpp
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
// ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
// Copyright 2018 Pawel Bylica.
|
||||
// Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
|
||||
/// @file
|
||||
/// Contains declarations of internal ethash functions to allow them to be
|
||||
/// unit-tested.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ethash/ethash.hpp>
|
||||
|
||||
#include "endianness.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
extern "C" struct ethash_epoch_context_full : ethash_epoch_context
|
||||
{
|
||||
ethash_hash1024* full_dataset;
|
||||
|
||||
constexpr ethash_epoch_context_full(int epoch_number, int light_cache_num_items,
|
||||
const ethash_hash512* light_cache, const uint32_t* l1_cache, int full_dataset_num_items,
|
||||
ethash_hash1024* full_dataset) noexcept
|
||||
: ethash_epoch_context{epoch_number, light_cache_num_items, light_cache, l1_cache,
|
||||
full_dataset_num_items},
|
||||
full_dataset{full_dataset}
|
||||
{}
|
||||
};
|
||||
|
||||
namespace ethash
|
||||
{
|
||||
inline bool is_less_or_equal(const hash256& a, const hash256& b) noexcept
|
||||
{
|
||||
for (size_t i = 0; i < (sizeof(a) / sizeof(a.word64s[0])); ++i)
|
||||
{
|
||||
if (be::uint64(a.word64s[i]) > be::uint64(b.word64s[i]))
|
||||
return false;
|
||||
if (be::uint64(a.word64s[i]) < be::uint64(b.word64s[i]))
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool is_equal(const hash256& a, const hash256& b) noexcept
|
||||
{
|
||||
return std::memcmp(a.bytes, b.bytes, sizeof(a)) == 0;
|
||||
}
|
||||
|
||||
void build_light_cache(hash512 cache[], int num_items, const hash256& seed) noexcept;
|
||||
|
||||
hash512 calculate_dataset_item_512(const epoch_context& context, int64_t index) noexcept;
|
||||
hash1024 calculate_dataset_item_1024(const epoch_context& context, uint32_t index) noexcept;
|
||||
hash2048 calculate_dataset_item_2048(const epoch_context& context, uint32_t index) noexcept;
|
||||
|
||||
namespace generic
|
||||
{
|
||||
using hash_fn_512 = hash512 (*)(const uint8_t* data, size_t size);
|
||||
using build_light_cache_fn = void (*)(hash512 cache[], int num_items, const hash256& seed);
|
||||
|
||||
void build_light_cache(
|
||||
hash_fn_512 hash_fn, hash512 cache[], int num_items, const hash256& seed) noexcept;
|
||||
|
||||
epoch_context_full* create_epoch_context(
|
||||
build_light_cache_fn build_fn, int epoch_number, bool full) noexcept;
|
||||
|
||||
} // namespace generic
|
||||
|
||||
} // namespace ethash
|
||||
441
contrib/ethereum/libethash/ethash.cpp
Normal file
441
contrib/ethereum/libethash/ethash.cpp
Normal file
|
|
@ -0,0 +1,441 @@
|
|||
// ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
// Copyright 2018 Pawel Bylica.
|
||||
// Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
|
||||
#include "ethash-internal.hpp"
|
||||
|
||||
#include "bit_manipulation.h"
|
||||
#include "endianness.hpp"
|
||||
#include "primes.h"
|
||||
#include "support/attributes.h"
|
||||
#include <ethash/keccak.hpp>
|
||||
#include <ethash/progpow.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
|
||||
namespace ethash
|
||||
{
|
||||
// Internal constants:
|
||||
constexpr static int light_cache_init_size = 1 << 24;
|
||||
constexpr static int light_cache_growth = 1 << 17;
|
||||
constexpr static int light_cache_rounds = 3;
|
||||
constexpr static int full_dataset_init_size = 1 << 30;
|
||||
constexpr static int full_dataset_growth = 1 << 23;
|
||||
constexpr static int full_dataset_item_parents = 256;
|
||||
|
||||
// Verify constants:
|
||||
static_assert(sizeof(hash512) == ETHASH_LIGHT_CACHE_ITEM_SIZE, "");
|
||||
static_assert(sizeof(hash1024) == ETHASH_FULL_DATASET_ITEM_SIZE, "");
|
||||
static_assert(light_cache_item_size == ETHASH_LIGHT_CACHE_ITEM_SIZE, "");
|
||||
static_assert(full_dataset_item_size == ETHASH_FULL_DATASET_ITEM_SIZE, "");
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
using ::fnv1;
|
||||
|
||||
inline hash512 fnv1(const hash512& u, const hash512& v) noexcept
|
||||
{
|
||||
hash512 r;
|
||||
for (size_t i = 0; i < sizeof(r) / sizeof(r.word32s[0]); ++i)
|
||||
r.word32s[i] = fnv1(u.word32s[i], v.word32s[i]);
|
||||
return r;
|
||||
}
|
||||
|
||||
inline hash512 bitwise_xor(const hash512& x, const hash512& y) noexcept
|
||||
{
|
||||
hash512 z;
|
||||
for (size_t i = 0; i < sizeof(z) / sizeof(z.word64s[0]); ++i)
|
||||
z.word64s[i] = x.word64s[i] ^ y.word64s[i];
|
||||
return z;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int find_epoch_number(const hash256& seed) noexcept
|
||||
{
|
||||
static constexpr int num_tries = 30000; // Divisible by 16.
|
||||
|
||||
// Thread-local cache of the last search.
|
||||
static thread_local int cached_epoch_number = 0;
|
||||
static thread_local hash256 cached_seed = {};
|
||||
|
||||
// Load from memory once (memory will be clobbered by keccak256()).
|
||||
const uint32_t seed_part = seed.word32s[0];
|
||||
const int e = cached_epoch_number;
|
||||
hash256 s = cached_seed;
|
||||
|
||||
if (s.word32s[0] == seed_part)
|
||||
return e;
|
||||
|
||||
// Try the next seed, will match for sequential epoch access.
|
||||
s = keccak256(s);
|
||||
if (s.word32s[0] == seed_part)
|
||||
{
|
||||
cached_seed = s;
|
||||
cached_epoch_number = e + 1;
|
||||
return e + 1;
|
||||
}
|
||||
|
||||
// Search for matching seed starting from epoch 0.
|
||||
s = {};
|
||||
for (int i = 0; i < num_tries; ++i)
|
||||
{
|
||||
if (s.word32s[0] == seed_part)
|
||||
{
|
||||
cached_seed = s;
|
||||
cached_epoch_number = i;
|
||||
return i;
|
||||
}
|
||||
|
||||
s = keccak256(s);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
namespace generic
|
||||
{
|
||||
void build_light_cache(
|
||||
hash_fn_512 hash_fn, hash512 cache[], int num_items, const hash256& seed) noexcept
|
||||
{
|
||||
hash512 item = hash_fn(seed.bytes, sizeof(seed));
|
||||
cache[0] = item;
|
||||
for (int i = 1; i < num_items; ++i)
|
||||
{
|
||||
item = hash_fn(item.bytes, sizeof(item));
|
||||
cache[i] = item;
|
||||
}
|
||||
|
||||
for (int q = 0; q < light_cache_rounds; ++q)
|
||||
{
|
||||
for (int i = 0; i < num_items; ++i)
|
||||
{
|
||||
const uint32_t index_limit = static_cast<uint32_t>(num_items);
|
||||
|
||||
// Fist index: 4 first bytes of the item as little-endian integer.
|
||||
const uint32_t t = le::uint32(cache[i].word32s[0]);
|
||||
const uint32_t v = t % index_limit;
|
||||
|
||||
// Second index.
|
||||
const uint32_t w = static_cast<uint32_t>(num_items + (i - 1)) % index_limit;
|
||||
|
||||
const hash512 x = bitwise_xor(cache[v], cache[w]);
|
||||
cache[i] = hash_fn(x.bytes, sizeof(x));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
epoch_context_full* create_epoch_context(
|
||||
build_light_cache_fn build_fn, int epoch_number, bool full) noexcept
|
||||
{
|
||||
static_assert(sizeof(epoch_context_full) < sizeof(hash512), "epoch_context too big");
|
||||
static constexpr size_t context_alloc_size = sizeof(hash512);
|
||||
|
||||
const int light_cache_num_items = calculate_light_cache_num_items(epoch_number);
|
||||
const int full_dataset_num_items = calculate_full_dataset_num_items(epoch_number);
|
||||
const size_t light_cache_size = get_light_cache_size(light_cache_num_items);
|
||||
const size_t full_dataset_size =
|
||||
full ? static_cast<size_t>(full_dataset_num_items) * sizeof(hash1024) :
|
||||
progpow::l1_cache_size;
|
||||
|
||||
const size_t alloc_size = context_alloc_size + light_cache_size + full_dataset_size;
|
||||
|
||||
char* const alloc_data = static_cast<char*>(std::calloc(1, alloc_size));
|
||||
if (!alloc_data)
|
||||
return nullptr; // Signal out-of-memory by returning null pointer.
|
||||
|
||||
hash512* const light_cache = reinterpret_cast<hash512*>(alloc_data + context_alloc_size);
|
||||
const hash256 epoch_seed = calculate_epoch_seed(epoch_number);
|
||||
build_fn(light_cache, light_cache_num_items, epoch_seed);
|
||||
|
||||
uint32_t* const l1_cache =
|
||||
reinterpret_cast<uint32_t*>(alloc_data + context_alloc_size + light_cache_size);
|
||||
|
||||
hash1024* full_dataset = full ? reinterpret_cast<hash1024*>(l1_cache) : nullptr;
|
||||
|
||||
epoch_context_full* const context = new (alloc_data) epoch_context_full{
|
||||
epoch_number,
|
||||
light_cache_num_items,
|
||||
light_cache,
|
||||
l1_cache,
|
||||
full_dataset_num_items,
|
||||
full_dataset,
|
||||
};
|
||||
|
||||
auto* full_dataset_2048 = reinterpret_cast<hash2048*>(l1_cache);
|
||||
for (uint32_t i = 0; i < progpow::l1_cache_size / sizeof(full_dataset_2048[0]); ++i)
|
||||
full_dataset_2048[i] = calculate_dataset_item_2048(*context, i);
|
||||
return context;
|
||||
}
|
||||
} // namespace generic
|
||||
|
||||
void build_light_cache(hash512 cache[], int num_items, const hash256& seed) noexcept
|
||||
{
|
||||
return generic::build_light_cache(keccak512, cache, num_items, seed);
|
||||
}
|
||||
|
||||
struct item_state
|
||||
{
|
||||
const hash512* const cache;
|
||||
const int64_t num_cache_items;
|
||||
const uint32_t seed;
|
||||
|
||||
hash512 mix;
|
||||
|
||||
ALWAYS_INLINE item_state(const epoch_context& context, int64_t index) noexcept
|
||||
: cache{context.light_cache},
|
||||
num_cache_items{context.light_cache_num_items},
|
||||
seed{static_cast<uint32_t>(index)}
|
||||
{
|
||||
mix = cache[index % num_cache_items];
|
||||
mix.word32s[0] ^= le::uint32(seed);
|
||||
mix = le::uint32s(keccak512(mix));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void update(uint32_t round) noexcept
|
||||
{
|
||||
static constexpr size_t num_words = sizeof(mix) / sizeof(uint32_t);
|
||||
const uint32_t t = fnv1(seed ^ round, mix.word32s[round % num_words]);
|
||||
const int64_t parent_index = t % num_cache_items;
|
||||
mix = fnv1(mix, le::uint32s(cache[parent_index]));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE hash512 final() noexcept { return keccak512(le::uint32s(mix)); }
|
||||
};
|
||||
|
||||
hash512 calculate_dataset_item_512(const epoch_context& context, int64_t index) noexcept
|
||||
{
|
||||
item_state item0{context, index};
|
||||
for (uint32_t j = 0; j < full_dataset_item_parents; ++j)
|
||||
item0.update(j);
|
||||
return item0.final();
|
||||
}
|
||||
|
||||
/// Calculates a full dataset item
|
||||
///
|
||||
/// This consist of two 512-bit items produced by calculate_dataset_item_partial().
|
||||
/// Here the computation is done interleaved for better performance.
|
||||
hash1024 calculate_dataset_item_1024(const epoch_context& context, uint32_t index) noexcept
|
||||
{
|
||||
item_state item0{context, int64_t(index) * 2};
|
||||
item_state item1{context, int64_t(index) * 2 + 1};
|
||||
|
||||
for (uint32_t j = 0; j < full_dataset_item_parents; ++j)
|
||||
{
|
||||
item0.update(j);
|
||||
item1.update(j);
|
||||
}
|
||||
|
||||
return hash1024{{item0.final(), item1.final()}};
|
||||
}
|
||||
|
||||
hash2048 calculate_dataset_item_2048(const epoch_context& context, uint32_t index) noexcept
|
||||
{
|
||||
item_state item0{context, int64_t(index) * 4};
|
||||
item_state item1{context, int64_t(index) * 4 + 1};
|
||||
item_state item2{context, int64_t(index) * 4 + 2};
|
||||
item_state item3{context, int64_t(index) * 4 + 3};
|
||||
|
||||
for (uint32_t j = 0; j < full_dataset_item_parents; ++j)
|
||||
{
|
||||
item0.update(j);
|
||||
item1.update(j);
|
||||
item2.update(j);
|
||||
item3.update(j);
|
||||
}
|
||||
|
||||
return hash2048{{item0.final(), item1.final(), item2.final(), item3.final()}};
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
using lookup_fn = hash1024 (*)(const epoch_context&, uint32_t);
|
||||
|
||||
inline hash512 hash_seed(const hash256& header_hash, uint64_t nonce) noexcept
|
||||
{
|
||||
nonce = le::uint64(nonce);
|
||||
uint8_t init_data[sizeof(header_hash) + sizeof(nonce)];
|
||||
std::memcpy(&init_data[0], &header_hash, sizeof(header_hash));
|
||||
std::memcpy(&init_data[sizeof(header_hash)], &nonce, sizeof(nonce));
|
||||
|
||||
return keccak512(init_data, sizeof(init_data));
|
||||
}
|
||||
|
||||
inline hash256 hash_final(const hash512& seed, const hash256& mix_hash)
|
||||
{
|
||||
uint8_t final_data[sizeof(seed) + sizeof(mix_hash)];
|
||||
std::memcpy(&final_data[0], seed.bytes, sizeof(seed));
|
||||
std::memcpy(&final_data[sizeof(seed)], mix_hash.bytes, sizeof(mix_hash));
|
||||
return keccak256(final_data, sizeof(final_data));
|
||||
}
|
||||
|
||||
inline hash256 hash_kernel(
|
||||
const epoch_context& context, const hash512& seed, lookup_fn lookup) noexcept
|
||||
{
|
||||
static constexpr size_t num_words = sizeof(hash1024) / sizeof(uint32_t);
|
||||
const uint32_t index_limit = static_cast<uint32_t>(context.full_dataset_num_items);
|
||||
const uint32_t seed_init = le::uint32(seed.word32s[0]);
|
||||
|
||||
hash1024 mix{{le::uint32s(seed), le::uint32s(seed)}};
|
||||
|
||||
for (uint32_t i = 0; i < num_dataset_accesses; ++i)
|
||||
{
|
||||
const uint32_t p = fnv1(i ^ seed_init, mix.word32s[i % num_words]) % index_limit;
|
||||
const hash1024 newdata = le::uint32s(lookup(context, p));
|
||||
|
||||
for (size_t j = 0; j < num_words; ++j)
|
||||
mix.word32s[j] = fnv1(mix.word32s[j], newdata.word32s[j]);
|
||||
}
|
||||
|
||||
hash256 mix_hash;
|
||||
for (size_t i = 0; i < num_words; i += 4)
|
||||
{
|
||||
const uint32_t h1 = fnv1(mix.word32s[i], mix.word32s[i + 1]);
|
||||
const uint32_t h2 = fnv1(h1, mix.word32s[i + 2]);
|
||||
const uint32_t h3 = fnv1(h2, mix.word32s[i + 3]);
|
||||
mix_hash.word32s[i / 4] = h3;
|
||||
}
|
||||
|
||||
return le::uint32s(mix_hash);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
result hash(const epoch_context& context, const hash256& header_hash, uint64_t nonce) noexcept
|
||||
{
|
||||
const hash512 seed = hash_seed(header_hash, nonce);
|
||||
const hash256 mix_hash = hash_kernel(context, seed, calculate_dataset_item_1024);
|
||||
return {hash_final(seed, mix_hash), mix_hash};
|
||||
}
|
||||
|
||||
result hash(const epoch_context_full& context, const hash256& header_hash, uint64_t nonce) noexcept
|
||||
{
|
||||
static const auto lazy_lookup = [](const epoch_context& context, uint32_t index) noexcept
|
||||
{
|
||||
auto full_dataset = static_cast<const epoch_context_full&>(context).full_dataset;
|
||||
hash1024& item = full_dataset[index];
|
||||
if (item.word64s[0] == 0)
|
||||
{
|
||||
// TODO: Copy elision here makes it thread-safe?
|
||||
item = calculate_dataset_item_1024(context, index);
|
||||
}
|
||||
|
||||
return item;
|
||||
};
|
||||
|
||||
const hash512 seed = hash_seed(header_hash, nonce);
|
||||
const hash256 mix_hash = hash_kernel(context, seed, lazy_lookup);
|
||||
return {hash_final(seed, mix_hash), mix_hash};
|
||||
}
|
||||
|
||||
bool verify_final_hash(const hash256& header_hash, const hash256& mix_hash, uint64_t nonce,
|
||||
const hash256& boundary) noexcept
|
||||
{
|
||||
const hash512 seed = hash_seed(header_hash, nonce);
|
||||
return is_less_or_equal(hash_final(seed, mix_hash), boundary);
|
||||
}
|
||||
|
||||
bool verify(const epoch_context& context, const hash256& header_hash, const hash256& mix_hash,
|
||||
uint64_t nonce, const hash256& boundary) noexcept
|
||||
{
|
||||
const hash512 seed = hash_seed(header_hash, nonce);
|
||||
if (!is_less_or_equal(hash_final(seed, mix_hash), boundary))
|
||||
return false;
|
||||
|
||||
const hash256 expected_mix_hash = hash_kernel(context, seed, calculate_dataset_item_1024);
|
||||
return is_equal(expected_mix_hash, mix_hash);
|
||||
}
|
||||
|
||||
search_result search_light(const epoch_context& context, const hash256& header_hash,
|
||||
const hash256& boundary, uint64_t start_nonce, size_t iterations) noexcept
|
||||
{
|
||||
const uint64_t end_nonce = start_nonce + iterations;
|
||||
for (uint64_t nonce = start_nonce; nonce < end_nonce; ++nonce)
|
||||
{
|
||||
result r = hash(context, header_hash, nonce);
|
||||
if (is_less_or_equal(r.final_hash, boundary))
|
||||
return {r, nonce};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
search_result search(const epoch_context_full& context, const hash256& header_hash,
|
||||
const hash256& boundary, uint64_t start_nonce, size_t iterations) noexcept
|
||||
{
|
||||
const uint64_t end_nonce = start_nonce + iterations;
|
||||
for (uint64_t nonce = start_nonce; nonce < end_nonce; ++nonce)
|
||||
{
|
||||
result r = hash(context, header_hash, nonce);
|
||||
if (is_less_or_equal(r.final_hash, boundary))
|
||||
return {r, nonce};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
} // namespace ethash
|
||||
|
||||
using namespace ethash;
|
||||
|
||||
extern "C" {
|
||||
|
||||
ethash_hash256 ethash_calculate_epoch_seed(int epoch_number) noexcept
|
||||
{
|
||||
ethash_hash256 epoch_seed = {};
|
||||
for (int i = 0; i < epoch_number; ++i)
|
||||
epoch_seed = ethash_keccak256_32(epoch_seed.bytes);
|
||||
return epoch_seed;
|
||||
}
|
||||
|
||||
int ethash_calculate_light_cache_num_items(int epoch_number) noexcept
|
||||
{
|
||||
static constexpr int item_size = sizeof(hash512);
|
||||
static constexpr int num_items_init = light_cache_init_size / item_size;
|
||||
static constexpr int num_items_growth = light_cache_growth / item_size;
|
||||
static_assert(
|
||||
light_cache_init_size % item_size == 0, "light_cache_init_size not multiple of item size");
|
||||
static_assert(
|
||||
light_cache_growth % item_size == 0, "light_cache_growth not multiple of item size");
|
||||
|
||||
int num_items_upper_bound = num_items_init + epoch_number * num_items_growth;
|
||||
int num_items = ethash_find_largest_prime(num_items_upper_bound);
|
||||
return num_items;
|
||||
}
|
||||
|
||||
int ethash_calculate_full_dataset_num_items(int epoch_number) noexcept
|
||||
{
|
||||
static constexpr int item_size = sizeof(hash1024);
|
||||
static constexpr int num_items_init = full_dataset_init_size / item_size;
|
||||
static constexpr int num_items_growth = full_dataset_growth / item_size;
|
||||
static_assert(full_dataset_init_size % item_size == 0,
|
||||
"full_dataset_init_size not multiple of item size");
|
||||
static_assert(
|
||||
full_dataset_growth % item_size == 0, "full_dataset_growth not multiple of item size");
|
||||
|
||||
int num_items_upper_bound = num_items_init + epoch_number * num_items_growth;
|
||||
int num_items = ethash_find_largest_prime(num_items_upper_bound);
|
||||
return num_items;
|
||||
}
|
||||
|
||||
epoch_context* ethash_create_epoch_context(int epoch_number) noexcept
|
||||
{
|
||||
return generic::create_epoch_context(build_light_cache, epoch_number, false);
|
||||
}
|
||||
|
||||
epoch_context_full* ethash_create_epoch_context_full(int epoch_number) noexcept
|
||||
{
|
||||
return generic::create_epoch_context(build_light_cache, epoch_number, true);
|
||||
}
|
||||
|
||||
void ethash_destroy_epoch_context_full(epoch_context_full* context) noexcept
|
||||
{
|
||||
ethash_destroy_epoch_context(context);
|
||||
}
|
||||
|
||||
void ethash_destroy_epoch_context(epoch_context* context) noexcept
|
||||
{
|
||||
context->~epoch_context();
|
||||
std::free(context);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
99
contrib/ethereum/libethash/ethash/ethash.h
Normal file
99
contrib/ethereum/libethash/ethash/ethash.h
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
* Copyright 2018 Pawel Bylica.
|
||||
* Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ethash/hash_types.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define NOEXCEPT noexcept
|
||||
#else
|
||||
#define NOEXCEPT
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The Ethash algorithm revision implemented as specified in the Ethash spec
|
||||
* https://github.com/ethereum/wiki/wiki/Ethash.
|
||||
*/
|
||||
#define ETHASH_REVISION "23"
|
||||
|
||||
#define ETHASH_EPOCH_LENGTH 30000
|
||||
#define ETHASH_LIGHT_CACHE_ITEM_SIZE 64
|
||||
#define ETHASH_FULL_DATASET_ITEM_SIZE 128
|
||||
#define ETHASH_NUM_DATASET_ACCESSES 64
|
||||
|
||||
|
||||
struct ethash_epoch_context
|
||||
{
|
||||
const int epoch_number;
|
||||
const int light_cache_num_items;
|
||||
const union ethash_hash512* const light_cache;
|
||||
const uint32_t* const l1_cache;
|
||||
const int full_dataset_num_items;
|
||||
};
|
||||
|
||||
|
||||
struct ethash_epoch_context_full;
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the number of items in the light cache for given epoch.
|
||||
*
|
||||
* This function will search for a prime number matching the criteria given
|
||||
* by the Ethash so the execution time is not constant. It takes ~ 0.01 ms.
|
||||
*
|
||||
* @param epoch_number The epoch number.
|
||||
* @return The number items in the light cache.
|
||||
*/
|
||||
int ethash_calculate_light_cache_num_items(int epoch_number) NOEXCEPT;
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the number of items in the full dataset for given epoch.
|
||||
*
|
||||
* This function will search for a prime number matching the criteria given
|
||||
* by the Ethash so the execution time is not constant. It takes ~ 0.05 ms.
|
||||
*
|
||||
* @param epoch_number The epoch number.
|
||||
* @return The number items in the full dataset.
|
||||
*/
|
||||
int ethash_calculate_full_dataset_num_items(int epoch_number) NOEXCEPT;
|
||||
|
||||
/**
|
||||
* Calculates the epoch seed hash.
|
||||
* @param epoch_number The epoch number.
|
||||
* @return The epoch seed hash.
|
||||
*/
|
||||
union ethash_hash256 ethash_calculate_epoch_seed(int epoch_number) NOEXCEPT;
|
||||
|
||||
|
||||
struct ethash_epoch_context* ethash_create_epoch_context(int epoch_number) NOEXCEPT;
|
||||
|
||||
/**
|
||||
* Creates the epoch context with the full dataset initialized.
|
||||
*
|
||||
* The memory for the full dataset is only allocated and marked as "not-generated".
|
||||
* The items of the full dataset are generated on the fly when hit for the first time.
|
||||
*
|
||||
* The memory allocated in the context MUST be freed with ethash_destroy_epoch_context_full().
|
||||
*
|
||||
* @param epoch_number The epoch number.
|
||||
* @return Pointer to the context or null in case of memory allocation failure.
|
||||
*/
|
||||
struct ethash_epoch_context_full* ethash_create_epoch_context_full(int epoch_number) NOEXCEPT;
|
||||
|
||||
void ethash_destroy_epoch_context(struct ethash_epoch_context* context) NOEXCEPT;
|
||||
|
||||
void ethash_destroy_epoch_context_full(struct ethash_epoch_context_full* context) NOEXCEPT;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
160
contrib/ethereum/libethash/ethash/ethash.hpp
Normal file
160
contrib/ethereum/libethash/ethash/ethash.hpp
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
// ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
// Copyright 2018 Pawel Bylica.
|
||||
// Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
|
||||
/// @file
|
||||
///
|
||||
/// API design decisions:
|
||||
///
|
||||
/// 1. Signed integer type is used whenever the size of the type is not
|
||||
/// restricted by the Ethash specification.
|
||||
/// See http://www.aristeia.com/Papers/C++ReportColumns/sep95.pdf.
|
||||
/// See https://stackoverflow.com/questions/10168079/why-is-size-t-unsigned/.
|
||||
/// See https://github.com/Microsoft/GSL/issues/171.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ethash/ethash.h>
|
||||
#include <ethash/hash_types.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
namespace ethash
|
||||
{
|
||||
constexpr auto revision = ETHASH_REVISION;
|
||||
|
||||
static constexpr int epoch_length = ETHASH_EPOCH_LENGTH;
|
||||
static constexpr int light_cache_item_size = ETHASH_LIGHT_CACHE_ITEM_SIZE;
|
||||
static constexpr int full_dataset_item_size = ETHASH_FULL_DATASET_ITEM_SIZE;
|
||||
static constexpr int num_dataset_accesses = ETHASH_NUM_DATASET_ACCESSES;
|
||||
|
||||
using epoch_context = ethash_epoch_context;
|
||||
using epoch_context_full = ethash_epoch_context_full;
|
||||
|
||||
/// Constructs a 256-bit hash from an array of bytes.
|
||||
///
|
||||
/// @param bytes A pointer to array of at least 32 bytes.
|
||||
/// @return The constructed hash.
|
||||
inline hash256 hash256_from_bytes(const uint8_t bytes[32]) noexcept
|
||||
{
|
||||
hash256 h;
|
||||
std::memcpy(&h, bytes, sizeof(h));
|
||||
return h;
|
||||
}
|
||||
|
||||
struct result
|
||||
{
|
||||
hash256 final_hash;
|
||||
hash256 mix_hash;
|
||||
};
|
||||
|
||||
struct search_result
|
||||
{
|
||||
bool solution_found = false;
|
||||
uint64_t nonce = 0;
|
||||
hash256 final_hash = {};
|
||||
hash256 mix_hash = {};
|
||||
|
||||
search_result() noexcept = default;
|
||||
|
||||
search_result(result res, uint64_t nonce) noexcept
|
||||
: solution_found(true), nonce(nonce), final_hash(res.final_hash), mix_hash(res.mix_hash)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
/// Alias for ethash_calculate_light_cache_num_items().
|
||||
static constexpr auto calculate_light_cache_num_items = ethash_calculate_light_cache_num_items;
|
||||
|
||||
/// Alias for ethash_calculate_full_dataset_num_items().
|
||||
static constexpr auto calculate_full_dataset_num_items = ethash_calculate_full_dataset_num_items;
|
||||
|
||||
/// Alias for ethash_calculate_epoch_seed().
|
||||
static constexpr auto calculate_epoch_seed = ethash_calculate_epoch_seed;
|
||||
|
||||
|
||||
/// Calculates the epoch number out of the block number.
|
||||
inline constexpr int get_epoch_number(int block_number) noexcept
|
||||
{
|
||||
return block_number / epoch_length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Coverts the number of items of a light cache to size in bytes.
|
||||
*
|
||||
* @param num_items The number of items in the light cache.
|
||||
* @return The size of the light cache in bytes.
|
||||
*/
|
||||
inline constexpr size_t get_light_cache_size(int num_items) noexcept
|
||||
{
|
||||
return static_cast<size_t>(num_items) * light_cache_item_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Coverts the number of items of a full dataset to size in bytes.
|
||||
*
|
||||
* @param num_items The number of items in the full dataset.
|
||||
* @return The size of the full dataset in bytes.
|
||||
*/
|
||||
inline constexpr uint64_t get_full_dataset_size(int num_items) noexcept
|
||||
{
|
||||
return static_cast<uint64_t>(num_items) * full_dataset_item_size;
|
||||
}
|
||||
|
||||
/// Owned unique pointer to an epoch context.
|
||||
using epoch_context_ptr = std::unique_ptr<epoch_context, decltype(ðash_destroy_epoch_context)>;
|
||||
|
||||
using epoch_context_full_ptr =
|
||||
std::unique_ptr<epoch_context_full, decltype(ðash_destroy_epoch_context_full)>;
|
||||
|
||||
/// Creates Ethash epoch context.
|
||||
///
|
||||
/// This is a wrapper for ethash_create_epoch_number C function that returns
|
||||
/// the context as a smart pointer which handles the destruction of the context.
|
||||
inline epoch_context_ptr create_epoch_context(int epoch_number) noexcept
|
||||
{
|
||||
return {ethash_create_epoch_context(epoch_number), ethash_destroy_epoch_context};
|
||||
}
|
||||
|
||||
inline epoch_context_full_ptr create_epoch_context_full(int epoch_number) noexcept
|
||||
{
|
||||
return {ethash_create_epoch_context_full(epoch_number), ethash_destroy_epoch_context_full};
|
||||
}
|
||||
|
||||
|
||||
result hash(const epoch_context& context, const hash256& header_hash, uint64_t nonce) noexcept;
|
||||
|
||||
result hash(const epoch_context_full& context, const hash256& header_hash, uint64_t nonce) noexcept;
|
||||
|
||||
bool verify_final_hash(const hash256& header_hash, const hash256& mix_hash, uint64_t nonce,
|
||||
const hash256& boundary) noexcept;
|
||||
|
||||
bool verify(const epoch_context& context, const hash256& header_hash, const hash256& mix_hash,
|
||||
uint64_t nonce, const hash256& boundary) noexcept;
|
||||
|
||||
search_result search_light(const epoch_context& context, const hash256& header_hash,
|
||||
const hash256& boundary, uint64_t start_nonce, size_t iterations) noexcept;
|
||||
|
||||
search_result search(const epoch_context_full& context, const hash256& header_hash,
|
||||
const hash256& boundary, uint64_t start_nonce, size_t iterations) noexcept;
|
||||
|
||||
|
||||
/// Tries to find the epoch number matching the given seed hash.
|
||||
///
|
||||
/// Mining pool protocols (many variants of stratum and "getwork") send out
|
||||
/// seed hash instead of epoch number to workers. This function tries to recover
|
||||
/// the epoch number from this seed hash.
|
||||
///
|
||||
/// @param seed Ethash seed hash.
|
||||
/// @return The epoch number or -1 if not found.
|
||||
int find_epoch_number(const hash256& seed) noexcept;
|
||||
|
||||
|
||||
/// Get global shared epoch context.
|
||||
const epoch_context& get_global_epoch_context(int epoch_number);
|
||||
|
||||
/// Get global shared epoch context with full dataset initialized.
|
||||
const epoch_context_full& get_global_epoch_context_full(int epoch_number);
|
||||
} // namespace ethash
|
||||
46
contrib/ethereum/libethash/ethash/hash_types.h
Normal file
46
contrib/ethereum/libethash/ethash/hash_types.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
* Copyright 2018 Pawel Bylica.
|
||||
* Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
union ethash_hash256
|
||||
{
|
||||
uint64_t word64s[4];
|
||||
uint32_t word32s[8];
|
||||
uint8_t bytes[32];
|
||||
};
|
||||
|
||||
union ethash_hash512
|
||||
{
|
||||
uint64_t word64s[8];
|
||||
uint32_t word32s[16];
|
||||
uint8_t bytes[64];
|
||||
};
|
||||
|
||||
union ethash_hash1024
|
||||
{
|
||||
union ethash_hash512 hash512s[2];
|
||||
uint64_t word64s[16];
|
||||
uint32_t word32s[32];
|
||||
uint8_t bytes[128];
|
||||
};
|
||||
|
||||
union ethash_hash2048
|
||||
{
|
||||
union ethash_hash512 hash512s[4];
|
||||
uint64_t word64s[32];
|
||||
uint32_t word32s[64];
|
||||
uint8_t bytes[256];
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
15
contrib/ethereum/libethash/ethash/hash_types.hpp
Normal file
15
contrib/ethereum/libethash/ethash/hash_types.hpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
// ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
// Copyright 2018 Pawel Bylica.
|
||||
// Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ethash/hash_types.h>
|
||||
|
||||
namespace ethash
|
||||
{
|
||||
using hash256 = ethash_hash256;
|
||||
using hash512 = ethash_hash512;
|
||||
using hash1024 = ethash_hash1024;
|
||||
using hash2048 = ethash_hash2048;
|
||||
} // namespace ethash
|
||||
49
contrib/ethereum/libethash/ethash/keccak.h
Normal file
49
contrib/ethereum/libethash/ethash/keccak.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
* Copyright 2018 Pawel Bylica.
|
||||
* Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ethash/hash_types.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define NOEXCEPT noexcept
|
||||
#else
|
||||
#define NOEXCEPT
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The Keccak-f[1600] function.
|
||||
*
|
||||
* The implementation of the Keccak-f function with 1600-bit width of the permutation (b).
|
||||
* The size of the state is also 1600 bit what gives 25 64-bit words.
|
||||
*
|
||||
* @param state The state of 25 64-bit words on which the permutation is to be performed.
|
||||
*/
|
||||
void ethash_keccakf1600(uint64_t state[25]) NOEXCEPT;
|
||||
|
||||
/**
|
||||
* The Keccak-f[800] function.
|
||||
*
|
||||
* The implementation of the Keccak-f function with 800-bit width of the permutation (b).
|
||||
* The size of the state is also 800 bit what gives 25 32-bit words.
|
||||
*
|
||||
* @param state The state of 25 32-bit words on which the permutation is to be performed.
|
||||
*/
|
||||
void ethash_keccakf800(uint32_t state[25]) NOEXCEPT;
|
||||
|
||||
union ethash_hash256 ethash_keccak256(const uint8_t* data, size_t size) NOEXCEPT;
|
||||
union ethash_hash256 ethash_keccak256_32(const uint8_t data[32]) NOEXCEPT;
|
||||
union ethash_hash512 ethash_keccak512(const uint8_t* data, size_t size) NOEXCEPT;
|
||||
union ethash_hash512 ethash_keccak512_64(const uint8_t data[64]) NOEXCEPT;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
35
contrib/ethereum/libethash/ethash/keccak.hpp
Normal file
35
contrib/ethereum/libethash/ethash/keccak.hpp
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
// Copyright 2018 Pawel Bylica.
|
||||
// Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ethash/keccak.h>
|
||||
#include <ethash/hash_types.hpp>
|
||||
|
||||
namespace ethash
|
||||
{
|
||||
inline hash256 keccak256(const uint8_t* data, size_t size) noexcept
|
||||
{
|
||||
return ethash_keccak256(data, size);
|
||||
}
|
||||
|
||||
inline hash256 keccak256(const hash256& input) noexcept
|
||||
{
|
||||
return ethash_keccak256_32(input.bytes);
|
||||
}
|
||||
|
||||
inline hash512 keccak512(const uint8_t* data, size_t size) noexcept
|
||||
{
|
||||
return ethash_keccak512(data, size);
|
||||
}
|
||||
|
||||
inline hash512 keccak512(const hash512& input) noexcept
|
||||
{
|
||||
return ethash_keccak512_64(input.bytes);
|
||||
}
|
||||
|
||||
static constexpr auto keccak256_32 = ethash_keccak256_32;
|
||||
static constexpr auto keccak512_64 = ethash_keccak512_64;
|
||||
|
||||
} // namespace ethash
|
||||
47
contrib/ethereum/libethash/ethash/progpow.hpp
Normal file
47
contrib/ethereum/libethash/ethash/progpow.hpp
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
// Copyright 2018 Pawel Bylica.
|
||||
// Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
|
||||
/// @file
|
||||
///
|
||||
/// ProgPoW API
|
||||
///
|
||||
/// This file provides the public API for ProgPoW as the Ethash API extension.
|
||||
|
||||
#include <ethash/ethash.hpp>
|
||||
|
||||
namespace progpow
|
||||
{
|
||||
using namespace ethash; // Include ethash namespace.
|
||||
|
||||
|
||||
/// The ProgPoW algorithm revision implemented as specified in the spec
|
||||
/// https://github.com/ifdefelse/ProgPOW#change-history.
|
||||
constexpr auto revision = "0.9.2";
|
||||
|
||||
constexpr int period_length = 50;
|
||||
constexpr uint32_t num_regs = 32;
|
||||
constexpr size_t num_lanes = 16;
|
||||
constexpr int num_cache_accesses = 12;
|
||||
constexpr int num_math_operations = 20;
|
||||
constexpr size_t l1_cache_size = 16 * 1024;
|
||||
constexpr size_t l1_cache_num_items = l1_cache_size / sizeof(uint32_t);
|
||||
|
||||
result hash(const epoch_context& context, int block_number, const hash256& header_hash,
|
||||
uint64_t nonce) noexcept;
|
||||
|
||||
result hash(const epoch_context_full& context, int block_number, const hash256& header_hash,
|
||||
uint64_t nonce) noexcept;
|
||||
|
||||
bool verify(const epoch_context& context, int block_number, const hash256& header_hash,
|
||||
const hash256& mix_hash, uint64_t nonce, const hash256& boundary) noexcept;
|
||||
|
||||
search_result search_light(const epoch_context& context, int block_number,
|
||||
const hash256& header_hash, const hash256& boundary, uint64_t start_nonce,
|
||||
size_t iterations) noexcept;
|
||||
|
||||
search_result search(const epoch_context_full& context, int block_number,
|
||||
const hash256& header_hash, const hash256& boundary, uint64_t start_nonce,
|
||||
size_t iterations) noexcept;
|
||||
|
||||
} // namespace progpow
|
||||
18
contrib/ethereum/libethash/ethash/version.h
Normal file
18
contrib/ethereum/libethash/ethash/version.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
* Copyright 2019 Pawel Bylica.
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** The ethash library version. */
|
||||
#define ETHASH_VERSION "0.4.3"
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace ethash
|
||||
{
|
||||
/// The ethash library version.
|
||||
constexpr auto version = ETHASH_VERSION;
|
||||
|
||||
} // namespace ethash
|
||||
#endif
|
||||
123
contrib/ethereum/libethash/keccak.c
Normal file
123
contrib/ethereum/libethash/keccak.c
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
* Copyright 2018 Pawel Bylica.
|
||||
* Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
*/
|
||||
|
||||
#include <ethash/keccak.h>
|
||||
|
||||
#include "support/attributes.h"
|
||||
#include <string.h>
|
||||
|
||||
#if _WIN32
|
||||
/* On Windows assume little endian. */
|
||||
#define __LITTLE_ENDIAN 1234
|
||||
#define __BIG_ENDIAN 4321
|
||||
#define __BYTE_ORDER __LITTLE_ENDIAN
|
||||
#elif __APPLE__
|
||||
#include <machine/endian.h>
|
||||
#else
|
||||
#include <endian.h>
|
||||
#endif
|
||||
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#define to_le64(X) X
|
||||
#else
|
||||
#define to_le64(X) __builtin_bswap64(X)
|
||||
#endif
|
||||
|
||||
|
||||
/** Loads 64-bit integer from given memory location as little-endian number. */
|
||||
static INLINE ALWAYS_INLINE uint64_t load_le(const uint8_t* data)
|
||||
{
|
||||
/* memcpy is the best way of expressing the intention. Every compiler will
|
||||
optimize is to single load instruction if the target architecture
|
||||
supports unaligned memory access (GCC and clang even in O0).
|
||||
This is great trick because we are violating C/C++ memory alignment
|
||||
restrictions with no performance penalty. */
|
||||
uint64_t word;
|
||||
memcpy(&word, data, sizeof(word));
|
||||
return to_le64(word);
|
||||
}
|
||||
|
||||
static INLINE ALWAYS_INLINE void keccak(
|
||||
uint64_t* out, size_t bits, const uint8_t* data, size_t size)
|
||||
{
|
||||
static const size_t word_size = sizeof(uint64_t);
|
||||
const size_t hash_size = bits / 8;
|
||||
const size_t block_size = (1600 - bits * 2) / 8;
|
||||
|
||||
size_t i;
|
||||
uint64_t* state_iter;
|
||||
uint64_t last_word = 0;
|
||||
uint8_t* last_word_iter = (uint8_t*)&last_word;
|
||||
|
||||
uint64_t state[25] = {0};
|
||||
|
||||
while (size >= block_size)
|
||||
{
|
||||
for (i = 0; i < (block_size / word_size); ++i)
|
||||
{
|
||||
state[i] ^= load_le(data);
|
||||
data += word_size;
|
||||
}
|
||||
|
||||
ethash_keccakf1600(state);
|
||||
|
||||
size -= block_size;
|
||||
}
|
||||
|
||||
state_iter = state;
|
||||
|
||||
while (size >= word_size)
|
||||
{
|
||||
*state_iter ^= load_le(data);
|
||||
++state_iter;
|
||||
data += word_size;
|
||||
size -= word_size;
|
||||
}
|
||||
|
||||
while (size > 0)
|
||||
{
|
||||
*last_word_iter = *data;
|
||||
++last_word_iter;
|
||||
++data;
|
||||
--size;
|
||||
}
|
||||
*last_word_iter = 0x01;
|
||||
*state_iter ^= to_le64(last_word);
|
||||
|
||||
state[(block_size / word_size) - 1] ^= 0x8000000000000000;
|
||||
|
||||
ethash_keccakf1600(state);
|
||||
|
||||
for (i = 0; i < (hash_size / word_size); ++i)
|
||||
out[i] = to_le64(state[i]);
|
||||
}
|
||||
|
||||
union ethash_hash256 ethash_keccak256(const uint8_t* data, size_t size)
|
||||
{
|
||||
union ethash_hash256 hash;
|
||||
keccak(hash.word64s, 256, data, size);
|
||||
return hash;
|
||||
}
|
||||
|
||||
union ethash_hash256 ethash_keccak256_32(const uint8_t data[32])
|
||||
{
|
||||
union ethash_hash256 hash;
|
||||
keccak(hash.word64s, 256, data, 32);
|
||||
return hash;
|
||||
}
|
||||
|
||||
union ethash_hash512 ethash_keccak512(const uint8_t* data, size_t size)
|
||||
{
|
||||
union ethash_hash512 hash;
|
||||
keccak(hash.word64s, 512, data, size);
|
||||
return hash;
|
||||
}
|
||||
|
||||
union ethash_hash512 ethash_keccak512_64(const uint8_t data[64])
|
||||
{
|
||||
union ethash_hash512 hash;
|
||||
keccak(hash.word64s, 512, data, 64);
|
||||
return hash;
|
||||
}
|
||||
255
contrib/ethereum/libethash/keccakf1600.c
Normal file
255
contrib/ethereum/libethash/keccakf1600.c
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
* Copyright 2018 Pawel Bylica.
|
||||
* Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static uint64_t rol(uint64_t x, unsigned s)
|
||||
{
|
||||
return (x << s) | (x >> (64 - s));
|
||||
}
|
||||
|
||||
static const uint64_t round_constants[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,
|
||||
};
|
||||
|
||||
void ethash_keccakf1600(uint64_t state[25])
|
||||
{
|
||||
/* The implementation based on the "simple" implementation by Ronny Van Keer. */
|
||||
|
||||
int round;
|
||||
|
||||
uint64_t Aba, Abe, Abi, Abo, Abu;
|
||||
uint64_t Aga, Age, Agi, Ago, Agu;
|
||||
uint64_t Aka, Ake, Aki, Ako, Aku;
|
||||
uint64_t Ama, Ame, Ami, Amo, Amu;
|
||||
uint64_t Asa, Ase, Asi, Aso, Asu;
|
||||
|
||||
uint64_t Eba, Ebe, Ebi, Ebo, Ebu;
|
||||
uint64_t Ega, Ege, Egi, Ego, Egu;
|
||||
uint64_t Eka, Eke, Eki, Eko, Eku;
|
||||
uint64_t Ema, Eme, Emi, Emo, Emu;
|
||||
uint64_t Esa, Ese, Esi, Eso, Esu;
|
||||
|
||||
uint64_t Ba, Be, Bi, Bo, Bu;
|
||||
|
||||
uint64_t Da, De, Di, Do, Du;
|
||||
|
||||
Aba = state[0];
|
||||
Abe = state[1];
|
||||
Abi = state[2];
|
||||
Abo = state[3];
|
||||
Abu = state[4];
|
||||
Aga = state[5];
|
||||
Age = state[6];
|
||||
Agi = state[7];
|
||||
Ago = state[8];
|
||||
Agu = state[9];
|
||||
Aka = state[10];
|
||||
Ake = state[11];
|
||||
Aki = state[12];
|
||||
Ako = state[13];
|
||||
Aku = state[14];
|
||||
Ama = state[15];
|
||||
Ame = state[16];
|
||||
Ami = state[17];
|
||||
Amo = state[18];
|
||||
Amu = state[19];
|
||||
Asa = state[20];
|
||||
Ase = state[21];
|
||||
Asi = state[22];
|
||||
Aso = state[23];
|
||||
Asu = state[24];
|
||||
|
||||
for (round = 0; round < 24; round += 2)
|
||||
{
|
||||
/* Round (round + 0): Axx -> Exx */
|
||||
|
||||
Ba = Aba ^ Aga ^ Aka ^ Ama ^ Asa;
|
||||
Be = Abe ^ Age ^ Ake ^ Ame ^ Ase;
|
||||
Bi = Abi ^ Agi ^ Aki ^ Ami ^ Asi;
|
||||
Bo = Abo ^ Ago ^ Ako ^ Amo ^ Aso;
|
||||
Bu = Abu ^ Agu ^ Aku ^ Amu ^ Asu;
|
||||
|
||||
Da = Bu ^ rol(Be, 1);
|
||||
De = Ba ^ rol(Bi, 1);
|
||||
Di = Be ^ rol(Bo, 1);
|
||||
Do = Bi ^ rol(Bu, 1);
|
||||
Du = Bo ^ rol(Ba, 1);
|
||||
|
||||
Ba = Aba ^ Da;
|
||||
Be = rol(Age ^ De, 44);
|
||||
Bi = rol(Aki ^ Di, 43);
|
||||
Bo = rol(Amo ^ Do, 21);
|
||||
Bu = rol(Asu ^ Du, 14);
|
||||
Eba = Ba ^ (~Be & Bi) ^ round_constants[round];
|
||||
Ebe = Be ^ (~Bi & Bo);
|
||||
Ebi = Bi ^ (~Bo & Bu);
|
||||
Ebo = Bo ^ (~Bu & Ba);
|
||||
Ebu = Bu ^ (~Ba & Be);
|
||||
|
||||
Ba = rol(Abo ^ Do, 28);
|
||||
Be = rol(Agu ^ Du, 20);
|
||||
Bi = rol(Aka ^ Da, 3);
|
||||
Bo = rol(Ame ^ De, 45);
|
||||
Bu = rol(Asi ^ Di, 61);
|
||||
Ega = Ba ^ (~Be & Bi);
|
||||
Ege = Be ^ (~Bi & Bo);
|
||||
Egi = Bi ^ (~Bo & Bu);
|
||||
Ego = Bo ^ (~Bu & Ba);
|
||||
Egu = Bu ^ (~Ba & Be);
|
||||
|
||||
Ba = rol(Abe ^ De, 1);
|
||||
Be = rol(Agi ^ Di, 6);
|
||||
Bi = rol(Ako ^ Do, 25);
|
||||
Bo = rol(Amu ^ Du, 8);
|
||||
Bu = rol(Asa ^ Da, 18);
|
||||
Eka = Ba ^ (~Be & Bi);
|
||||
Eke = Be ^ (~Bi & Bo);
|
||||
Eki = Bi ^ (~Bo & Bu);
|
||||
Eko = Bo ^ (~Bu & Ba);
|
||||
Eku = Bu ^ (~Ba & Be);
|
||||
|
||||
Ba = rol(Abu ^ Du, 27);
|
||||
Be = rol(Aga ^ Da, 36);
|
||||
Bi = rol(Ake ^ De, 10);
|
||||
Bo = rol(Ami ^ Di, 15);
|
||||
Bu = rol(Aso ^ Do, 56);
|
||||
Ema = Ba ^ (~Be & Bi);
|
||||
Eme = Be ^ (~Bi & Bo);
|
||||
Emi = Bi ^ (~Bo & Bu);
|
||||
Emo = Bo ^ (~Bu & Ba);
|
||||
Emu = Bu ^ (~Ba & Be);
|
||||
|
||||
Ba = rol(Abi ^ Di, 62);
|
||||
Be = rol(Ago ^ Do, 55);
|
||||
Bi = rol(Aku ^ Du, 39);
|
||||
Bo = rol(Ama ^ Da, 41);
|
||||
Bu = rol(Ase ^ De, 2);
|
||||
Esa = Ba ^ (~Be & Bi);
|
||||
Ese = Be ^ (~Bi & Bo);
|
||||
Esi = Bi ^ (~Bo & Bu);
|
||||
Eso = Bo ^ (~Bu & Ba);
|
||||
Esu = Bu ^ (~Ba & Be);
|
||||
|
||||
|
||||
/* Round (round + 1): Exx -> Axx */
|
||||
|
||||
Ba = Eba ^ Ega ^ Eka ^ Ema ^ Esa;
|
||||
Be = Ebe ^ Ege ^ Eke ^ Eme ^ Ese;
|
||||
Bi = Ebi ^ Egi ^ Eki ^ Emi ^ Esi;
|
||||
Bo = Ebo ^ Ego ^ Eko ^ Emo ^ Eso;
|
||||
Bu = Ebu ^ Egu ^ Eku ^ Emu ^ Esu;
|
||||
|
||||
Da = Bu ^ rol(Be, 1);
|
||||
De = Ba ^ rol(Bi, 1);
|
||||
Di = Be ^ rol(Bo, 1);
|
||||
Do = Bi ^ rol(Bu, 1);
|
||||
Du = Bo ^ rol(Ba, 1);
|
||||
|
||||
Ba = Eba ^ Da;
|
||||
Be = rol(Ege ^ De, 44);
|
||||
Bi = rol(Eki ^ Di, 43);
|
||||
Bo = rol(Emo ^ Do, 21);
|
||||
Bu = rol(Esu ^ Du, 14);
|
||||
Aba = Ba ^ (~Be & Bi) ^ round_constants[round + 1];
|
||||
Abe = Be ^ (~Bi & Bo);
|
||||
Abi = Bi ^ (~Bo & Bu);
|
||||
Abo = Bo ^ (~Bu & Ba);
|
||||
Abu = Bu ^ (~Ba & Be);
|
||||
|
||||
Ba = rol(Ebo ^ Do, 28);
|
||||
Be = rol(Egu ^ Du, 20);
|
||||
Bi = rol(Eka ^ Da, 3);
|
||||
Bo = rol(Eme ^ De, 45);
|
||||
Bu = rol(Esi ^ Di, 61);
|
||||
Aga = Ba ^ (~Be & Bi);
|
||||
Age = Be ^ (~Bi & Bo);
|
||||
Agi = Bi ^ (~Bo & Bu);
|
||||
Ago = Bo ^ (~Bu & Ba);
|
||||
Agu = Bu ^ (~Ba & Be);
|
||||
|
||||
Ba = rol(Ebe ^ De, 1);
|
||||
Be = rol(Egi ^ Di, 6);
|
||||
Bi = rol(Eko ^ Do, 25);
|
||||
Bo = rol(Emu ^ Du, 8);
|
||||
Bu = rol(Esa ^ Da, 18);
|
||||
Aka = Ba ^ (~Be & Bi);
|
||||
Ake = Be ^ (~Bi & Bo);
|
||||
Aki = Bi ^ (~Bo & Bu);
|
||||
Ako = Bo ^ (~Bu & Ba);
|
||||
Aku = Bu ^ (~Ba & Be);
|
||||
|
||||
Ba = rol(Ebu ^ Du, 27);
|
||||
Be = rol(Ega ^ Da, 36);
|
||||
Bi = rol(Eke ^ De, 10);
|
||||
Bo = rol(Emi ^ Di, 15);
|
||||
Bu = rol(Eso ^ Do, 56);
|
||||
Ama = Ba ^ (~Be & Bi);
|
||||
Ame = Be ^ (~Bi & Bo);
|
||||
Ami = Bi ^ (~Bo & Bu);
|
||||
Amo = Bo ^ (~Bu & Ba);
|
||||
Amu = Bu ^ (~Ba & Be);
|
||||
|
||||
Ba = rol(Ebi ^ Di, 62);
|
||||
Be = rol(Ego ^ Do, 55);
|
||||
Bi = rol(Eku ^ Du, 39);
|
||||
Bo = rol(Ema ^ Da, 41);
|
||||
Bu = rol(Ese ^ De, 2);
|
||||
Asa = Ba ^ (~Be & Bi);
|
||||
Ase = Be ^ (~Bi & Bo);
|
||||
Asi = Bi ^ (~Bo & Bu);
|
||||
Aso = Bo ^ (~Bu & Ba);
|
||||
Asu = Bu ^ (~Ba & Be);
|
||||
}
|
||||
|
||||
state[0] = Aba;
|
||||
state[1] = Abe;
|
||||
state[2] = Abi;
|
||||
state[3] = Abo;
|
||||
state[4] = Abu;
|
||||
state[5] = Aga;
|
||||
state[6] = Age;
|
||||
state[7] = Agi;
|
||||
state[8] = Ago;
|
||||
state[9] = Agu;
|
||||
state[10] = Aka;
|
||||
state[11] = Ake;
|
||||
state[12] = Aki;
|
||||
state[13] = Ako;
|
||||
state[14] = Aku;
|
||||
state[15] = Ama;
|
||||
state[16] = Ame;
|
||||
state[17] = Ami;
|
||||
state[18] = Amo;
|
||||
state[19] = Amu;
|
||||
state[20] = Asa;
|
||||
state[21] = Ase;
|
||||
state[22] = Asi;
|
||||
state[23] = Aso;
|
||||
state[24] = Asu;
|
||||
}
|
||||
253
contrib/ethereum/libethash/keccakf800.c
Normal file
253
contrib/ethereum/libethash/keccakf800.c
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
* Copyright 2018 Pawel Bylica.
|
||||
* Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static uint32_t rol(uint32_t x, unsigned s)
|
||||
{
|
||||
return (x << s) | (x >> (32 - s));
|
||||
}
|
||||
|
||||
static const uint32_t round_constants[22] = {
|
||||
0x00000001,
|
||||
0x00008082,
|
||||
0x0000808A,
|
||||
0x80008000,
|
||||
0x0000808B,
|
||||
0x80000001,
|
||||
0x80008081,
|
||||
0x00008009,
|
||||
0x0000008A,
|
||||
0x00000088,
|
||||
0x80008009,
|
||||
0x8000000A,
|
||||
0x8000808B,
|
||||
0x0000008B,
|
||||
0x00008089,
|
||||
0x00008003,
|
||||
0x00008002,
|
||||
0x00000080,
|
||||
0x0000800A,
|
||||
0x8000000A,
|
||||
0x80008081,
|
||||
0x00008080,
|
||||
};
|
||||
|
||||
void ethash_keccakf800(uint32_t state[25])
|
||||
{
|
||||
/* The implementation directly translated from ethash_keccakf1600. */
|
||||
|
||||
int round;
|
||||
|
||||
uint32_t Aba, Abe, Abi, Abo, Abu;
|
||||
uint32_t Aga, Age, Agi, Ago, Agu;
|
||||
uint32_t Aka, Ake, Aki, Ako, Aku;
|
||||
uint32_t Ama, Ame, Ami, Amo, Amu;
|
||||
uint32_t Asa, Ase, Asi, Aso, Asu;
|
||||
|
||||
uint32_t Eba, Ebe, Ebi, Ebo, Ebu;
|
||||
uint32_t Ega, Ege, Egi, Ego, Egu;
|
||||
uint32_t Eka, Eke, Eki, Eko, Eku;
|
||||
uint32_t Ema, Eme, Emi, Emo, Emu;
|
||||
uint32_t Esa, Ese, Esi, Eso, Esu;
|
||||
|
||||
uint32_t Ba, Be, Bi, Bo, Bu;
|
||||
|
||||
uint32_t Da, De, Di, Do, Du;
|
||||
|
||||
Aba = state[0];
|
||||
Abe = state[1];
|
||||
Abi = state[2];
|
||||
Abo = state[3];
|
||||
Abu = state[4];
|
||||
Aga = state[5];
|
||||
Age = state[6];
|
||||
Agi = state[7];
|
||||
Ago = state[8];
|
||||
Agu = state[9];
|
||||
Aka = state[10];
|
||||
Ake = state[11];
|
||||
Aki = state[12];
|
||||
Ako = state[13];
|
||||
Aku = state[14];
|
||||
Ama = state[15];
|
||||
Ame = state[16];
|
||||
Ami = state[17];
|
||||
Amo = state[18];
|
||||
Amu = state[19];
|
||||
Asa = state[20];
|
||||
Ase = state[21];
|
||||
Asi = state[22];
|
||||
Aso = state[23];
|
||||
Asu = state[24];
|
||||
|
||||
for (round = 0; round < 22; round += 2)
|
||||
{
|
||||
/* Round (round + 0): Axx -> Exx */
|
||||
|
||||
Ba = Aba ^ Aga ^ Aka ^ Ama ^ Asa;
|
||||
Be = Abe ^ Age ^ Ake ^ Ame ^ Ase;
|
||||
Bi = Abi ^ Agi ^ Aki ^ Ami ^ Asi;
|
||||
Bo = Abo ^ Ago ^ Ako ^ Amo ^ Aso;
|
||||
Bu = Abu ^ Agu ^ Aku ^ Amu ^ Asu;
|
||||
|
||||
Da = Bu ^ rol(Be, 1);
|
||||
De = Ba ^ rol(Bi, 1);
|
||||
Di = Be ^ rol(Bo, 1);
|
||||
Do = Bi ^ rol(Bu, 1);
|
||||
Du = Bo ^ rol(Ba, 1);
|
||||
|
||||
Ba = Aba ^ Da;
|
||||
Be = rol(Age ^ De, 12);
|
||||
Bi = rol(Aki ^ Di, 11);
|
||||
Bo = rol(Amo ^ Do, 21);
|
||||
Bu = rol(Asu ^ Du, 14);
|
||||
Eba = Ba ^ (~Be & Bi) ^ round_constants[round];
|
||||
Ebe = Be ^ (~Bi & Bo);
|
||||
Ebi = Bi ^ (~Bo & Bu);
|
||||
Ebo = Bo ^ (~Bu & Ba);
|
||||
Ebu = Bu ^ (~Ba & Be);
|
||||
|
||||
Ba = rol(Abo ^ Do, 28);
|
||||
Be = rol(Agu ^ Du, 20);
|
||||
Bi = rol(Aka ^ Da, 3);
|
||||
Bo = rol(Ame ^ De, 13);
|
||||
Bu = rol(Asi ^ Di, 29);
|
||||
Ega = Ba ^ (~Be & Bi);
|
||||
Ege = Be ^ (~Bi & Bo);
|
||||
Egi = Bi ^ (~Bo & Bu);
|
||||
Ego = Bo ^ (~Bu & Ba);
|
||||
Egu = Bu ^ (~Ba & Be);
|
||||
|
||||
Ba = rol(Abe ^ De, 1);
|
||||
Be = rol(Agi ^ Di, 6);
|
||||
Bi = rol(Ako ^ Do, 25);
|
||||
Bo = rol(Amu ^ Du, 8);
|
||||
Bu = rol(Asa ^ Da, 18);
|
||||
Eka = Ba ^ (~Be & Bi);
|
||||
Eke = Be ^ (~Bi & Bo);
|
||||
Eki = Bi ^ (~Bo & Bu);
|
||||
Eko = Bo ^ (~Bu & Ba);
|
||||
Eku = Bu ^ (~Ba & Be);
|
||||
|
||||
Ba = rol(Abu ^ Du, 27);
|
||||
Be = rol(Aga ^ Da, 4);
|
||||
Bi = rol(Ake ^ De, 10);
|
||||
Bo = rol(Ami ^ Di, 15);
|
||||
Bu = rol(Aso ^ Do, 24);
|
||||
Ema = Ba ^ (~Be & Bi);
|
||||
Eme = Be ^ (~Bi & Bo);
|
||||
Emi = Bi ^ (~Bo & Bu);
|
||||
Emo = Bo ^ (~Bu & Ba);
|
||||
Emu = Bu ^ (~Ba & Be);
|
||||
|
||||
Ba = rol(Abi ^ Di, 30);
|
||||
Be = rol(Ago ^ Do, 23);
|
||||
Bi = rol(Aku ^ Du, 7);
|
||||
Bo = rol(Ama ^ Da, 9);
|
||||
Bu = rol(Ase ^ De, 2);
|
||||
Esa = Ba ^ (~Be & Bi);
|
||||
Ese = Be ^ (~Bi & Bo);
|
||||
Esi = Bi ^ (~Bo & Bu);
|
||||
Eso = Bo ^ (~Bu & Ba);
|
||||
Esu = Bu ^ (~Ba & Be);
|
||||
|
||||
|
||||
/* Round (round + 1): Exx -> Axx */
|
||||
|
||||
Ba = Eba ^ Ega ^ Eka ^ Ema ^ Esa;
|
||||
Be = Ebe ^ Ege ^ Eke ^ Eme ^ Ese;
|
||||
Bi = Ebi ^ Egi ^ Eki ^ Emi ^ Esi;
|
||||
Bo = Ebo ^ Ego ^ Eko ^ Emo ^ Eso;
|
||||
Bu = Ebu ^ Egu ^ Eku ^ Emu ^ Esu;
|
||||
|
||||
Da = Bu ^ rol(Be, 1);
|
||||
De = Ba ^ rol(Bi, 1);
|
||||
Di = Be ^ rol(Bo, 1);
|
||||
Do = Bi ^ rol(Bu, 1);
|
||||
Du = Bo ^ rol(Ba, 1);
|
||||
|
||||
Ba = Eba ^ Da;
|
||||
Be = rol(Ege ^ De, 12);
|
||||
Bi = rol(Eki ^ Di, 11);
|
||||
Bo = rol(Emo ^ Do, 21);
|
||||
Bu = rol(Esu ^ Du, 14);
|
||||
Aba = Ba ^ (~Be & Bi) ^ round_constants[round + 1];
|
||||
Abe = Be ^ (~Bi & Bo);
|
||||
Abi = Bi ^ (~Bo & Bu);
|
||||
Abo = Bo ^ (~Bu & Ba);
|
||||
Abu = Bu ^ (~Ba & Be);
|
||||
|
||||
Ba = rol(Ebo ^ Do, 28);
|
||||
Be = rol(Egu ^ Du, 20);
|
||||
Bi = rol(Eka ^ Da, 3);
|
||||
Bo = rol(Eme ^ De, 13);
|
||||
Bu = rol(Esi ^ Di, 29);
|
||||
Aga = Ba ^ (~Be & Bi);
|
||||
Age = Be ^ (~Bi & Bo);
|
||||
Agi = Bi ^ (~Bo & Bu);
|
||||
Ago = Bo ^ (~Bu & Ba);
|
||||
Agu = Bu ^ (~Ba & Be);
|
||||
|
||||
Ba = rol(Ebe ^ De, 1);
|
||||
Be = rol(Egi ^ Di, 6);
|
||||
Bi = rol(Eko ^ Do, 25);
|
||||
Bo = rol(Emu ^ Du, 8);
|
||||
Bu = rol(Esa ^ Da, 18);
|
||||
Aka = Ba ^ (~Be & Bi);
|
||||
Ake = Be ^ (~Bi & Bo);
|
||||
Aki = Bi ^ (~Bo & Bu);
|
||||
Ako = Bo ^ (~Bu & Ba);
|
||||
Aku = Bu ^ (~Ba & Be);
|
||||
|
||||
Ba = rol(Ebu ^ Du, 27);
|
||||
Be = rol(Ega ^ Da, 4);
|
||||
Bi = rol(Eke ^ De, 10);
|
||||
Bo = rol(Emi ^ Di, 15);
|
||||
Bu = rol(Eso ^ Do, 24);
|
||||
Ama = Ba ^ (~Be & Bi);
|
||||
Ame = Be ^ (~Bi & Bo);
|
||||
Ami = Bi ^ (~Bo & Bu);
|
||||
Amo = Bo ^ (~Bu & Ba);
|
||||
Amu = Bu ^ (~Ba & Be);
|
||||
|
||||
Ba = rol(Ebi ^ Di, 30);
|
||||
Be = rol(Ego ^ Do, 23);
|
||||
Bi = rol(Eku ^ Du, 7);
|
||||
Bo = rol(Ema ^ Da, 9);
|
||||
Bu = rol(Ese ^ De, 2);
|
||||
Asa = Ba ^ (~Be & Bi);
|
||||
Ase = Be ^ (~Bi & Bo);
|
||||
Asi = Bi ^ (~Bo & Bu);
|
||||
Aso = Bo ^ (~Bu & Ba);
|
||||
Asu = Bu ^ (~Ba & Be);
|
||||
}
|
||||
|
||||
state[0] = Aba;
|
||||
state[1] = Abe;
|
||||
state[2] = Abi;
|
||||
state[3] = Abo;
|
||||
state[4] = Abu;
|
||||
state[5] = Aga;
|
||||
state[6] = Age;
|
||||
state[7] = Agi;
|
||||
state[8] = Ago;
|
||||
state[9] = Agu;
|
||||
state[10] = Aka;
|
||||
state[11] = Ake;
|
||||
state[12] = Aki;
|
||||
state[13] = Ako;
|
||||
state[14] = Aku;
|
||||
state[15] = Ama;
|
||||
state[16] = Ame;
|
||||
state[17] = Ami;
|
||||
state[18] = Amo;
|
||||
state[19] = Amu;
|
||||
state[20] = Asa;
|
||||
state[21] = Ase;
|
||||
state[22] = Asi;
|
||||
state[23] = Aso;
|
||||
state[24] = Asu;
|
||||
}
|
||||
64
contrib/ethereum/libethash/kiss99.hpp
Normal file
64
contrib/ethereum/libethash/kiss99.hpp
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
* Copyright 2018 Pawel Bylica.
|
||||
* Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "support/attributes.h"
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* KISS PRNG by the spec from 1999.
|
||||
*
|
||||
* The implementation of KISS pseudo-random number generator
|
||||
* by the specification published on 21 Jan 1999 in
|
||||
* http://www.cse.yorku.ca/~oz/marsaglia-rng.html.
|
||||
* The KISS is not versioned so here we are using `kiss99` prefix to indicate
|
||||
* the version from 1999.
|
||||
*
|
||||
* The specification uses `unsigned long` type with the intention for 32-bit
|
||||
* values. Because in GCC/clang for 64-bit architectures `unsigned long` is
|
||||
* 64-bit size type, here the explicit `uint32_t` type is used.
|
||||
*
|
||||
* @defgroup kiss99 KISS99
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* The KISS generator.
|
||||
*/
|
||||
class kiss99
|
||||
{
|
||||
uint32_t z = 362436069;
|
||||
uint32_t w = 521288629;
|
||||
uint32_t jsr = 123456789;
|
||||
uint32_t jcong = 380116160;
|
||||
|
||||
public:
|
||||
/** Creates KISS generator state with default values provided by the specification. */
|
||||
kiss99() noexcept = default;
|
||||
|
||||
/** Creates KISS generator state with provided init values.*/
|
||||
kiss99(uint32_t z, uint32_t w, uint32_t jsr, uint32_t jcong) noexcept
|
||||
: z{z}, w{w}, jsr{jsr}, jcong{jcong}
|
||||
{}
|
||||
|
||||
/** Generates next number from the KISS generator. */
|
||||
NO_SANITIZE("unsigned-integer-overflow")
|
||||
uint32_t operator()() noexcept
|
||||
{
|
||||
z = 36969 * (z & 0xffff) + (z >> 16);
|
||||
w = 18000 * (w & 0xffff) + (w >> 16);
|
||||
|
||||
jcong = 69069 * jcong + 1234567;
|
||||
|
||||
jsr ^= (jsr << 17);
|
||||
jsr ^= (jsr >> 13);
|
||||
jsr ^= (jsr << 5);
|
||||
|
||||
return (((z << 16) + w) ^ jcong) + jsr;
|
||||
}
|
||||
};
|
||||
|
||||
/** @} */
|
||||
100
contrib/ethereum/libethash/managed.cpp
Normal file
100
contrib/ethereum/libethash/managed.cpp
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
// ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
// Copyright 2018 Pawel Bylica.
|
||||
// Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
|
||||
#include "ethash-internal.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#if !defined(__has_cpp_attribute)
|
||||
#define __has_cpp_attribute(x) 0
|
||||
#endif
|
||||
|
||||
#if __has_cpp_attribute(gnu::noinline)
|
||||
#define ATTRIBUTE_NOINLINE [[gnu::noinline]]
|
||||
#elif _MSC_VER
|
||||
#define ATTRIBUTE_NOINLINE __declspec(noinline)
|
||||
#else
|
||||
#define ATTRIBUTE_NOINLINE
|
||||
#endif
|
||||
|
||||
namespace ethash
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::mutex shared_context_mutex;
|
||||
std::shared_ptr<epoch_context> shared_context;
|
||||
thread_local std::shared_ptr<epoch_context> thread_local_context;
|
||||
|
||||
std::mutex shared_context_full_mutex;
|
||||
std::shared_ptr<epoch_context_full> shared_context_full;
|
||||
thread_local std::shared_ptr<epoch_context_full> thread_local_context_full;
|
||||
|
||||
/// Update thread local epoch context.
|
||||
///
|
||||
/// This function is on the slow path. It's separated to allow inlining the fast
|
||||
/// path.
|
||||
///
|
||||
/// @todo: Redesign to guarantee deallocation before new allocation.
|
||||
ATTRIBUTE_NOINLINE
|
||||
void update_local_context(int epoch_number)
|
||||
{
|
||||
// Release the shared pointer of the obsoleted context.
|
||||
thread_local_context.reset();
|
||||
|
||||
// Local context invalid, check the shared context.
|
||||
std::lock_guard<std::mutex> lock{shared_context_mutex};
|
||||
|
||||
if (!shared_context || shared_context->epoch_number != epoch_number)
|
||||
{
|
||||
// Release the shared pointer of the obsoleted context.
|
||||
shared_context.reset();
|
||||
|
||||
// Build new context.
|
||||
shared_context = create_epoch_context(epoch_number);
|
||||
}
|
||||
|
||||
thread_local_context = shared_context;
|
||||
}
|
||||
|
||||
ATTRIBUTE_NOINLINE
|
||||
void update_local_context_full(int epoch_number)
|
||||
{
|
||||
// Release the shared pointer of the obsoleted context.
|
||||
thread_local_context_full.reset();
|
||||
|
||||
// Local context invalid, check the shared context.
|
||||
std::lock_guard<std::mutex> lock{shared_context_full_mutex};
|
||||
|
||||
if (!shared_context_full || shared_context_full->epoch_number != epoch_number)
|
||||
{
|
||||
// Release the shared pointer of the obsoleted context.
|
||||
shared_context_full.reset();
|
||||
|
||||
// Build new context.
|
||||
shared_context_full = create_epoch_context_full(epoch_number);
|
||||
}
|
||||
|
||||
thread_local_context_full = shared_context_full;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
const epoch_context& get_global_epoch_context(int epoch_number)
|
||||
{
|
||||
// Check if local context matches epoch number.
|
||||
if (!thread_local_context || thread_local_context->epoch_number != epoch_number)
|
||||
update_local_context(epoch_number);
|
||||
|
||||
return *thread_local_context;
|
||||
}
|
||||
|
||||
const epoch_context_full& get_global_epoch_context_full(int epoch_number)
|
||||
{
|
||||
// Check if local context matches epoch number.
|
||||
if (!thread_local_context_full || thread_local_context_full->epoch_number != epoch_number)
|
||||
update_local_context_full(epoch_number);
|
||||
|
||||
return *thread_local_context_full;
|
||||
}
|
||||
} // namespace ethash
|
||||
43
contrib/ethereum/libethash/primes.c
Normal file
43
contrib/ethereum/libethash/primes.c
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
* Copyright 2018 Pawel Bylica.
|
||||
* Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "primes.h"
|
||||
|
||||
/** Checks if the number is prime. Requires the number to be > 2 and odd. */
|
||||
static int is_odd_prime(int number)
|
||||
{
|
||||
int d;
|
||||
|
||||
/* Check factors up to sqrt(number).
|
||||
To avoid computing sqrt, compare d*d <= number with 64-bit precision. */
|
||||
for (d = 3; (int64_t)d * (int64_t)d <= (int64_t)number; d += 2)
|
||||
{
|
||||
if (number % d == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ethash_find_largest_prime(int upper_bound)
|
||||
{
|
||||
int n = upper_bound;
|
||||
|
||||
if (n < 2)
|
||||
return 0;
|
||||
|
||||
if (n == 2)
|
||||
return 2;
|
||||
|
||||
/* If even number, skip it. */
|
||||
if (n % 2 == 0)
|
||||
--n;
|
||||
|
||||
/* Test descending odd numbers. */
|
||||
while (!is_odd_prime(n))
|
||||
n -= 2;
|
||||
|
||||
return n;
|
||||
}
|
||||
25
contrib/ethereum/libethash/primes.h
Normal file
25
contrib/ethereum/libethash/primes.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
* Copyright 2018 Pawel Bylica.
|
||||
* Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ethash/ethash.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Finds the largest prime number not greater than the provided upper bound.
|
||||
*
|
||||
* @param upper_bound The upper bound. SHOULD be greater than 1.
|
||||
* @return The largest prime number `p` such `p <= upper_bound`.
|
||||
* In case `upper_bound <= 1`, returns 0.
|
||||
*/
|
||||
int ethash_find_largest_prime(int upper_bound) NOEXCEPT;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
360
contrib/ethereum/libethash/progpow.cpp
Normal file
360
contrib/ethereum/libethash/progpow.cpp
Normal file
|
|
@ -0,0 +1,360 @@
|
|||
// ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
// Copyright 2018 Pawel Bylica.
|
||||
// Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
|
||||
#include <ethash/progpow.hpp>
|
||||
|
||||
#include "bit_manipulation.h"
|
||||
#include "endianness.hpp"
|
||||
#include "ethash-internal.hpp"
|
||||
#include "kiss99.hpp"
|
||||
#include <ethash/keccak.hpp>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace progpow
|
||||
{
|
||||
namespace
|
||||
{
|
||||
/// A variant of Keccak hash function for ProgPoW.
|
||||
///
|
||||
/// This Keccak hash function uses 800-bit permutation (Keccak-f[800]) with 576 bitrate.
|
||||
/// It take exactly 576 bits of input (split across 3 arguments) and adds no padding.
|
||||
///
|
||||
/// @param header_hash The 256-bit header hash.
|
||||
/// @param nonce The 64-bit nonce.
|
||||
/// @param mix_hash Additional 256-bits of data.
|
||||
/// @return The 256-bit output of the hash function.
|
||||
hash256 keccak_progpow_256(
|
||||
const hash256& header_hash, uint64_t nonce, const hash256& mix_hash) noexcept
|
||||
{
|
||||
static constexpr size_t num_words =
|
||||
sizeof(header_hash.word32s) / sizeof(header_hash.word32s[0]);
|
||||
|
||||
uint32_t state[25] = {};
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < num_words; ++i)
|
||||
state[i] = le::uint32(header_hash.word32s[i]);
|
||||
|
||||
state[i++] = static_cast<uint32_t>(nonce);
|
||||
state[i++] = static_cast<uint32_t>(nonce >> 32);
|
||||
|
||||
for (uint32_t mix_word : mix_hash.word32s)
|
||||
state[i++] = le::uint32(mix_word);
|
||||
|
||||
ethash_keccakf800(state);
|
||||
|
||||
hash256 output;
|
||||
for (i = 0; i < num_words; ++i)
|
||||
output.word32s[i] = le::uint32(state[i]);
|
||||
return output;
|
||||
}
|
||||
|
||||
/// The same as keccak_progpow_256() but uses null mix
|
||||
/// and returns top 64 bits of the output being a big-endian prefix of the 256-bit hash.
|
||||
inline uint64_t keccak_progpow_64(const hash256& header_hash, uint64_t nonce) noexcept
|
||||
{
|
||||
const hash256 h = keccak_progpow_256(header_hash, nonce, {});
|
||||
return be::uint64(h.word64s[0]);
|
||||
}
|
||||
|
||||
|
||||
/// ProgPoW mix RNG state.
|
||||
///
|
||||
/// Encapsulates the state of the random number generator used in computing ProgPoW mix.
|
||||
/// This includes the state of the KISS99 RNG and the precomputed random permutation of the
|
||||
/// sequence of mix item indexes.
|
||||
class mix_rng_state
|
||||
{
|
||||
public:
|
||||
inline explicit mix_rng_state(uint64_t seed) noexcept;
|
||||
|
||||
uint32_t next_dst() noexcept { return dst_seq[(dst_counter++) % num_regs]; }
|
||||
uint32_t next_src() noexcept { return src_seq[(src_counter++) % num_regs]; }
|
||||
|
||||
kiss99 rng;
|
||||
|
||||
private:
|
||||
size_t dst_counter = 0;
|
||||
std::array<uint32_t, num_regs> dst_seq;
|
||||
size_t src_counter = 0;
|
||||
std::array<uint32_t, num_regs> src_seq;
|
||||
};
|
||||
|
||||
mix_rng_state::mix_rng_state(uint64_t seed) noexcept
|
||||
{
|
||||
const auto seed_lo = static_cast<uint32_t>(seed);
|
||||
const auto seed_hi = static_cast<uint32_t>(seed >> 32);
|
||||
|
||||
const auto z = fnv1a(fnv_offset_basis, seed_lo);
|
||||
const auto w = fnv1a(z, seed_hi);
|
||||
const auto jsr = fnv1a(w, seed_lo);
|
||||
const auto jcong = fnv1a(jsr, seed_hi);
|
||||
|
||||
rng = kiss99{z, w, jsr, jcong};
|
||||
|
||||
// Create random permutations of mix destinations / sources.
|
||||
// Uses Fisher-Yates shuffle.
|
||||
for (uint32_t i = 0; i < num_regs; ++i)
|
||||
{
|
||||
dst_seq[i] = i;
|
||||
src_seq[i] = i;
|
||||
}
|
||||
|
||||
for (uint32_t i = num_regs; i > 1; --i)
|
||||
{
|
||||
std::swap(dst_seq[i - 1], dst_seq[rng() % i]);
|
||||
std::swap(src_seq[i - 1], src_seq[rng() % i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NO_SANITIZE("unsigned-integer-overflow")
|
||||
inline uint32_t random_math(uint32_t a, uint32_t b, uint32_t selector) noexcept
|
||||
{
|
||||
switch (selector % 11)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
return a + b;
|
||||
case 1:
|
||||
return a * b;
|
||||
case 2:
|
||||
return mul_hi32(a, b);
|
||||
case 3:
|
||||
return std::min(a, b);
|
||||
case 4:
|
||||
return rotl32(a, b);
|
||||
case 5:
|
||||
return rotr32(a, b);
|
||||
case 6:
|
||||
return a & b;
|
||||
case 7:
|
||||
return a | b;
|
||||
case 8:
|
||||
return a ^ b;
|
||||
case 9:
|
||||
return clz32(a) + clz32(b);
|
||||
case 10:
|
||||
return popcount32(a) + popcount32(b);
|
||||
}
|
||||
}
|
||||
|
||||
/// Merge data from `b` and `a`.
|
||||
/// Assuming `a` has high entropy, only do ops that retain entropy even if `b`
|
||||
/// has low entropy (i.e. do not do `a & b`).
|
||||
NO_SANITIZE("unsigned-integer-overflow")
|
||||
inline void random_merge(uint32_t& a, uint32_t b, uint32_t selector) noexcept
|
||||
{
|
||||
const auto x = (selector >> 16) % 31 + 1; // Additional non-zero selector from higher bits.
|
||||
switch (selector % 4)
|
||||
{
|
||||
case 0:
|
||||
a = (a * 33) + b;
|
||||
break;
|
||||
case 1:
|
||||
a = (a ^ b) * 33;
|
||||
break;
|
||||
case 2:
|
||||
a = rotl32(a, x) ^ b;
|
||||
break;
|
||||
case 3:
|
||||
a = rotr32(a, x) ^ b;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
using lookup_fn = hash2048 (*)(const epoch_context&, uint32_t);
|
||||
|
||||
using mix_array = std::array<std::array<uint32_t, num_regs>, num_lanes>;
|
||||
|
||||
void round(
|
||||
const epoch_context& context, uint32_t r, mix_array& mix, mix_rng_state state, lookup_fn lookup)
|
||||
{
|
||||
const uint32_t num_items = static_cast<uint32_t>(context.full_dataset_num_items / 2);
|
||||
const uint32_t item_index = mix[r % num_lanes][0] % num_items;
|
||||
const hash2048 item = lookup(context, item_index);
|
||||
|
||||
constexpr size_t num_words_per_lane = sizeof(item) / (sizeof(uint32_t) * num_lanes);
|
||||
constexpr int max_operations =
|
||||
num_cache_accesses > num_math_operations ? num_cache_accesses : num_math_operations;
|
||||
|
||||
// Process lanes.
|
||||
for (int i = 0; i < max_operations; ++i)
|
||||
{
|
||||
if (i < num_cache_accesses) // Random access to cached memory.
|
||||
{
|
||||
const auto src = state.next_src();
|
||||
const auto dst = state.next_dst();
|
||||
const auto sel = state.rng();
|
||||
|
||||
for (size_t l = 0; l < num_lanes; ++l)
|
||||
{
|
||||
const size_t offset = mix[l][src] % l1_cache_num_items;
|
||||
random_merge(mix[l][dst], le::uint32(context.l1_cache[offset]), sel);
|
||||
}
|
||||
}
|
||||
if (i < num_math_operations) // Random math.
|
||||
{
|
||||
// Generate 2 unique source indexes.
|
||||
const auto src_rnd = state.rng() % (num_regs * (num_regs - 1));
|
||||
const auto src1 = src_rnd % num_regs; // O <= src1 < num_regs
|
||||
auto src2 = src_rnd / num_regs; // 0 <= src2 < num_regs - 1
|
||||
if (src2 >= src1)
|
||||
++src2;
|
||||
|
||||
const auto sel1 = state.rng();
|
||||
const auto dst = state.next_dst();
|
||||
const auto sel2 = state.rng();
|
||||
|
||||
for (size_t l = 0; l < num_lanes; ++l)
|
||||
{
|
||||
const uint32_t data = random_math(mix[l][src1], mix[l][src2], sel1);
|
||||
random_merge(mix[l][dst], data, sel2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DAG access pattern.
|
||||
uint32_t dsts[num_words_per_lane];
|
||||
uint32_t sels[num_words_per_lane];
|
||||
for (size_t i = 0; i < num_words_per_lane; ++i)
|
||||
{
|
||||
dsts[i] = i == 0 ? 0 : state.next_dst();
|
||||
sels[i] = state.rng();
|
||||
}
|
||||
|
||||
// DAG access.
|
||||
for (size_t l = 0; l < num_lanes; ++l)
|
||||
{
|
||||
const auto offset = ((l ^ r) % num_lanes) * num_words_per_lane;
|
||||
for (size_t i = 0; i < num_words_per_lane; ++i)
|
||||
{
|
||||
const auto word = le::uint32(item.word32s[offset + i]);
|
||||
random_merge(mix[l][dsts[i]], word, sels[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mix_array init_mix(uint64_t seed)
|
||||
{
|
||||
const uint32_t z = fnv1a(fnv_offset_basis, static_cast<uint32_t>(seed));
|
||||
const uint32_t w = fnv1a(z, static_cast<uint32_t>(seed >> 32));
|
||||
|
||||
mix_array mix;
|
||||
for (uint32_t l = 0; l < mix.size(); ++l)
|
||||
{
|
||||
const uint32_t jsr = fnv1a(w, l);
|
||||
const uint32_t jcong = fnv1a(jsr, l);
|
||||
kiss99 rng{z, w, jsr, jcong};
|
||||
|
||||
for (auto& row : mix[l])
|
||||
row = rng();
|
||||
}
|
||||
return mix;
|
||||
}
|
||||
|
||||
hash256 hash_mix(
|
||||
const epoch_context& context, int block_number, uint64_t seed, lookup_fn lookup) noexcept
|
||||
{
|
||||
auto mix = init_mix(seed);
|
||||
mix_rng_state state{uint64_t(block_number / period_length)};
|
||||
|
||||
for (uint32_t i = 0; i < 64; ++i)
|
||||
round(context, i, mix, state, lookup);
|
||||
|
||||
// Reduce mix data to a single per-lane result.
|
||||
uint32_t lane_hash[num_lanes];
|
||||
for (size_t l = 0; l < num_lanes; ++l)
|
||||
{
|
||||
lane_hash[l] = fnv_offset_basis;
|
||||
for (uint32_t i = 0; i < num_regs; ++i)
|
||||
lane_hash[l] = fnv1a(lane_hash[l], mix[l][i]);
|
||||
}
|
||||
|
||||
// Reduce all lanes to a single 256-bit result.
|
||||
static constexpr size_t num_words = sizeof(hash256) / sizeof(uint32_t);
|
||||
hash256 mix_hash;
|
||||
for (uint32_t& w : mix_hash.word32s)
|
||||
w = fnv_offset_basis;
|
||||
for (size_t l = 0; l < num_lanes; ++l)
|
||||
mix_hash.word32s[l % num_words] = fnv1a(mix_hash.word32s[l % num_words], lane_hash[l]);
|
||||
return le::uint32s(mix_hash);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
result hash(const epoch_context& context, int block_number, const hash256& header_hash,
|
||||
uint64_t nonce) noexcept
|
||||
{
|
||||
const uint64_t seed = keccak_progpow_64(header_hash, nonce);
|
||||
const hash256 mix_hash = hash_mix(context, block_number, seed, calculate_dataset_item_2048);
|
||||
const hash256 final_hash = keccak_progpow_256(header_hash, seed, mix_hash);
|
||||
return {final_hash, mix_hash};
|
||||
}
|
||||
|
||||
result hash(const epoch_context_full& context, int block_number, const hash256& header_hash,
|
||||
uint64_t nonce) noexcept
|
||||
{
|
||||
static const auto lazy_lookup = [](const epoch_context& context, uint32_t index) noexcept
|
||||
{
|
||||
auto* full_dataset_1024 = static_cast<const epoch_context_full&>(context).full_dataset;
|
||||
auto* full_dataset_2048 = reinterpret_cast<hash2048*>(full_dataset_1024);
|
||||
hash2048& item = full_dataset_2048[index];
|
||||
if (item.word64s[0] == 0)
|
||||
{
|
||||
// TODO: Copy elision here makes it thread-safe?
|
||||
item = calculate_dataset_item_2048(context, index);
|
||||
}
|
||||
|
||||
return item;
|
||||
};
|
||||
|
||||
const uint64_t seed = keccak_progpow_64(header_hash, nonce);
|
||||
const hash256 mix_hash = hash_mix(context, block_number, seed, lazy_lookup);
|
||||
const hash256 final_hash = keccak_progpow_256(header_hash, seed, mix_hash);
|
||||
return {final_hash, mix_hash};
|
||||
}
|
||||
|
||||
bool verify(const epoch_context& context, int block_number, const hash256& header_hash,
|
||||
const hash256& mix_hash, uint64_t nonce, const hash256& boundary) noexcept
|
||||
{
|
||||
const uint64_t seed = keccak_progpow_64(header_hash, nonce);
|
||||
const hash256 final_hash = keccak_progpow_256(header_hash, seed, mix_hash);
|
||||
if (!is_less_or_equal(final_hash, boundary))
|
||||
return false;
|
||||
|
||||
const hash256 expected_mix_hash =
|
||||
hash_mix(context, block_number, seed, calculate_dataset_item_2048);
|
||||
return is_equal(expected_mix_hash, mix_hash);
|
||||
}
|
||||
|
||||
search_result search_light(const epoch_context& context, int block_number,
|
||||
const hash256& header_hash, const hash256& boundary, uint64_t start_nonce,
|
||||
size_t iterations) noexcept
|
||||
{
|
||||
const uint64_t end_nonce = start_nonce + iterations;
|
||||
for (uint64_t nonce = start_nonce; nonce < end_nonce; ++nonce)
|
||||
{
|
||||
result r = hash(context, block_number, header_hash, nonce);
|
||||
if (is_less_or_equal(r.final_hash, boundary))
|
||||
return {r, nonce};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
search_result search(const epoch_context_full& context, int block_number,
|
||||
const hash256& header_hash, const hash256& boundary, uint64_t start_nonce,
|
||||
size_t iterations) noexcept
|
||||
{
|
||||
const uint64_t end_nonce = start_nonce + iterations;
|
||||
for (uint64_t nonce = start_nonce; nonce < end_nonce; ++nonce)
|
||||
{
|
||||
result r = hash(context, block_number, header_hash, nonce);
|
||||
if (is_less_or_equal(r.final_hash, boundary))
|
||||
return {r, nonce};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace progpow
|
||||
33
contrib/ethereum/libethash/support/attributes.h
Normal file
33
contrib/ethereum/libethash/support/attributes.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
|
||||
* Copyright 2018 Pawel Bylica.
|
||||
* Licensed under the Apache License, Version 2.0. See the LICENSE file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** inline */
|
||||
#if _MSC_VER || __STDC_VERSION__
|
||||
#define INLINE inline
|
||||
#else
|
||||
#define INLINE
|
||||
#endif
|
||||
|
||||
/** [[always_inline]] */
|
||||
#if _MSC_VER
|
||||
#define ALWAYS_INLINE __forceinline
|
||||
#elif defined(__has_attribute) && __STDC_VERSION__
|
||||
#if __has_attribute(always_inline)
|
||||
#define ALWAYS_INLINE __attribute__((always_inline))
|
||||
#endif
|
||||
#endif
|
||||
#if !defined(ALWAYS_INLINE)
|
||||
#define ALWAYS_INLINE
|
||||
#endif
|
||||
|
||||
/** [[no_sanitize()]] */
|
||||
#if __clang__
|
||||
#define NO_SANITIZE(sanitizer) \
|
||||
__attribute__((no_sanitize(sanitizer)))
|
||||
#else
|
||||
#define NO_SANITIZE(sanitizer)
|
||||
#endif
|
||||
|
|
@ -49,6 +49,7 @@ file(GLOB_RECURSE DAEMON daemon/*)
|
|||
|
||||
file(GLOB_RECURSE P2P p2p/*)
|
||||
file(GLOB_RECURSE RPC rpc/*)
|
||||
file(GLOB_RECURSE STRATUM stratum/*)
|
||||
file(GLOB_RECURSE SIMPLEWALLET simplewallet/*)
|
||||
file(GLOB_RECURSE CONN_TOOL connectivity_tool/*)
|
||||
file(GLOB_RECURSE WALLET wallet/*)
|
||||
|
|
@ -73,6 +74,7 @@ source_group(currency_protocol FILES ${CURRENCY_PROTOCOL})
|
|||
source_group(daemon FILES ${DAEMON})
|
||||
source_group(p2p FILES ${P2P})
|
||||
source_group(rpc FILES ${RPC})
|
||||
source_group(stratum FILES ${STRATUM})
|
||||
source_group(simplewallet FILES ${SIMPLEWALLET})
|
||||
source_group(connectivity-tool FILES ${CONN_TOOL})
|
||||
source_group(wallet FILES ${WALLET})
|
||||
|
|
@ -106,6 +108,10 @@ add_library(rpc ${RPC})
|
|||
add_dependencies(rpc version ${PCH_LIB_NAME})
|
||||
ENABLE_SHARED_PCH(RPC)
|
||||
|
||||
add_library(stratum ${STRATUM})
|
||||
add_dependencies(stratum version ${PCH_LIB_NAME})
|
||||
ENABLE_SHARED_PCH(STRATUM)
|
||||
|
||||
add_library(wallet ${WALLET})
|
||||
add_dependencies(wallet version ${PCH_LIB_NAME})
|
||||
ENABLE_SHARED_PCH(WALLET)
|
||||
|
|
@ -114,23 +120,23 @@ target_link_libraries(currency_core lmdb)
|
|||
|
||||
add_executable(daemon ${DAEMON} ${P2P} ${CURRENCY_PROTOCOL})
|
||||
add_dependencies(daemon version)
|
||||
target_link_libraries(daemon rpc currency_core crypto common upnpc-static zlibstatic ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES})
|
||||
target_link_libraries(daemon rpc stratum currency_core crypto common upnpc-static zlibstatic ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES})
|
||||
ENABLE_SHARED_PCH(DAEMON)
|
||||
ENABLE_SHARED_PCH_EXECUTABLE(daemon)
|
||||
|
||||
add_executable(connectivity_tool ${CONN_TOOL})
|
||||
add_dependencies(connectivity_tool version)
|
||||
target_link_libraries(connectivity_tool currency_core crypto common zlibstatic ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES})
|
||||
target_link_libraries(connectivity_tool currency_core crypto common zlibstatic ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES})
|
||||
ENABLE_SHARED_PCH(CONN_TOOL)
|
||||
ENABLE_SHARED_PCH_EXECUTABLE(connectivity_tool)
|
||||
|
||||
add_executable(simplewallet ${SIMPLEWALLET})
|
||||
add_dependencies(simplewallet version)
|
||||
target_link_libraries(simplewallet wallet rpc currency_core crypto common zlibstatic ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES})
|
||||
target_link_libraries(simplewallet wallet rpc currency_core crypto common zlibstatic ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES})
|
||||
ENABLE_SHARED_PCH(SIMPLEWALLET)
|
||||
ENABLE_SHARED_PCH_EXECUTABLE(simplewallet)
|
||||
|
||||
set_property(TARGET common crypto currency_core rpc wallet PROPERTY FOLDER "libs")
|
||||
set_property(TARGET common crypto currency_core rpc stratum wallet PROPERTY FOLDER "libs")
|
||||
set_property(TARGET daemon simplewallet connectivity_tool PROPERTY FOLDER "prog")
|
||||
set_property(TARGET daemon PROPERTY OUTPUT_NAME "zanod")
|
||||
|
||||
|
|
@ -150,7 +156,7 @@ if(BUILD_GUI)
|
|||
QT5_USE_MODULES(Zano WebEngineWidgets WebChannel)
|
||||
find_package(Qt5PrintSupport REQUIRED)
|
||||
|
||||
target_link_libraries(Zano wallet rpc currency_core crypto common zlibstatic Qt5::WebEngineWidgets Qt5::PrintSupport ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES})
|
||||
target_link_libraries(Zano wallet rpc currency_core crypto common zlibstatic ethash Qt5::WebEngineWidgets Qt5::PrintSupport ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES})
|
||||
|
||||
if(APPLE)
|
||||
target_link_libraries(Zano ${COCOA_LIBRARY})
|
||||
|
|
|
|||
|
|
@ -1,136 +0,0 @@
|
|||
// keccak.c
|
||||
// 19-Nov-11 Markku-Juhani O. Saarinen <mjos@iki.fi>
|
||||
// A baseline Keccak (3rd round) implementation.
|
||||
|
||||
// Memory-hard extension of keccak for PoW
|
||||
// Copyright (c) 2014 The Boolberry developers
|
||||
// Copyright (c) 2019 The Hyle Team
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
||||
#include "wild_keccak.h"
|
||||
#include "include_base_utils.h"
|
||||
namespace crypto
|
||||
{
|
||||
|
||||
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 regular_f::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];
|
||||
}
|
||||
}
|
||||
|
||||
bool generate_scratchpad(const crypto::hash& seed_data, std::vector<crypto::hash>& result_data, uint64_t target_size)
|
||||
{
|
||||
result_data.resize(target_size);
|
||||
result_data[0] = crypto::cn_fast_hash(&seed_data, sizeof(seed_data));
|
||||
for (size_t i = 1; i < target_size; i++)
|
||||
{
|
||||
result_data[i] = crypto::cn_fast_hash(&result_data[i - 1], sizeof(result_data[i - 1]));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool generate_scratchpad_light(const crypto::hash& seed_data, std::vector<crypto::hash>& result_data, uint64_t target_size)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(target_size % 10 == 0, "wrong target_size = " << target_size);
|
||||
result_data.reserve(target_size/10);
|
||||
result_data.push_back(crypto::cn_fast_hash(&seed_data, sizeof(seed_data)));
|
||||
crypto::hash prev_hash = result_data[0];
|
||||
for (size_t i = 1; i < target_size; i++)
|
||||
{
|
||||
prev_hash = crypto::cn_fast_hash(&prev_hash, sizeof(prev_hash));
|
||||
if (!(i % 10))
|
||||
{
|
||||
result_data.push_back(prev_hash);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool get_wild_keccak_light(const std::string& bd, crypto::hash& res, const std::vector<crypto::hash>& scratchpad_light)
|
||||
{
|
||||
if (!scratchpad_light.size())
|
||||
return false;
|
||||
auto light_scr_accessor = [&](uint64_t i)
|
||||
{
|
||||
//get index of int64 item in scratchpad from i, where is is random number in whole uint64_t range
|
||||
uint64_t int64_mod_index = i%(scratchpad_light.size() * 10 * 4);
|
||||
//get related hash index
|
||||
uint64_t hash_index = int64_mod_index / 4;
|
||||
//get_in hash index (from 0 to 3)
|
||||
uint64_t in_hash_index = int64_mod_index % 4;
|
||||
|
||||
//get index of primary hash in scratchpad_light
|
||||
uint64_t primary_item_index = (hash_index - (hash_index % 10)) / 10;
|
||||
uint64_t sha_count = hash_index % 10;
|
||||
crypto::hash res = scratchpad_light[primary_item_index];
|
||||
for (uint64_t i = 0; i != sha_count; i++)
|
||||
{
|
||||
res = cn_fast_hash(&res, sizeof(res));
|
||||
}
|
||||
return ((uint64_t*)&res)[in_hash_index];
|
||||
};
|
||||
return get_wild_keccak_light(bd, res, light_scr_accessor);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,169 +0,0 @@
|
|||
// keccak.h
|
||||
// 19-Nov-11 Markku-Juhani O. Saarinen <mjos@iki.fi>
|
||||
|
||||
// Copyright (c) 2014 The Boolberry developers
|
||||
// Copyright (c) 2019 The Hyle Team
|
||||
// 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 <string.h>
|
||||
#include "crypto.h"
|
||||
|
||||
extern "C" {
|
||||
//#include "crypto/alt/KeccakNISTInterface.h"
|
||||
}
|
||||
|
||||
#ifndef KECCAK_ROUNDS
|
||||
#define KECCAK_ROUNDS 24
|
||||
#endif
|
||||
|
||||
#ifndef ROTL64
|
||||
#define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 - (y))))
|
||||
#endif
|
||||
|
||||
|
||||
#define KK_MIXIN_SIZE 24
|
||||
|
||||
namespace crypto
|
||||
{
|
||||
typedef uint64_t state_t_m[25];
|
||||
typedef uint64_t mixin_t[KK_MIXIN_SIZE];
|
||||
|
||||
template<class f_traits, class callback_t>
|
||||
int wild_keccak2(const uint8_t *in, size_t inlen, uint8_t *md, size_t mdlen, callback_t cb)
|
||||
{
|
||||
state_t_m st;
|
||||
uint8_t temp[144];
|
||||
uint64_t rsiz, rsizw;
|
||||
|
||||
rsiz = sizeof(state_t_m) == mdlen ? HASH_DATA_AREA : 200 - 2 * mdlen;
|
||||
rsizw = rsiz / 8;
|
||||
memset(&st[0], 0, 25 * sizeof(st[0]));
|
||||
|
||||
|
||||
for (; inlen >= rsiz; inlen -= rsiz, in += rsiz)
|
||||
{
|
||||
for (size_t i = 0; i < rsizw; i++)
|
||||
st[i] ^= ((uint64_t *)in)[i];
|
||||
|
||||
for (int keccak_round = 0; keccak_round != KECCAK_ROUNDS; keccak_round++)
|
||||
{
|
||||
f_traits::keccakf(st, keccak_round);
|
||||
cb(st);
|
||||
}
|
||||
}
|
||||
|
||||
// last block and padding
|
||||
memcpy(temp, in, inlen);
|
||||
temp[inlen++] = 1;
|
||||
memset(temp + inlen, 0, rsiz - inlen);
|
||||
temp[rsiz - 1] |= 0x80;
|
||||
|
||||
for (size_t i = 0; i < rsizw; i++)
|
||||
st[i] ^= ((uint64_t *)temp)[i];
|
||||
|
||||
for (int keccak_round = 0; keccak_round != KECCAK_ROUNDS; keccak_round++)
|
||||
{
|
||||
f_traits::keccakf(st, keccak_round);
|
||||
cb(st);
|
||||
}
|
||||
|
||||
memcpy(md, st, mdlen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<class f_traits, class callback_t>
|
||||
int wild_keccak2_dbl(const uint8_t *in, size_t inlen, uint8_t *md, size_t mdlen, callback_t cb)
|
||||
{
|
||||
//Satoshi's classic
|
||||
wild_keccak2<f_traits>(in, inlen, md, mdlen, cb);
|
||||
wild_keccak2<f_traits>(md, mdlen, md, mdlen, cb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
class regular_f
|
||||
{
|
||||
public:
|
||||
static void keccakf(uint64_t st[25], int rounds);
|
||||
};
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
inline
|
||||
bool get_wild_keccak2(const std::string& bd, crypto::hash& res, const uint64_t* int_array_ptr_scratch, uint64_t int64_sz)
|
||||
{
|
||||
uint64_t count_access = 0;
|
||||
crypto::wild_keccak2_dbl<crypto::regular_f>(reinterpret_cast<const uint8_t*>(bd.data()), bd.size(), reinterpret_cast<uint8_t*>(&res), sizeof(res), [&](crypto::state_t_m& st)
|
||||
{
|
||||
++count_access;
|
||||
if (!int64_sz)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i != sizeof(st) / sizeof(st[0]); i++)
|
||||
{
|
||||
size_t depend_index = 0;
|
||||
if (i == 0)
|
||||
{
|
||||
depend_index = sizeof(st) / sizeof(st[0]) - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
depend_index = i - 1;
|
||||
}
|
||||
st[i] ^= int_array_ptr_scratch[int_array_ptr_scratch[int_array_ptr_scratch[st[depend_index] % int64_sz] % int64_sz] % int64_sz];
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
inline
|
||||
bool get_wild_keccak2(const std::string& bd, crypto::hash& res, const std::vector<crypto::hash>& scratchpad, uint64_t sz)
|
||||
{
|
||||
|
||||
const uint64_t* int_array_ptr = (const uint64_t*)&scratchpad[0];
|
||||
size_t int64_sz = sz * 4;
|
||||
return get_wild_keccak2(bd, res, int_array_ptr, int64_sz);
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
template<class t_items_accessor>
|
||||
bool get_wild_keccak_light(const std::string& bd, crypto::hash& res, t_items_accessor cb_get_item)
|
||||
{
|
||||
crypto::wild_keccak2_dbl<crypto::regular_f>(reinterpret_cast<const uint8_t*>(bd.data()), bd.size(), reinterpret_cast<uint8_t*>(&res), sizeof(res), [&](crypto::state_t_m& st)
|
||||
{
|
||||
for (size_t i = 0; i != sizeof(st) / sizeof(st[0]); i++)
|
||||
{
|
||||
size_t depend_index = 0;
|
||||
if (i == 0)
|
||||
{
|
||||
depend_index = sizeof(st) / sizeof(st[0]) - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
depend_index = i - 1;
|
||||
}
|
||||
st[i] ^= cb_get_item(cb_get_item(cb_get_item(st[depend_index])));
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool get_wild_keccak_light(const std::string& bd, crypto::hash& res, const std::vector<crypto::hash>& scratchpad_light);
|
||||
//------------------------------------------------------------------
|
||||
inline
|
||||
bool get_wild_keccak2(const std::string& bd, crypto::hash& res, const std::vector<crypto::hash>& scratchpad)
|
||||
{
|
||||
return get_wild_keccak2(bd, res, scratchpad, scratchpad.size());
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool generate_scratchpad(const crypto::hash& source_data, std::vector<crypto::hash>& result_data, uint64_t target_size);
|
||||
bool generate_scratchpad_light(const crypto::hash& seed_data, std::vector<crypto::hash>& result_data, uint64_t target_size);
|
||||
}
|
||||
|
||||
|
|
@ -98,7 +98,7 @@ namespace currency
|
|||
//cut the last timestamp word from restore_dats
|
||||
std::list<std::string> words;
|
||||
boost::split(words, restore_data_, boost::is_space());
|
||||
CHECK_AND_ASSERT_THROW_MES(words.size() == BRAINWALLET_DEFAULT_WORDS_COUNT, "Words count missmatch: " << words.size());
|
||||
CHECK_AND_ASSERT_MES(words.size() == BRAINWALLET_DEFAULT_WORDS_COUNT, false, "Words count missmatch: " << words.size());
|
||||
|
||||
std::string timestamp_word = words.back();
|
||||
words.erase(--words.end());
|
||||
|
|
|
|||
74
src/currency_core/basic_pow_helpers.cpp
Normal file
74
src/currency_core/basic_pow_helpers.cpp
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright (c) 2018-2019 Zano Project
|
||||
// Copyright (c) 2018-2019 Hyle Team
|
||||
// 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 "basic_pow_helpers.h"
|
||||
#include "currency_format_utils.h"
|
||||
#include "serialization/binary_utils.h"
|
||||
#include "serialization/stl_containers.h"
|
||||
#include "currency_core/currency_config.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "common/int-util.h"
|
||||
#include "ethereum/libethash/ethash/ethash.hpp"
|
||||
#include "ethereum/libethash/ethash/progpow.hpp"
|
||||
|
||||
namespace currency
|
||||
{
|
||||
|
||||
//--------------------------------------------------------------
|
||||
//global object
|
||||
// crypto::ethash::cache_manager cache;
|
||||
// void ethash_set_use_dag(bool use_dag)
|
||||
// {
|
||||
// cache.set_use_dag(use_dag);
|
||||
// }
|
||||
// //------------------------------------------------------------------
|
||||
// const uint8_t* ethash_get_dag(uint64_t epoch, uint64_t& dag_size)
|
||||
// {
|
||||
// return cache.get_dag(epoch, dag_size);
|
||||
// }
|
||||
//------------------------------------------------------------------
|
||||
int ethash_height_to_epoch(uint64_t height)
|
||||
{
|
||||
return height / ETHASH_EPOCH_LENGTH;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
crypto::hash get_block_longhash(uint64_t height, const crypto::hash& block_long_ash, uint64_t nonce)
|
||||
{
|
||||
int epoch = ethash_height_to_epoch(height);
|
||||
const auto& context = progpow::get_global_epoch_context_full(static_cast<int>(epoch));
|
||||
auto res_eth = progpow::hash(context, height, *(ethash::hash256*)&block_long_ash, nonce);
|
||||
crypto::hash result = currency::null_hash;
|
||||
memcpy(&result.data, &res_eth.final_hash, sizeof(res_eth.final_hash));
|
||||
return result;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
void get_block_longhash(const block& b, crypto::hash& res)
|
||||
{
|
||||
/*
|
||||
Since etherium hash has a bit different approach in minig, to adopt our code we made little hack:
|
||||
etherium hash calculates from block's hash and(!) nonce, both passed into PoW hash function.
|
||||
To achieve the same effect we make blob of data from block in normal way, but then set to zerro nonce
|
||||
inside serialized buffer, and then pass this nonce to ethash algo as a second argument, as it expected.
|
||||
*/
|
||||
blobdata bd = get_block_hashing_blob(b);
|
||||
|
||||
access_nonce_in_block_blob(bd) = 0;
|
||||
crypto::hash bl_hash = crypto::cn_fast_hash(bd.data(), bd.size());
|
||||
|
||||
res = get_block_longhash(get_block_height(b), bl_hash, b.nonce);
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
crypto::hash get_block_longhash(const block& b)
|
||||
{
|
||||
crypto::hash p = null_hash;
|
||||
get_block_longhash(b, p);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
44
src/currency_core/basic_pow_helpers.h
Normal file
44
src/currency_core/basic_pow_helpers.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) 2014-2018 Zano Project
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <typeindex>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "account.h"
|
||||
#include "include_base_utils.h"
|
||||
|
||||
#include "currency_format_utils_abstract.h"
|
||||
#include "common/crypto_stream_operators.h"
|
||||
#include "currency_protocol/currency_protocol_defs.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "difficulty.h"
|
||||
//#include "offers_services_helpers.h"
|
||||
#include "rpc/core_rpc_server_commands_defs.h"
|
||||
#include "bc_payments_id_service.h"
|
||||
#include "bc_attachments_helpers_basic.h"
|
||||
#include "blockchain_storage_basic.h"
|
||||
|
||||
#define CURRENCY_MINER_BLOCK_BLOB_NONCE_OFFSET 1
|
||||
|
||||
namespace currency
|
||||
{
|
||||
int ethash_height_to_epoch(uint64_t height);
|
||||
crypto::hash get_block_longhash(uint64_t h, const crypto::hash& block_long_ash, uint64_t nonce);
|
||||
void get_block_longhash(const block& b, crypto::hash& res);
|
||||
crypto::hash get_block_longhash(const block& b);
|
||||
|
||||
inline uint64_t& access_nonce_in_block_blob(blobdata& bd)
|
||||
{
|
||||
return *reinterpret_cast<uint64_t*>(&bd[CURRENCY_MINER_BLOCK_BLOB_NONCE_OFFSET]);
|
||||
}
|
||||
|
||||
inline const uint64_t& access_nonce_in_block_blob(const blobdata& bd)
|
||||
{
|
||||
return *reinterpret_cast<const uint64_t*>(&bd[CURRENCY_MINER_BLOCK_BLOB_NONCE_OFFSET]);
|
||||
}
|
||||
}
|
||||
|
|
@ -31,6 +31,7 @@
|
|||
#include "miner_common.h"
|
||||
#include "storages/portable_storage_template_helper.h"
|
||||
#include "common/db_backend_lmdb.h"
|
||||
#include "basic_pow_helpers.h"
|
||||
#include "version.h"
|
||||
|
||||
#undef LOG_DEFAULT_CHANNEL
|
||||
|
|
@ -99,9 +100,7 @@ blockchain_storage::blockchain_storage(tx_memory_pool& tx_pool) :m_db(std::share
|
|||
m_current_fee_median(0),
|
||||
m_current_fee_median_effective_index(0),
|
||||
m_is_reorganize_in_process(false),
|
||||
m_deinit_is_done(false),
|
||||
m_current_scratchpad_seed(currency::null_hash),
|
||||
m_current_scratchpad_seed_height(0)
|
||||
m_deinit_is_done(false)
|
||||
|
||||
|
||||
{
|
||||
|
|
@ -291,9 +290,6 @@ bool blockchain_storage::init(const std::string& config_folder, const boost::pro
|
|||
|
||||
|
||||
initialize_db_solo_options_values();
|
||||
get_seed_for_scratchpad(m_db_blocks.size(), m_current_scratchpad_seed);
|
||||
m_current_scratchpad_seed_height = m_db_blocks.size();
|
||||
|
||||
|
||||
m_services_mgr.init(config_folder, vm);
|
||||
|
||||
|
|
@ -1112,16 +1108,16 @@ uint64_t blockchain_storage::get_current_comulative_blocksize_limit() const
|
|||
return m_db_current_block_cumul_sz_limit;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool blockchain_storage::create_block_template(block& b, crypto::hash& seed,
|
||||
bool blockchain_storage::create_block_template(block& b,
|
||||
const account_public_address& miner_address,
|
||||
wide_difficulty_type& diffic,
|
||||
uint64_t& height,
|
||||
const blobdata& ex_nonce) const
|
||||
{
|
||||
return create_block_template(b, seed, miner_address, miner_address, diffic, height, ex_nonce, false, pos_entry());
|
||||
return create_block_template(b, miner_address, miner_address, diffic, height, ex_nonce, false, pos_entry());
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool blockchain_storage::create_block_template(block& b, crypto::hash& seed,
|
||||
bool blockchain_storage::create_block_template(block& b,
|
||||
const account_public_address& miner_address,
|
||||
const account_public_address& stakeholder_address,
|
||||
wide_difficulty_type& diffic,
|
||||
|
|
@ -1131,7 +1127,6 @@ bool blockchain_storage::create_block_template(block& b, crypto::hash& seed,
|
|||
const pos_entry& pe,
|
||||
fill_block_template_func_t custom_fill_block_template_func /* = nullptr */) const
|
||||
{
|
||||
seed = m_current_scratchpad_seed;
|
||||
size_t median_size;
|
||||
uint64_t already_generated_coins;
|
||||
CRITICAL_REGION_BEGIN(m_read_lock);
|
||||
|
|
@ -1456,10 +1451,7 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto::
|
|||
}
|
||||
else
|
||||
{
|
||||
crypto::hash seed = null_hash;
|
||||
get_seed_for_scratchpad_alt_chain(abei.height, seed, alt_chain);
|
||||
|
||||
proof_of_work = m_scratchpad.get_pow_hash(abei.bl, seed);
|
||||
proof_of_work = get_block_longhash(abei.bl);
|
||||
|
||||
if (!check_hash(proof_of_work, current_diff))
|
||||
{
|
||||
|
|
@ -1613,7 +1605,7 @@ bool blockchain_storage::pre_validate_relayed_block(block& bl, block_verificatio
|
|||
}
|
||||
else
|
||||
{
|
||||
proof_hash = m_scratchpad.get_pow_hash(bl, m_current_scratchpad_seed); //get_block_longhash(bl);
|
||||
proof_hash = get_block_longhash(bl); //get_block_longhash(bl);
|
||||
|
||||
if (!check_hash(proof_hash, current_diffic))
|
||||
{
|
||||
|
|
@ -1738,9 +1730,6 @@ bool blockchain_storage::get_main_block_rpc_details(uint64_t i, block_rpc_extend
|
|||
CRITICAL_REGION_LOCAL(m_read_lock);
|
||||
auto core_bei_ptr = m_db_blocks[i];
|
||||
crypto::hash id = get_block_hash(core_bei_ptr->bl);
|
||||
crypto::hash pow_seed = null_hash;
|
||||
get_seed_for_scratchpad(i, pow_seed);
|
||||
bei.pow_seed = epee::string_tools::pod_to_hex(pow_seed);
|
||||
bei.is_orphan = false;
|
||||
bei.total_fee = 0;
|
||||
bei.total_txs_size = 0;
|
||||
|
|
@ -2763,7 +2752,6 @@ bool blockchain_storage::pop_transaction_from_global_index(const transaction& tx
|
|||
CHECK_AND_ASSERT_MES(back_item->tx_id == tx_id, false, "transactions outs global index consistency broken: tx id missmatch");
|
||||
CHECK_AND_ASSERT_MES(back_item->out_no == i, false, "transactions outs global index consistency broken: in transaction index missmatch");
|
||||
m_db_outputs.pop_back_item(ot.amount);
|
||||
//do not let to exist empty m_outputs entries - this will broke scratchpad selector
|
||||
//if (!it->second.size())
|
||||
// m_db_outputs.erase(it);
|
||||
}
|
||||
|
|
@ -4301,18 +4289,13 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
|
|||
}
|
||||
else
|
||||
{
|
||||
//++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
//precaution(do we really need this check?)
|
||||
check_scratchpad();
|
||||
//++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
proof_hash = m_scratchpad.get_pow_hash(bl, m_current_scratchpad_seed);
|
||||
proof_hash = get_block_longhash(bl);
|
||||
|
||||
if (!check_hash(proof_hash, current_diffic))
|
||||
{
|
||||
LOG_ERROR("Block with id: " << id << ENDL
|
||||
<< "PoW hash: " << proof_hash << ENDL
|
||||
<< "PoW seed: " << m_current_scratchpad_seed << ENDL
|
||||
<< "unexpected difficulty: " << current_diffic);
|
||||
bvc.m_verification_failed = true;
|
||||
return false;
|
||||
|
|
@ -4575,28 +4558,16 @@ void blockchain_storage::on_block_added(const block_extended_info& bei, const cr
|
|||
{
|
||||
update_next_comulative_size_limit();
|
||||
m_timestamps_median_cache.clear();
|
||||
check_scratchpad();
|
||||
m_tx_pool.on_blockchain_inc(bei.height, id);
|
||||
TIME_MEASURE_START_PD(raise_block_core_event);
|
||||
rise_core_event(CORE_EVENT_BLOCK_ADDED, void_struct());
|
||||
TIME_MEASURE_FINISH_PD(raise_block_core_event);
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool blockchain_storage::check_scratchpad()
|
||||
{
|
||||
if(get_scratchpad_last_update_rebuild_height(m_db_blocks.size()) != m_current_scratchpad_seed_height)
|
||||
{
|
||||
get_seed_for_scratchpad(m_db_blocks.size(), m_current_scratchpad_seed);
|
||||
m_current_scratchpad_seed_height = get_scratchpad_last_update_rebuild_height(m_db_blocks.size());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
void blockchain_storage::on_block_removed(const block_extended_info& bei)
|
||||
{
|
||||
m_tx_pool.on_blockchain_dec(m_db_blocks.size() - 1, get_top_block_id());
|
||||
m_timestamps_median_cache.clear();
|
||||
check_scratchpad();
|
||||
LOG_PRINT_L2("block at height " << bei.height << " was removed from the blockchain");
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
|
|
@ -5330,42 +5301,6 @@ bool blockchain_storage::validate_alt_block_ms_input(const transaction& input_tx
|
|||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool blockchain_storage::get_seed_for_scratchpad(uint64_t height, crypto::hash& seed)const
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_read_lock);
|
||||
LOG_PRINT_MAGENTA("Get seed for scratchpad [main_chain] on height " << height, LOG_LEVEL_0);
|
||||
return get_seed_for_scratchpad_cb(height, seed, [&](uint64_t index) -> crypto::hash
|
||||
{
|
||||
return get_block_hash(m_db_blocks[index]->bl);
|
||||
});
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool blockchain_storage::get_seed_for_scratchpad_alt_chain(uint64_t height, crypto::hash& seed, const alt_chain_type& alt_chain)const
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_read_lock);
|
||||
//alt_chain: front -> mainchain, back -> alternative head
|
||||
uint64_t connection_to_main_chain = height;
|
||||
if (alt_chain.size())
|
||||
connection_to_main_chain = alt_chain.front()->second.height;
|
||||
|
||||
return get_seed_for_scratchpad_cb(height, seed, [&](uint64_t index) -> crypto::hash
|
||||
{
|
||||
if (index < connection_to_main_chain)
|
||||
{
|
||||
//addressed to main chain
|
||||
return get_block_hash(m_db_blocks[index]->bl);
|
||||
}
|
||||
else
|
||||
{
|
||||
//addressed to alt chain
|
||||
uint64_t offset = index - connection_to_main_chain;
|
||||
CHECK_AND_ASSERT_THROW_MES(offset < alt_chain.size(), "Internal error: failed to validate offset(" << offset << ") < alt_chain.size()("<< alt_chain.size() <<")");
|
||||
CHECK_AND_ASSERT_THROW_MES(alt_chain[offset]->second.height == index, "Internal error: failed to validate offset(" << offset << ") < alt_chain.size()(" << alt_chain.size() << ")");
|
||||
return get_block_hash(alt_chain[offset]->second.bl);
|
||||
}
|
||||
});
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool blockchain_storage::get_transaction_from_pool_or_db(const crypto::hash& tx_id, std::shared_ptr<transaction>& tx_ptr, uint64_t min_allowed_block_height /* = 0 */) const
|
||||
{
|
||||
tx_ptr.reset(new transaction());
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
#include "dispatch_core_events.h"
|
||||
#include "bc_attachments_service_manager.h"
|
||||
#include "common/median_db_cache.h"
|
||||
#include "scratchpad_helper.h"
|
||||
|
||||
|
||||
|
||||
MARK_AS_POD_C11(crypto::key_image);
|
||||
|
|
@ -231,8 +231,8 @@ namespace currency
|
|||
wide_difficulty_type get_cached_next_difficulty(bool pos) const;
|
||||
|
||||
typedef bool fill_block_template_func_t(block &bl, bool pos, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t height);
|
||||
bool create_block_template(block& b, crypto::hash& seed, const account_public_address& miner_address, const account_public_address& stakeholder_address, wide_difficulty_type& di, uint64_t& height, const blobdata& ex_nonce, bool pos, const pos_entry& pe, fill_block_template_func_t custom_fill_block_template_func = nullptr) const;
|
||||
bool create_block_template(block& b, crypto::hash& seed, const account_public_address& miner_address, wide_difficulty_type& di, uint64_t& height, const blobdata& ex_nonce) const;
|
||||
bool create_block_template(block& b, const account_public_address& miner_address, const account_public_address& stakeholder_address, wide_difficulty_type& di, uint64_t& height, const blobdata& ex_nonce, bool pos, const pos_entry& pe, fill_block_template_func_t custom_fill_block_template_func = nullptr) const;
|
||||
bool create_block_template(block& b, const account_public_address& miner_address, wide_difficulty_type& di, uint64_t& height, const blobdata& ex_nonce) const;
|
||||
|
||||
bool have_block(const crypto::hash& id) const;
|
||||
size_t get_total_transactions()const;
|
||||
|
|
@ -510,10 +510,7 @@ namespace currency
|
|||
//work like a cache to avoid
|
||||
mutable uint64_t m_current_fee_median;
|
||||
mutable uint64_t m_current_fee_median_effective_index;
|
||||
bool m_is_reorganize_in_process;
|
||||
mutable scratchpad_light_pool m_scratchpad; //TODO: optimization for using full scratchpad in mainchain
|
||||
crypto::hash m_current_scratchpad_seed;
|
||||
uint64_t m_current_scratchpad_seed_height;
|
||||
bool m_is_reorganize_in_process;
|
||||
mutable std::atomic<bool> m_deinit_is_done;
|
||||
|
||||
|
||||
|
|
@ -542,10 +539,6 @@ namespace currency
|
|||
bool validate_alt_block_txs(const block& b, const crypto::hash& id, std::set<crypto::key_image>& collected_keyimages, alt_block_extended_info& abei, const alt_chain_type& alt_chain, uint64_t split_height, uint64_t& ki_lookup_time_total) const;
|
||||
bool update_alt_out_indexes_for_tx_in_block(const transaction& tx, alt_block_extended_info& abei)const;
|
||||
bool get_transaction_from_pool_or_db(const crypto::hash& tx_id, std::shared_ptr<transaction>& tx_ptr, uint64_t min_allowed_block_height = 0) const;
|
||||
bool get_seed_for_scratchpad(uint64_t height, crypto::hash& seed)const ;
|
||||
bool get_seed_for_scratchpad_alt_chain(uint64_t height, crypto::hash& seed, const alt_chain_type& alt_chain) const ;
|
||||
|
||||
bool check_scratchpad();
|
||||
|
||||
bool prevalidate_miner_transaction(const block& b, uint64_t height, bool pos)const;
|
||||
bool validate_transaction(const block& b, uint64_t height, const transaction& tx)const;
|
||||
|
|
@ -572,7 +565,6 @@ namespace currency
|
|||
uint64_t get_adjusted_time()const;
|
||||
bool complete_timestamps_vector(uint64_t start_height, std::vector<uint64_t>& timestamps);
|
||||
bool update_next_comulative_size_limit();
|
||||
//bool get_block_for_scratchpad_alt(uint64_t connection_height, uint64_t block_index, std::list<blockchain_storage::blocks_ext_by_hash::iterator>& alt_chain, block & b);
|
||||
bool process_blockchain_tx_extra(const transaction& tx);
|
||||
bool unprocess_blockchain_tx_extra(const transaction& tx);
|
||||
bool process_blockchain_tx_attachments(const transaction& tx, uint64_t h, const crypto::hash& bl_id, uint64_t timestamp);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#define CURRENCY_FORMATION_VERSION 76
|
||||
#define CURRENCY_FORMATION_VERSION 78
|
||||
|
||||
|
||||
#define CURRENCY_MAX_BLOCK_NUMBER 500000000
|
||||
|
|
@ -46,12 +46,6 @@
|
|||
#define BASE_REWARD_DUST_THRESHOLD ((uint64_t)1000000) // pow(10, 6) - change this will cause hard-fork!
|
||||
#define DEFAULT_DUST_THRESHOLD ((uint64_t)0)//((uint64_t)100000) // pow(10, 5)
|
||||
|
||||
#define CURRENCY_SCRATCHPAD_BASE_SIZE 16777210 //count in crypto::hash, to get size in bytes x32
|
||||
#define CURRENCY_SCRATCHPAD_REBUILD_INTERVAL 720 //once a day if block goes once in 2 minute
|
||||
#define CURRENCY_SCRATCHPAD_BASE_INDEX_ID_OFFSET 20 //offset down from last rebuild height to block id, that used for indexing seed blocks in CURRENCY_SCRATCHPAD_SEED_BLOCKS_WINDOW
|
||||
#define CURRENCY_SCRATCHPAD_SEED_BLOCKS_WINDOW 700 //window for addressing seed block ids
|
||||
#define CURRENCY_SCRATCHPAD_GENESIS_SEED "4c98962ddce32c7763bb9326933a4692975ca29a76349ae7a139faa3430cc5ab"
|
||||
|
||||
#define TX_DEFAULT_FEE ((uint64_t)100000) // pow(10, 5)
|
||||
#define TX_MINIMUM_FEE ((uint64_t)100000) // pow(10, 5)
|
||||
|
||||
|
|
|
|||
|
|
@ -401,14 +401,9 @@ namespace currency
|
|||
return m_mempool.add_tx(tx, tx_hash, blob_size, tvc, kept_by_block);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_block_template(block& b, crypto::hash& seed, const account_public_address& adr, const account_public_address& stakeholder_address, wide_difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce, bool pos, const pos_entry& pe)
|
||||
{
|
||||
return m_blockchain_storage.create_block_template(b, seed, adr, stakeholder_address, diffic, height, ex_nonce, pos, pe);
|
||||
}
|
||||
bool core::get_block_template(block& b, const account_public_address& adr, const account_public_address& stakeholder_address, wide_difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce, bool pos, const pos_entry& pe)
|
||||
{
|
||||
crypto::hash seed_subst = currency::null_hash;
|
||||
return m_blockchain_storage.create_block_template(b, seed_subst, adr, stakeholder_address, diffic, height, ex_nonce, pos, pe);
|
||||
return m_blockchain_storage.create_block_template(b, adr, stakeholder_address, diffic, height, ex_nonce, pos, pe);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const
|
||||
|
|
|
|||
|
|
@ -51,9 +51,7 @@ namespace currency
|
|||
|
||||
//-------------------- i_miner_handler -----------------------
|
||||
virtual bool handle_block_found(const block& b, block_verification_context* p_verification_result = nullptr);
|
||||
virtual bool get_block_template(block& b, crypto::hash& seed, const account_public_address& adr, const account_public_address& stakeholder_address, wide_difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce, bool pos = false, const pos_entry& pe = pos_entry());
|
||||
|
||||
bool get_block_template(block& b, const account_public_address& adr, const account_public_address& stakeholder_address, wide_difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce, bool pos = false, const pos_entry& pe = pos_entry());
|
||||
virtual bool get_block_template(block& b, const account_public_address& adr, const account_public_address& stakeholder_address, wide_difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce, bool pos = false, const pos_entry& pe = pos_entry());
|
||||
|
||||
miner& get_miner(){ return m_miner; }
|
||||
static void init_options(boost::program_options::options_description& desc);
|
||||
|
|
|
|||
|
|
@ -1212,7 +1212,7 @@ namespace currency
|
|||
//---------------------------------------------------------------
|
||||
std::string get_word_from_timstamp(uint64_t timestamp)
|
||||
{
|
||||
uint64_t date_offset = timestamp ? timestamp - WALLET_BRAIN_DATE_OFFSET : 0;
|
||||
uint64_t date_offset = timestamp > WALLET_BRAIN_DATE_OFFSET ? timestamp - WALLET_BRAIN_DATE_OFFSET : 0;
|
||||
uint64_t weeks_count = date_offset / WALLET_BRAIN_DATE_QUANTUM;
|
||||
CHECK_AND_ASSERT_THROW_MES(weeks_count < std::numeric_limits<uint32_t>::max(), "internal error: unable to converto to uint32, val = " << weeks_count);
|
||||
uint32_t weeks_count_32 = static_cast<uint32_t>(weeks_count);
|
||||
|
|
@ -1664,24 +1664,7 @@ namespace currency
|
|||
}
|
||||
return true;
|
||||
}
|
||||
// //--------------------------------------------------------------
|
||||
// crypto::hash get_block_longhash(uint64_t height, const crypto::hash& block_long_ash, uint64_t nonce)
|
||||
// {
|
||||
// //TODO: add wild keccak 2
|
||||
// return null_hash;//TODO;
|
||||
// }
|
||||
// //---------------------------------------------------------------
|
||||
// void get_block_longhash(const block& b, crypto::hash& res)
|
||||
// {
|
||||
// //TODO: add wild keccak 2
|
||||
// }
|
||||
// //---------------------------------------------------------------
|
||||
// crypto::hash get_block_longhash(const block& b)
|
||||
// {
|
||||
// crypto::hash p = null_hash;
|
||||
// get_block_longhash(b, p);
|
||||
// return p;
|
||||
// }
|
||||
|
||||
//---------------------------------------------------------------
|
||||
uint64_t get_alias_coast_from_fee(const std::string& alias, uint64_t median_fee)
|
||||
{
|
||||
|
|
@ -2405,21 +2388,6 @@ namespace currency
|
|||
return CURRENCY_TESTNET_CONST_REWARD;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
uint64_t get_scratchpad_last_update_rebuild_height(uint64_t h)
|
||||
{
|
||||
return h - (h%CURRENCY_SCRATCHPAD_REBUILD_INTERVAL);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
uint64_t get_scratchpad_size_for_height(uint64_t h)
|
||||
{
|
||||
if (h < CURRENCY_BLOCKS_PER_DAY * 7)
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
//let's have ~250MB/year if block interval is 2 minutes
|
||||
return CURRENCY_SCRATCHPAD_BASE_SIZE + get_scratchpad_last_update_rebuild_height(h)*30;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool get_block_reward(bool is_pos, size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward, uint64_t height)
|
||||
{
|
||||
uint64_t base_reward = get_base_block_reward(is_pos, already_generated_coins, height);
|
||||
|
|
|
|||
|
|
@ -316,11 +316,6 @@ namespace currency
|
|||
bool parse_amount(uint64_t& amount, const std::string& str_amount);
|
||||
|
||||
|
||||
|
||||
// crypto::hash get_block_longhash(uint64_t h, const crypto::hash& block_long_ash, uint64_t nonce);
|
||||
// void get_block_longhash(const block& b, crypto::hash& res);
|
||||
// crypto::hash get_block_longhash(const block& b);
|
||||
|
||||
bool unserialize_block_complete_entry(const COMMAND_RPC_GET_BLOCKS_FAST::response& serialized,
|
||||
COMMAND_RPC_GET_BLOCKS_DIRECT::response& unserialized);
|
||||
|
||||
|
|
@ -410,8 +405,6 @@ namespace currency
|
|||
size_t get_max_tx_size();
|
||||
bool get_block_reward(bool is_pos, size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward, uint64_t height);
|
||||
uint64_t get_base_block_reward(bool is_pos, uint64_t already_generated_coins, uint64_t height);
|
||||
uint64_t get_scratchpad_last_update_rebuild_height(uint64_t h);
|
||||
uint64_t get_scratchpad_size_for_height(uint64_t h);
|
||||
bool is_payment_id_size_ok(const payment_id_t& payment_id);
|
||||
std::string get_account_address_as_str(const account_public_address& addr);
|
||||
std::string get_account_address_and_payment_id_as_str(const account_public_address& addr, const payment_id_t& payment_id);
|
||||
|
|
@ -542,41 +535,6 @@ namespace currency
|
|||
}
|
||||
return false;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
template<class block_chain_accessor_t>
|
||||
bool get_seed_for_scratchpad_cb(uint64_t height, crypto::hash& seed, block_chain_accessor_t cb)
|
||||
{
|
||||
//CHECK_AND_ASSERT_THROW_MES(m_db_blocks.size() > height, "Internal error: m_db_blocks.size()=" << m_db_blocks.size() << " > height=" << height);
|
||||
uint64_t last_upd_h = get_scratchpad_last_update_rebuild_height(height);
|
||||
std::vector<crypto::hash> seed_data;
|
||||
if (last_upd_h == 0)
|
||||
{
|
||||
crypto::hash genesis_seed = null_hash;
|
||||
bool r = epee::string_tools::hex_to_pod(CURRENCY_SCRATCHPAD_GENESIS_SEED, genesis_seed);
|
||||
CHECK_AND_ASSERT_THROW_MES(r, "Unable to parse CURRENCY_SCRATCHPAD_GENESIS_SEED " << CURRENCY_SCRATCHPAD_GENESIS_SEED);
|
||||
LOG_PRINT_MAGENTA("[SCRATCHPAD] GENESIS SEED SELECTED: " << genesis_seed, LOG_LEVEL_0);
|
||||
seed = genesis_seed;
|
||||
return true;
|
||||
}
|
||||
uint64_t low_bound_window = 0;
|
||||
CHECK_AND_ASSERT_THROW_MES(last_upd_h >= CURRENCY_SCRATCHPAD_SEED_BLOCKS_WINDOW, "Internal error: last_upd_h(" << last_upd_h << ") < CURRENCY_SCRATCHPAD_SEED_BLOCKS_WINDOW(" << CURRENCY_SCRATCHPAD_SEED_BLOCKS_WINDOW << ")");
|
||||
low_bound_window = last_upd_h - CURRENCY_SCRATCHPAD_SEED_BLOCKS_WINDOW;
|
||||
|
||||
crypto::hash selector_id = cb(last_upd_h - CURRENCY_SCRATCHPAD_BASE_INDEX_ID_OFFSET);
|
||||
|
||||
const uint64_t* pselectors = (const uint64_t*)&selector_id;
|
||||
std::stringstream ss;
|
||||
for (size_t i = 0; i != 4; i++)
|
||||
{
|
||||
seed_data.push_back(cb(low_bound_window + pselectors[i] % CURRENCY_SCRATCHPAD_SEED_BLOCKS_WINDOW));
|
||||
ss << "[" << std::setw(8) << std::hex << pselectors[i] << "->" << low_bound_window + pselectors[i] % CURRENCY_SCRATCHPAD_SEED_BLOCKS_WINDOW << "]" << seed_data.back() << ENDL;
|
||||
}
|
||||
seed = crypto::cn_fast_hash(&seed_data[0], sizeof(seed_data[0]) * seed_data.size());
|
||||
|
||||
LOG_PRINT_MAGENTA("[SCRATCHPAD] SEED SELECTED: h = " << last_upd_h << ", selector: " << selector_id << ENDL << ss.str() << "SEED: " << seed, LOG_LEVEL_0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// 62387455827 -> 455827 + 7000000 + 80000000 + 300000000 + 2000000000 + 60000000000, where 455827 <= dust_threshold
|
||||
|
|
|
|||
|
|
@ -114,12 +114,12 @@ namespace currency {
|
|||
return false;
|
||||
// usual slow check
|
||||
boost::multiprecision::uint512_t hashVal = 0;
|
||||
for (int i = 1; i < 4; i++)
|
||||
{ // highest word is zero
|
||||
hashVal |= swap64le(((const uint64_t *)&h)[3 - i]);
|
||||
hashVal << 64;
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
hashVal <<= 64;
|
||||
hashVal |= swap64le(((const uint64_t *) &h)[3-i]);
|
||||
}
|
||||
return (hashVal * difficulty > max256bit);
|
||||
return (hashVal * difficulty <= max256bit);
|
||||
}
|
||||
|
||||
uint64_t difficulty_to_boundary(wide_difficulty_type difficulty)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include "string_coding.h"
|
||||
#include "version.h"
|
||||
#include "storages/portable_storage_template_helper.h"
|
||||
#include "basic_pow_helpers.h"
|
||||
|
||||
using namespace epee;
|
||||
|
||||
|
|
@ -64,16 +65,14 @@ namespace currency
|
|||
stop();
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool miner::set_block_template(const block& bl, const wide_difficulty_type& di, uint64_t height, const crypto::hash& seed)
|
||||
bool miner::set_block_template(const block& bl, const wide_difficulty_type& di, uint64_t height)
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_template_lock);
|
||||
m_template = bl;
|
||||
m_diffic = di;
|
||||
m_height = height;
|
||||
m_seed = seed;
|
||||
++m_template_no;
|
||||
m_starter_nonce = crypto::rand<uint32_t>();
|
||||
m_scratchpad.generate(m_seed, height);
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
|
|
@ -96,13 +95,12 @@ namespace currency
|
|||
{
|
||||
extra_nonce += std::string("|") + m_extra_messages[m_config.current_extra_message_index];
|
||||
}
|
||||
crypto::hash seed = null_hash;
|
||||
if(!m_phandler->get_block_template(bl, seed, m_mine_address, m_mine_address, di, height, extra_nonce))
|
||||
if(!m_phandler->get_block_template(bl, m_mine_address, m_mine_address, di, height, extra_nonce))
|
||||
{
|
||||
LOG_ERROR("Failed to get_block_template()");
|
||||
return false;
|
||||
}
|
||||
set_block_template(bl, di, height, seed);
|
||||
set_block_template(bl, di, height);
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
|
|
@ -309,8 +307,8 @@ namespace currency
|
|||
wide_difficulty_type local_diff = 0;
|
||||
uint32_t local_template_ver = 0;
|
||||
blobdata local_blob_data;
|
||||
crypto::hash local_seed = null_hash;
|
||||
uint64_t local_height = 0;
|
||||
crypto::hash local_blob_data_hash = null_hash;
|
||||
|
||||
//uint64_t local_template_height = 0;
|
||||
block b;
|
||||
|
|
@ -327,14 +325,15 @@ namespace currency
|
|||
{
|
||||
CRITICAL_REGION_BEGIN(m_template_lock);
|
||||
b = m_template;
|
||||
b.nonce = 0;
|
||||
local_diff = m_diffic;
|
||||
local_seed = m_seed;
|
||||
local_height = m_height;
|
||||
CRITICAL_REGION_END();
|
||||
//local_template_height = get_block_height(b);
|
||||
local_template_ver = m_template_no;
|
||||
nonce = m_starter_nonce + th_local_index;
|
||||
local_blob_data = get_block_hashing_blob(b);
|
||||
local_blob_data_hash = crypto::cn_fast_hash(local_blob_data.data(), local_blob_data.size());
|
||||
}
|
||||
|
||||
if(!local_template_ver)//no any set_block_template call
|
||||
|
|
@ -343,14 +342,15 @@ namespace currency
|
|||
epee::misc_utils::sleep_no_w(1000);
|
||||
continue;
|
||||
}
|
||||
b.nonce = nonce;
|
||||
access_nonce_in_block_blob(local_blob_data) = b.nonce;
|
||||
crypto::hash h = m_scratchpad.get_pow_hash_from_blob(local_blob_data, local_height, local_seed);
|
||||
//b.nonce = nonce;
|
||||
//access_nonce_in_block_blob(local_blob_data) = b.nonce;
|
||||
crypto::hash h = get_block_longhash(local_height, local_blob_data_hash, nonce);
|
||||
|
||||
if(check_hash(h, local_diff))
|
||||
{
|
||||
b.nonce = nonce;
|
||||
++m_config.current_extra_message_index;
|
||||
LOG_PRINT_GREEN("Found block for difficulty: " << local_diff, LOG_LEVEL_0);
|
||||
LOG_PRINT_GREEN("Found block for difficulty: " << local_diff << ", height: " << local_height << ", PoW hash: " << h << ", local_blob_data_hash: " << local_blob_data_hash << ", nonce: " << std::hex << nonce, LOG_LEVEL_0);
|
||||
if(!m_phandler->handle_block_found(b))
|
||||
{
|
||||
--m_config.current_extra_message_index;
|
||||
|
|
|
|||
|
|
@ -14,26 +14,17 @@
|
|||
#include "difficulty.h"
|
||||
#include "math_helper.h"
|
||||
#include "blockchain_storage.h"
|
||||
#include "basic_pow_helpers.h"
|
||||
|
||||
|
||||
#define CURRENCY_MINER_BLOCK_BLOB_NONCE_OFFSET 1
|
||||
|
||||
namespace currency
|
||||
{
|
||||
|
||||
inline uint64_t& access_nonce_in_block_blob(blobdata& bd)
|
||||
{
|
||||
return *reinterpret_cast<uint64_t*>(&bd[CURRENCY_MINER_BLOCK_BLOB_NONCE_OFFSET]);
|
||||
}
|
||||
|
||||
inline const uint64_t& access_nonce_in_block_blob(const blobdata& bd)
|
||||
{
|
||||
return *reinterpret_cast<const uint64_t*>(&bd[CURRENCY_MINER_BLOCK_BLOB_NONCE_OFFSET]);
|
||||
}
|
||||
|
||||
struct i_miner_handler
|
||||
{
|
||||
virtual bool handle_block_found(const block& b, block_verification_context* p_verification_result = nullptr) = 0;
|
||||
virtual bool get_block_template(block& b, crypto::hash& seed, const account_public_address& adr, const account_public_address& stakeholder_address, wide_difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce, bool pos = false, const pos_entry& pe = pos_entry()) = 0;
|
||||
virtual bool get_block_template(block& b, const account_public_address& adr, const account_public_address& stakeholder_address, wide_difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce, bool pos = false, const pos_entry& pe = pos_entry()) = 0;
|
||||
protected:
|
||||
~i_miner_handler(){};
|
||||
};
|
||||
|
|
@ -63,17 +54,17 @@ namespace currency
|
|||
void do_print_hashrate(bool do_hr);
|
||||
|
||||
inline
|
||||
static bool find_nonce_for_given_block(block& bl, const wide_difficulty_type& diffic, uint64_t height, const crypto::hash& seed, scratchpad_keeper& sk)
|
||||
static bool find_nonce_for_given_block(block& bl, const wide_difficulty_type& diffic, uint64_t height)
|
||||
{
|
||||
bl.nonce = 0;
|
||||
blobdata bd = get_block_hashing_blob(bl);
|
||||
uint64_t& nonce_ref = access_nonce_in_block_blob(bd);
|
||||
nonce_ref = 0;
|
||||
crypto::hash bd_hash = crypto::cn_fast_hash(bd.data(), bd.size());
|
||||
//uint64_t& nonce_ref = access_nonce_in_block_blob(bd);
|
||||
//nonce_ref = 0;
|
||||
|
||||
for(; bl.nonce != std::numeric_limits<uint64_t>::max(); bl.nonce++)
|
||||
{
|
||||
nonce_ref = bl.nonce;
|
||||
|
||||
crypto::hash h = sk.get_pow_hash_from_blob(bd, height, seed);
|
||||
crypto::hash h = get_block_longhash(height, bd_hash, bl.nonce);
|
||||
if(check_hash(h, diffic))
|
||||
{
|
||||
LOG_PRINT_L0("Found nonce for block: " << get_block_hash(bl) << "[" << height << "]: PoW:" << h << "(diff:" << diffic << "), ts: " << bl.timestamp);
|
||||
|
|
@ -84,7 +75,7 @@ namespace currency
|
|||
}
|
||||
|
||||
private:
|
||||
bool set_block_template(const block& bl, const wide_difficulty_type& diffic, uint64_t height, const crypto::hash& seed);
|
||||
bool set_block_template(const block& bl, const wide_difficulty_type& diffic, uint64_t height);
|
||||
bool worker_thread();
|
||||
bool request_block_template();
|
||||
void merge_hr();
|
||||
|
|
@ -106,8 +97,6 @@ namespace currency
|
|||
std::atomic<uint32_t> m_starter_nonce;
|
||||
wide_difficulty_type m_diffic;
|
||||
std::atomic<uint64_t> m_height;
|
||||
scratchpad_keeper m_scratchpad;
|
||||
crypto::hash m_seed;
|
||||
volatile uint32_t m_thread_index;
|
||||
volatile uint32_t m_threads_total;
|
||||
std::atomic<int32_t> m_pausers_count;
|
||||
|
|
|
|||
|
|
@ -6,25 +6,3 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#define SCRATCHPAD_DEFAULT_FILENAME "scratchpad.bin"
|
||||
#define WILD_KECCAK_ADDENDUMS_ARRAY_SIZE 10
|
||||
|
||||
#pragma pack (push, 1)
|
||||
|
||||
struct export_scratchpad_hi
|
||||
{
|
||||
crypto::hash prevhash;
|
||||
uint64_t height;
|
||||
};
|
||||
struct export_addendums_array_entry
|
||||
{
|
||||
export_scratchpad_hi prev_hi;
|
||||
uint64_t add_size;
|
||||
};
|
||||
struct export_scratchpad_file_header
|
||||
{
|
||||
export_scratchpad_hi current_hi;
|
||||
export_addendums_array_entry add_arr[WILD_KECCAK_ADDENDUMS_ARRAY_SIZE];
|
||||
uint64_t scratchpad_size;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
|
|
|||
|
|
@ -1,76 +0,0 @@
|
|||
// Copyright (c) 2018-2019 Zano Project
|
||||
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
||||
#include "scratchpad_helper.h"
|
||||
#include "currency_format_utils.h"
|
||||
|
||||
|
||||
|
||||
namespace currency
|
||||
{
|
||||
scratchpad_keeper::scratchpad_keeper():m_seed(null_hash)
|
||||
{
|
||||
|
||||
}
|
||||
//------------------------------------------------------------------------------------
|
||||
bool scratchpad_keeper::generate(const crypto::hash& scr_seed, uint64_t height)
|
||||
{
|
||||
bool r = false;
|
||||
CRITICAL_REGION_BEGIN(m_lock);
|
||||
r = crypto::generate_scratchpad(scr_seed, m_scratchpad, get_scratchpad_size_for_height(height));
|
||||
if (r)
|
||||
m_seed = scr_seed;
|
||||
CRITICAL_REGION_END();
|
||||
return r;
|
||||
}
|
||||
//------------------------------------------------------------------------------------
|
||||
crypto::hash scratchpad_keeper::get_pow_hash_from_blob(const blobdata& bd, uint64_t height, const crypto::hash& scr_seed)
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_lock);
|
||||
crypto::hash res_hash = null_hash;
|
||||
if (scr_seed != m_seed || get_scratchpad_size_for_height(height) != this->size())
|
||||
{
|
||||
bool r = generate(scr_seed, height);
|
||||
CHECK_AND_ASSERT_THROW_MES(r, "Unable to generate scratchpad");
|
||||
}
|
||||
CHECK_AND_ASSERT_THROW_MES(get_scratchpad_size_for_height(height) == this->size(), "Fatal error on hash calculation: scratchpad_size=" << m_scratchpad.size() << " at height=" << height << ", scr_seed=" << scr_seed << ", m_seed=" << m_seed);
|
||||
CHECK_AND_ASSERT_THROW_MES(scr_seed == m_seed, "Fatal error on hash calculation: scratchpad_seed missmatch scr_seed=" << scr_seed << ", m_seed=" << m_seed);
|
||||
|
||||
bool res = get_wild_keccak2(bd, res_hash, m_scratchpad);
|
||||
CHECK_AND_ASSERT_THROW_MES(res, "Fatal error on hash calculation: scratchpad_size=" << m_scratchpad.size());
|
||||
return res_hash;
|
||||
}
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
uint64_t scratchpad_keeper::size()
|
||||
{
|
||||
return m_scratchpad.size();
|
||||
}
|
||||
//------------------------------------------------------------------------------------
|
||||
crypto::hash scratchpad_light_pool::get_pow_hash_from_blob(const blobdata& bd, uint64_t height, const crypto::hash& seed)
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_lock);
|
||||
std::shared_ptr<std::vector<crypto::hash>> pscr_light;
|
||||
if (!m_scratchpad_pools.get(seed, pscr_light))
|
||||
{
|
||||
LOG_PRINT_MAGENTA("Generating scratchpad light for " << seed << "["<< height <<"]", LOG_LEVEL_0);
|
||||
pscr_light.reset(new std::vector<crypto::hash>());
|
||||
bool r = crypto::generate_scratchpad_light(seed, *pscr_light, currency::get_scratchpad_size_for_height(height));
|
||||
CHECK_AND_ASSERT_THROW_MES(r, "Failed to generate_scratchpad_light");
|
||||
m_scratchpad_pools.set(seed, pscr_light);
|
||||
LOG_PRINT_MAGENTA("Generated ok", LOG_LEVEL_0);
|
||||
}
|
||||
CHECK_AND_ASSERT_THROW_MES(pscr_light->size()*10 == currency::get_scratchpad_size_for_height(height),
|
||||
"Wrong size of cached scratchpad = " << pscr_light->size() << ", expected " << currency::get_scratchpad_size_for_height(height) << " for height " << height);
|
||||
crypto::hash res = currency::null_hash;
|
||||
bool r = crypto::get_wild_keccak_light(bd, res, *pscr_light);
|
||||
CHECK_AND_ASSERT_THROW_MES(r, "Failed to get_wild_keccak_light");
|
||||
return res;
|
||||
}
|
||||
//------------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
// Copyright (c) 2014-2018 Zano Project
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
#include "crypto/wild_keccak.h"
|
||||
#include "currency_protocol/blobdatatype.h"
|
||||
#include "currency_core/currency_basic.h"
|
||||
#include "cache_helper.h"
|
||||
#include "currency_core/currency_format_utils.h"
|
||||
#include "currency_core/currency_format_utils_blocks.h"
|
||||
|
||||
namespace currency
|
||||
{
|
||||
template<class t_parent>
|
||||
class scratchpad_keeper_base
|
||||
{
|
||||
public:
|
||||
crypto::hash get_pow_hash(const block& b, const crypto::hash& scr_seed)
|
||||
{
|
||||
blobdata bl = get_block_hashing_blob(b);
|
||||
return static_cast<t_parent*>(this)->get_pow_hash_from_blob(bl, get_block_height(b), scr_seed);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class scratchpad_keeper: public scratchpad_keeper_base<scratchpad_keeper>
|
||||
{
|
||||
public:
|
||||
scratchpad_keeper();
|
||||
bool generate(const crypto::hash& seed, uint64_t height);
|
||||
crypto::hash get_pow_hash_from_blob(const blobdata& bd, uint64_t height, const crypto::hash& seed);
|
||||
uint64_t size();
|
||||
private:
|
||||
scratchpad_keeper(const scratchpad_keeper&) {}
|
||||
crypto::hash m_seed;
|
||||
std::vector<crypto::hash> m_scratchpad;
|
||||
std::recursive_mutex m_lock;
|
||||
};
|
||||
|
||||
|
||||
class scratchpad_light_pool : public scratchpad_keeper_base<scratchpad_light_pool>
|
||||
{
|
||||
public:
|
||||
scratchpad_light_pool() {}
|
||||
crypto::hash get_pow_hash_from_blob(const blobdata& bd, uint64_t height, const crypto::hash& seed);
|
||||
private:
|
||||
//map of seed to
|
||||
epee::misc_utils::cache_base<false, crypto::hash, std::shared_ptr<std::vector<crypto::hash>>, 4> m_scratchpad_pools;
|
||||
std::recursive_mutex m_lock;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ using namespace epee;
|
|||
#include "currency_core/checkpoints_create.h"
|
||||
#include "currency_core/currency_core.h"
|
||||
#include "rpc/core_rpc_server.h"
|
||||
#include "stratum/stratum_server.h"
|
||||
#include "currency_protocol/currency_protocol_handler.h"
|
||||
#include "daemon_commands_handler.h"
|
||||
#include "common/miniupnp_helper.h"
|
||||
|
|
@ -85,6 +86,7 @@ int main(int argc, char* argv[])
|
|||
nodetool::node_server<currency::t_currency_protocol_handler<currency::core> >::init_options(desc_cmd_sett);
|
||||
currency::miner::init_options(desc_cmd_sett);
|
||||
bc_services::bc_offers_service::init_options(desc_cmd_sett);
|
||||
currency::stratum_server::init_options(desc_cmd_sett);
|
||||
|
||||
|
||||
po::options_description desc_options("Allowed options");
|
||||
|
|
@ -146,8 +148,8 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// stratum server is enabled if any of its options present
|
||||
bool stratum_enabled = currency::stratum_server::should_start(vm);
|
||||
LOG_PRINT("Module folder: " << argv[0], LOG_LEVEL_0);
|
||||
|
||||
//create objects and link them
|
||||
|
|
@ -162,6 +164,9 @@ int main(int argc, char* argv[])
|
|||
daemon_cmmands_handler dch(p2psrv, rpc_server);
|
||||
tools::miniupnp_helper upnp_helper;
|
||||
//ccore.get_blockchain_storage().get_attachment_services_manager().add_service(&offers_service);
|
||||
std::shared_ptr<currency::stratum_server> stratum_server_ptr;
|
||||
if (stratum_enabled)
|
||||
stratum_server_ptr = std::make_shared<currency::stratum_server>(&ccore);
|
||||
|
||||
if (command_line::get_arg(vm, command_line::arg_show_rpc_autodoc))
|
||||
{
|
||||
|
|
@ -206,6 +211,15 @@ int main(int argc, char* argv[])
|
|||
res = ccore.init(vm);
|
||||
CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core");
|
||||
LOG_PRINT_L0("Core initialized OK");
|
||||
|
||||
if (stratum_enabled)
|
||||
{
|
||||
LOG_PRINT_L0("Initializing stratum server...");
|
||||
res = stratum_server_ptr->init(vm);
|
||||
CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize stratum server.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
auto& bcs = ccore.get_blockchain_storage();
|
||||
if (!offers_service.is_disabled() && bcs.get_current_blockchain_size() > 1 && bcs.get_top_block_id() != offers_service.get_last_seen_block_id())
|
||||
|
|
@ -231,16 +245,35 @@ int main(int argc, char* argv[])
|
|||
CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core rpc server.");
|
||||
LOG_PRINT_L0("Core rpc server started ok");
|
||||
|
||||
//start stratum only after core got initialized
|
||||
if (stratum_enabled)
|
||||
{
|
||||
LOG_PRINT_L0("Starting stratum server...");
|
||||
res = stratum_server_ptr->run(false);
|
||||
CHECK_AND_ASSERT_MES(res, 1, "Failed to start stratum server.");
|
||||
LOG_PRINT_L0("Stratum server started ok");
|
||||
}
|
||||
|
||||
tools::signal_handler::install([&dch, &p2psrv] {
|
||||
tools::signal_handler::install([&dch, &p2psrv, &stratum_server_ptr] {
|
||||
dch.stop_handling();
|
||||
p2psrv.send_stop_signal();
|
||||
if (stratum_server_ptr)
|
||||
stratum_server_ptr->send_stop_signal();
|
||||
});
|
||||
|
||||
LOG_PRINT_L0("Starting p2p net loop...");
|
||||
p2psrv.run();
|
||||
LOG_PRINT_L0("p2p net loop stopped");
|
||||
|
||||
//stop components
|
||||
if (stratum_enabled)
|
||||
{
|
||||
LOG_PRINT_L0("Stopping stratum server...");
|
||||
stratum_server_ptr->send_stop_signal();
|
||||
stratum_server_ptr->timed_wait_server_stop(1000);
|
||||
LOG_PRINT_L0("Stratum server stopped");
|
||||
}
|
||||
|
||||
LOG_PRINT_L0("Stopping core rpc server...");
|
||||
rpc_server.send_stop_signal();
|
||||
rpc_server.timed_wait_server_stop(5000);
|
||||
|
|
@ -253,6 +286,9 @@ int main(int argc, char* argv[])
|
|||
LOG_PRINT_L0("Deinitializing market...");
|
||||
(static_cast<currency::i_bc_service&>(offers_service)).deinit();
|
||||
|
||||
LOG_PRINT_L0("Deinitializing stratum server ...");
|
||||
stratum_server_ptr.reset();
|
||||
|
||||
LOG_PRINT_L0("Deinitializing rpc server ...");
|
||||
rpc_server.deinit();
|
||||
LOG_PRINT_L0("Deinitializing currency_protocol...");
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -15,7 +15,7 @@
|
|||
"BACK": "Go back"
|
||||
},
|
||||
"BREADCRUMBS": {
|
||||
"ADD_WALLET": "Add new wallet",
|
||||
"ADD_WALLET": "Add wallet",
|
||||
"CREATE_WALLET": "Create new wallet",
|
||||
"SAVE_PHRASE": "Save your seed phrase",
|
||||
"OPEN_WALLET": "Open existing wallet",
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
},
|
||||
"SIDEBAR": {
|
||||
"TITLE": "Wallets",
|
||||
"ADD_NEW": "+ Add new",
|
||||
"ADD_NEW": "+ Add",
|
||||
"ACCOUNT": {
|
||||
"STAKING": "Staking",
|
||||
"MESSAGES": "New offers/Messages",
|
||||
|
|
@ -68,6 +68,7 @@
|
|||
"FORM_ERRORS": {
|
||||
"NAME_REQUIRED": "Name is required.",
|
||||
"NAME_DUPLICATE": "Name is duplicate.",
|
||||
"MAX_LENGTH": "Maximum name length reached.",
|
||||
"CONFIRM_NOT_MATCH": "Confirm password not match."
|
||||
}
|
||||
},
|
||||
|
|
@ -80,7 +81,8 @@
|
|||
"FILE_NOT_FOUND2": "<br/><br/> It might have been renamed or moved. <br/> To open it, use the \"Open wallet\" button.",
|
||||
"FORM_ERRORS": {
|
||||
"NAME_REQUIRED": "Name is required.",
|
||||
"NAME_DUPLICATE": "Name is duplicate."
|
||||
"NAME_DUPLICATE": "Name is duplicate.",
|
||||
"MAX_LENGTH": "Maximum name length reached."
|
||||
}
|
||||
},
|
||||
"RESTORE_WALLET": {
|
||||
|
|
@ -95,6 +97,7 @@
|
|||
"FORM_ERRORS": {
|
||||
"NAME_REQUIRED": "Name is required.",
|
||||
"NAME_DUPLICATE": "Name is duplicate.",
|
||||
"MAX_LENGTH": "Maximum name length reached.",
|
||||
"CONFIRM_NOT_MATCH": "Confirm password not match.",
|
||||
"KEY_REQUIRED": "Key is required.",
|
||||
"KEY_NOT_VALID": "Key not valid."
|
||||
|
|
@ -104,11 +107,24 @@
|
|||
"TITLE": "Make sure to keep your seed phrase in a safe place. If you forget your seed phrase you will not be able to recover your wallet.",
|
||||
"BUTTON_CREATE_ACCOUNT" : "Create wallet"
|
||||
},
|
||||
"PROGRESS": {
|
||||
"ADD_WALLET": "Add wallet",
|
||||
"SELECT_LOCATION": "Select wallet location",
|
||||
"CREATE_WALLET": "Create new wallet",
|
||||
"RESTORE_WALLET": "Restore from backup"
|
||||
},
|
||||
"SETTINGS": {
|
||||
"TITLE": "Settings",
|
||||
"DARK_THEME": "Dark theme",
|
||||
"WHITE_THEME": "White theme",
|
||||
"GRAY_THEME": "Grey theme",
|
||||
"APP_LOCK": {
|
||||
"TITLE": "Lock app after:",
|
||||
"TIME1": "5 min",
|
||||
"TIME2": "15 min",
|
||||
"TIME3": "1 hour",
|
||||
"TIME4": "Never"
|
||||
},
|
||||
"MASTER_PASSWORD": {
|
||||
"TITLE": "Update master password",
|
||||
"OLD": "Old password",
|
||||
|
|
@ -145,10 +161,11 @@
|
|||
"LABEL_SEED_PHRASE": "Seed phrase",
|
||||
"SEED_PHRASE_HINT": "Click to reveal the seed phrase",
|
||||
"BUTTON_SAVE": "Save",
|
||||
"BUTTON_REMOVE": "Remove wallet",
|
||||
"BUTTON_REMOVE": "Close wallet",
|
||||
"FORM_ERRORS": {
|
||||
"NAME_REQUIRED": "Name is required.",
|
||||
"NAME_DUPLICATE": "Name is duplicate."
|
||||
"NAME_DUPLICATE": "Name is duplicate.",
|
||||
"MAX_LENGTH": "Maximum name length reached."
|
||||
}
|
||||
},
|
||||
"ASSIGN_ALIAS": {
|
||||
|
|
@ -170,7 +187,8 @@
|
|||
"NAME_WRONG": "Alias has wrong name",
|
||||
"NAME_LENGTH": "The alias must be 6-25 characters long",
|
||||
"NAME_EXISTS": "Alias name already exists",
|
||||
"NO_MONEY": "You do not have enough funds to assign this alias"
|
||||
"NO_MONEY": "You do not have enough funds to assign this alias",
|
||||
"MAX_LENGTH": "Maximum comment length reached"
|
||||
},
|
||||
"ONE_ALIAS": "You can create only one alias per wallet",
|
||||
"REQUEST_ADD_REG": "The alias will be assigned within 10 minutes"
|
||||
|
|
@ -185,7 +203,8 @@
|
|||
"PLACEHOLDER": "Enter comment"
|
||||
},
|
||||
"FORM_ERRORS": {
|
||||
"NO_MONEY": "You do not have enough funds to change the comment to this alias"
|
||||
"NO_MONEY": "You do not have enough funds to change the comment to this alias",
|
||||
"MAX_LENGTH": "Maximum comment length reached"
|
||||
},
|
||||
"COST": "Cost to edit alias {{value}} {{currency}}",
|
||||
"BUTTON_EDIT": "Edit",
|
||||
|
|
@ -202,7 +221,7 @@
|
|||
},
|
||||
"ADDRESS": {
|
||||
"LABEL": "The account to which the alias will be transferred",
|
||||
"PLACEHOLDER": "Enter account number"
|
||||
"PLACEHOLDER": "Enter wallet address"
|
||||
},
|
||||
"FORM_ERRORS": {
|
||||
"WRONG_ADDRESS": "No wallet with this account exists",
|
||||
|
|
@ -234,7 +253,7 @@
|
|||
},
|
||||
"HISTORY": {
|
||||
"STATUS": "Status",
|
||||
"STATUS_TOOLTIP": "Approved {{current}}/{{total}}",
|
||||
"STATUS_TOOLTIP": "Confirmations {{current}}/{{total}}",
|
||||
"SEND": "Send",
|
||||
"RECEIVED": "Received",
|
||||
"DATE": "Date",
|
||||
|
|
@ -336,6 +355,7 @@
|
|||
"AMOUNT": "Amount",
|
||||
"YOUR_DEPOSIT": "Your deposit",
|
||||
"SELLER_DEPOSIT": "Seller deposit",
|
||||
"BUYER_DEPOSIT": "Buyer deposit",
|
||||
"SAME_AMOUNT": "Same amount",
|
||||
"COMMENT": "Comment",
|
||||
"DETAILS": "Additional details",
|
||||
|
|
@ -347,6 +367,7 @@
|
|||
"SELLER_NOT_VALID": "Seller not valid.",
|
||||
"AMOUNT_REQUIRED": "Amount is required.",
|
||||
"YOUR_DEPOSIT_REQUIRED": "Your deposit is required.",
|
||||
"YOUR_DEPOSIT_TOO_SMALL": "Your deposit should be equal or greater than amount.",
|
||||
"SELLER_DEPOSIT_REQUIRED": "Seller deposit is required.",
|
||||
"SELLER_SAME": "The seller's and buyer's accounts are identical. The seller and buyer must use different wallet for the contract.",
|
||||
"COMMENT_MAXIMUM": "Maximum field length reached."
|
||||
|
|
@ -393,7 +414,13 @@
|
|||
"BUTTON_RECEIVED": "Item received (Transfer payment and return pledge to seller)",
|
||||
"BUTTON_CANCEL_BUYER": "Cancel contract (Return pledge)",
|
||||
"BUTTON_NOT_CANCEL": "Do not cancel (Item shipped)",
|
||||
"BUTTON_CANCEL_SELLER": "Cancel contract (Return pledge)"
|
||||
"BUTTON_CANCEL_SELLER": "Cancel contract (Return pledge)",
|
||||
"HOUR": "hour",
|
||||
"HOURS": "hours",
|
||||
"CANCEL": "Cancel",
|
||||
"NULLIFY_QUESTION": "Are you sure you want to nullify your pledge and the seller's pledge?",
|
||||
"BUTTON_NULLIFY_SHORT": "Nullify",
|
||||
"WAITING_TIME_QUESTION": "Are you sure you want to cancel the contract?"
|
||||
},
|
||||
"MESSAGES": {
|
||||
"ADDRESS": "Address",
|
||||
|
|
@ -427,7 +454,7 @@
|
|||
"CORE_BUSY": "Internal error (core is busy)",
|
||||
"DAEMON_BUSY": "Internal error: deamon is busy",
|
||||
"NO_MONEY_REMOVE_OFFER": "There is no fee for deleting an offer, but in order to protect the network against flood transactions you need to have at least {{fee}} {{currency}} in your wallet",
|
||||
"NOT_ENOUGH_OUTPUTS_TO_MIX": "For the sake of security, mixed transaction will take several days",
|
||||
"NOT_ENOUGH_OUTPUTS_TO_MIX": "Mix-in number is too big for current blockchain state. There are not enough unspent outputs to mix with",
|
||||
"TRANSACTION_IS_TO_BIG": "Transaction exceeds network limit, send required amount with multiple transactions",
|
||||
"TRANSFER_ATTEMPT": "There is no connection to Zano network",
|
||||
"ACCESS_DENIED": "Access denied",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,16 @@
|
|||
.app-content {
|
||||
|
||||
.preloader {
|
||||
|
||||
.loading-bar {
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed(progressBarFullBackgroundColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
font-family: OpenSans, sans-serif;
|
||||
|
|
@ -24,6 +37,12 @@ button {
|
|||
|
||||
&.blue-button:not(:disabled) {
|
||||
|
||||
&.error-text {
|
||||
@include themify($themes) {
|
||||
color: themed(redTextColor);
|
||||
}
|
||||
}
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed(blueButtonBackgroundColor);
|
||||
color: themed(alternativeTextColor);
|
||||
|
|
@ -143,7 +162,7 @@ button {
|
|||
height: 100%;
|
||||
min-height: 7.5rem;
|
||||
max-height: 7.5rem;
|
||||
overflow: hidden;
|
||||
overflow: auto;
|
||||
resize: none;
|
||||
|
||||
@include themify($themes) {
|
||||
|
|
@ -165,6 +184,12 @@ button {
|
|||
}
|
||||
}
|
||||
|
||||
.error-text {
|
||||
@include themify($themes) {
|
||||
color: themed(redTextColor);
|
||||
}
|
||||
}
|
||||
|
||||
input[type='radio'].style-radio {
|
||||
|
||||
& + label {
|
||||
|
|
@ -508,6 +533,11 @@ input[type='checkbox'].style-checkbox {
|
|||
}
|
||||
}
|
||||
|
||||
.account-tooltip {
|
||||
word-break: break-word;
|
||||
max-width: 18rem;
|
||||
}
|
||||
|
||||
.ngx-contextmenu {
|
||||
|
||||
.dropdown-menu {
|
||||
|
|
@ -546,6 +576,69 @@ input[type='checkbox'].style-checkbox {
|
|||
}
|
||||
}
|
||||
|
||||
.ng-select {
|
||||
|
||||
&.lock-selection-select {
|
||||
width: 100%;
|
||||
|
||||
.ng-select-container {
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
cursor: pointer;
|
||||
font-size: 1.4rem;
|
||||
outline: none;
|
||||
padding: 0 1rem;
|
||||
height: 4.2rem;
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed(inputBackgroundColor);
|
||||
color: themed(mainTextColor);
|
||||
}
|
||||
|
||||
.ng-value-container {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ng-dropdown-panel {
|
||||
border: none;
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed(inputBackgroundColor);
|
||||
}
|
||||
|
||||
.ng-dropdown-panel-items {
|
||||
|
||||
.ng-option {
|
||||
font-size: 1.4rem;
|
||||
padding: 1rem;
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed(inputBackgroundColor);
|
||||
color: themed(mainTextColor);
|
||||
}
|
||||
|
||||
&.ng-option-marked {
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed(selectHoverColor);
|
||||
color: themed(mainTextColor);
|
||||
}
|
||||
}
|
||||
|
||||
&.ng-option-selected {
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed(selectSelectedColor);
|
||||
color: themed(mainTextColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app-modal-container {
|
||||
|
||||
.modal {
|
||||
|
|
@ -635,3 +728,27 @@ app-transaction-details {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
app-progress-container {
|
||||
|
||||
.progress-bar-container {
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed(mainTextColor);
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed(progressBarBackgroundColor);
|
||||
}
|
||||
|
||||
.progress-bar-full {
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed(progressBarFullBackgroundColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ $themes: (
|
|||
turquoiseButtonHoverColor: #52d9ea,
|
||||
transparentButtonBorderColor: #2b3644,
|
||||
inputBackgroundColor: #171e27,
|
||||
selectHoverColor: rgba(58, 72, 90, 0.5),
|
||||
selectSelectedColor: rgba(43, 54, 68, 0.5),
|
||||
switchBackgroundColor: #000000,
|
||||
accountBackgroundColor: rgba(43, 54, 68, 0.5),
|
||||
accountHoverBackgroundColor: rgba(58, 72, 90, 0.5),
|
||||
|
|
@ -66,6 +68,8 @@ $themes: (
|
|||
turquoiseButtonHoverColor: #43cee0,
|
||||
transparentButtonBorderColor: #2f3438,
|
||||
inputBackgroundColor: #292d31,
|
||||
selectHoverColor: rgba(70, 76, 81, 0.5),
|
||||
selectSelectedColor: rgba(37, 40, 43, 0.5),
|
||||
switchBackgroundColor: #000000,
|
||||
accountBackgroundColor: rgba(37, 40, 43, 0.5),
|
||||
accountHoverBackgroundColor: rgba(70, 76, 81, 0.5),
|
||||
|
|
@ -110,6 +114,8 @@ $themes: (
|
|||
turquoiseButtonHoverColor: #2bbdcf,
|
||||
transparentButtonBorderColor: #ebebeb,
|
||||
inputBackgroundColor: #e6e6e6,
|
||||
selectHoverColor: rgba(240, 240, 240, 0.5),
|
||||
selectSelectedColor: rgba(224, 224, 224, 0.5),
|
||||
switchBackgroundColor: #e0e0e0,
|
||||
accountBackgroundColor: rgba(30, 136, 229, 1),
|
||||
accountHoverBackgroundColor: rgba(240, 240, 240, 0.5),
|
||||
|
|
|
|||
|
|
@ -68,6 +68,11 @@ app-wallet-details {
|
|||
app-settings {
|
||||
|
||||
.content {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
|
||||
.theme-selection {
|
||||
|
||||
|
|
@ -75,6 +80,37 @@ app-settings {
|
|||
color: themed(optionalTextColor);
|
||||
}
|
||||
}
|
||||
|
||||
.scale-selection {
|
||||
|
||||
.button-block {
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed(tabInactiveBackgroundColor);
|
||||
}
|
||||
|
||||
.label {
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed(optionalTextColor);
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed(blueTextColor);
|
||||
}
|
||||
|
||||
.label {
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed(blueTextColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -138,16 +138,37 @@ app-sidebar {
|
|||
border-bottom: 0.2rem solid themed(sidebarBorderColor);
|
||||
}
|
||||
|
||||
button {
|
||||
.wrap-button {
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed(mainTextColor);
|
||||
}
|
||||
|
||||
.icon {
|
||||
button {
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed(blueTextColor);
|
||||
color: themed(mainTextColor);
|
||||
}
|
||||
|
||||
.icon {
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed(blueTextColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
|
||||
button {
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed(accountBackgroundColor)!important;
|
||||
color: themed(accountMainTextColor)!important;
|
||||
}
|
||||
|
||||
.icon {
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed(accountIndicatorBackgroundColor)!important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
position: relative;
|
||||
cursor: pointer;
|
||||
margin-right: 20px;
|
||||
outline-style: none;
|
||||
|
||||
&:after {
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 3.2 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -649,7 +649,7 @@ module.exports = function (NAME, wrapper, methods, common, IS_MAP, IS_WEAK) {
|
|||
/*! no static exports found */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
var core = module.exports = { version: '2.6.1' };
|
||||
var core = module.exports = { version: '2.6.2' };
|
||||
if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef
|
||||
|
||||
|
||||
|
|
@ -1807,7 +1807,7 @@ var store = global[SHARED] || (global[SHARED] = {});
|
|||
})('versions', []).push({
|
||||
version: core.version,
|
||||
mode: __webpack_require__(/*! ./_library */ "./node_modules/core-js/modules/_library.js") ? 'pure' : 'global',
|
||||
copyright: '© 2018 Denis Pushkarev (zloirock.ru)'
|
||||
copyright: '© 2019 Denis Pushkarev (zloirock.ru)'
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -2364,7 +2364,6 @@ $metadata.exp({ metadata: function metadata(metadataKey, metadataValue) {
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
var Zone$1 = (function (global) {
|
||||
var FUNCTION = 'function';
|
||||
var performance = global['performance'];
|
||||
function mark(name) {
|
||||
performance && performance['mark'] && performance['mark'](name);
|
||||
|
|
@ -2373,12 +2372,26 @@ var Zone$1 = (function (global) {
|
|||
performance && performance['measure'] && performance['measure'](name, label);
|
||||
}
|
||||
mark('Zone');
|
||||
var checkDuplicate = global[('__zone_symbol__forceDuplicateZoneCheck')] === true;
|
||||
if (global['Zone']) {
|
||||
throw new Error('Zone already loaded.');
|
||||
// if global['Zone'] already exists (maybe zone.js was already loaded or
|
||||
// some other lib also registered a global object named Zone), we may need
|
||||
// to throw an error, but sometimes user may not want this error.
|
||||
// For example,
|
||||
// we have two web pages, page1 includes zone.js, page2 doesn't.
|
||||
// and the 1st time user load page1 and page2, everything work fine,
|
||||
// but when user load page2 again, error occurs because global['Zone'] already exists.
|
||||
// so we add a flag to let user choose whether to throw this error or not.
|
||||
// By default, if existing Zone is from zone.js, we will not throw the error.
|
||||
if (checkDuplicate || typeof global['Zone'].__symbol__ !== 'function') {
|
||||
throw new Error('Zone already loaded.');
|
||||
}
|
||||
else {
|
||||
return global['Zone'];
|
||||
}
|
||||
}
|
||||
var Zone = /** @class */ (function () {
|
||||
function Zone(parent, zoneSpec) {
|
||||
this._properties = null;
|
||||
this._parent = parent;
|
||||
this._name = zoneSpec ? zoneSpec.name || 'unnamed' : '<root>';
|
||||
this._properties = zoneSpec && zoneSpec.properties || {};
|
||||
|
|
@ -2421,7 +2434,9 @@ var Zone$1 = (function (global) {
|
|||
});
|
||||
Zone.__load_patch = function (name, fn) {
|
||||
if (patches.hasOwnProperty(name)) {
|
||||
throw Error('Already loaded patch: ' + name);
|
||||
if (checkDuplicate) {
|
||||
throw Error('Already loaded patch: ' + name);
|
||||
}
|
||||
}
|
||||
else if (!global['__Zone_disable_' + name]) {
|
||||
var perfName = 'Zone:' + name;
|
||||
|
|
@ -2465,7 +2480,7 @@ var Zone$1 = (function (global) {
|
|||
return this._zoneDelegate.fork(this, zoneSpec);
|
||||
};
|
||||
Zone.prototype.wrap = function (callback, source) {
|
||||
if (typeof callback !== FUNCTION) {
|
||||
if (typeof callback !== 'function') {
|
||||
throw new Error('Expecting function got: ' + callback);
|
||||
}
|
||||
var _callback = this._zoneDelegate.intercept(this, callback, source);
|
||||
|
|
@ -2475,9 +2490,6 @@ var Zone$1 = (function (global) {
|
|||
};
|
||||
};
|
||||
Zone.prototype.run = function (callback, applyThis, applyArgs, source) {
|
||||
if (applyThis === void 0) { applyThis = undefined; }
|
||||
if (applyArgs === void 0) { applyArgs = null; }
|
||||
if (source === void 0) { source = null; }
|
||||
_currentZoneFrame = { parent: _currentZoneFrame, zone: this };
|
||||
try {
|
||||
return this._zoneDelegate.invoke(this, callback, applyThis, applyArgs, source);
|
||||
|
|
@ -2488,8 +2500,6 @@ var Zone$1 = (function (global) {
|
|||
};
|
||||
Zone.prototype.runGuarded = function (callback, applyThis, applyArgs, source) {
|
||||
if (applyThis === void 0) { applyThis = null; }
|
||||
if (applyArgs === void 0) { applyArgs = null; }
|
||||
if (source === void 0) { source = null; }
|
||||
_currentZoneFrame = { parent: _currentZoneFrame, zone: this };
|
||||
try {
|
||||
try {
|
||||
|
|
@ -2513,10 +2523,7 @@ var Zone$1 = (function (global) {
|
|||
// https://github.com/angular/zone.js/issues/778, sometimes eventTask
|
||||
// will run in notScheduled(canceled) state, we should not try to
|
||||
// run such kind of task but just return
|
||||
// we have to define an variable here, if not
|
||||
// typescript compiler will complain below
|
||||
var isNotScheduled = task.state === notScheduled;
|
||||
if (isNotScheduled && task.type === eventTask) {
|
||||
if (task.state === notScheduled && (task.type === eventTask || task.type === macroTask)) {
|
||||
return;
|
||||
}
|
||||
var reEntryGuard = task.state != running;
|
||||
|
|
@ -2527,7 +2534,7 @@ var Zone$1 = (function (global) {
|
|||
_currentZoneFrame = { parent: _currentZoneFrame, zone: this };
|
||||
try {
|
||||
if (task.type == macroTask && task.data && !task.data.isPeriodic) {
|
||||
task.cancelFn = null;
|
||||
task.cancelFn = undefined;
|
||||
}
|
||||
try {
|
||||
return this._zoneDelegate.invokeTask(this, task, applyThis, applyArgs);
|
||||
|
|
@ -2563,8 +2570,7 @@ var Zone$1 = (function (global) {
|
|||
var newZone = this;
|
||||
while (newZone) {
|
||||
if (newZone === task.zone) {
|
||||
throw Error("can not reschedule task to " + this
|
||||
.name + " which is descendants of the original zone " + task.zone.name);
|
||||
throw Error("can not reschedule task to " + this.name + " which is descendants of the original zone " + task.zone.name);
|
||||
}
|
||||
newZone = newZone.parent;
|
||||
}
|
||||
|
|
@ -2594,7 +2600,7 @@ var Zone$1 = (function (global) {
|
|||
return task;
|
||||
};
|
||||
Zone.prototype.scheduleMicroTask = function (source, callback, data, customSchedule) {
|
||||
return this.scheduleTask(new ZoneTask(microTask, source, callback, data, customSchedule, null));
|
||||
return this.scheduleTask(new ZoneTask(microTask, source, callback, data, customSchedule, undefined));
|
||||
};
|
||||
Zone.prototype.scheduleMacroTask = function (source, callback, data, customSchedule, customCancel) {
|
||||
return this.scheduleTask(new ZoneTask(macroTask, source, callback, data, customSchedule, customCancel));
|
||||
|
|
@ -2635,16 +2641,14 @@ var Zone$1 = (function (global) {
|
|||
}());
|
||||
var DELEGATE_ZS = {
|
||||
name: '',
|
||||
onHasTask: function (delegate, _, target, hasTaskState) {
|
||||
return delegate.hasTask(target, hasTaskState);
|
||||
},
|
||||
onHasTask: function (delegate, _, target, hasTaskState) { return delegate.hasTask(target, hasTaskState); },
|
||||
onScheduleTask: function (delegate, _, target, task) {
|
||||
return delegate.scheduleTask(target, task);
|
||||
},
|
||||
onInvokeTask: function (delegate, _, target, task, applyThis, applyArgs) { return delegate.invokeTask(target, task, applyThis, applyArgs); },
|
||||
onCancelTask: function (delegate, _, target, task) {
|
||||
return delegate.cancelTask(target, task);
|
||||
}
|
||||
onInvokeTask: function (delegate, _, target, task, applyThis, applyArgs) {
|
||||
return delegate.invokeTask(target, task, applyThis, applyArgs);
|
||||
},
|
||||
onCancelTask: function (delegate, _, target, task) { return delegate.cancelTask(target, task); }
|
||||
};
|
||||
var ZoneDelegate = /** @class */ (function () {
|
||||
function ZoneDelegate(zone, parentDelegate, zoneSpec) {
|
||||
|
|
@ -2672,8 +2676,8 @@ var Zone$1 = (function (global) {
|
|||
zoneSpec && (zoneSpec.onHandleError ? this.zone : parentDelegate.zone);
|
||||
this._scheduleTaskZS =
|
||||
zoneSpec && (zoneSpec.onScheduleTask ? zoneSpec : parentDelegate._scheduleTaskZS);
|
||||
this._scheduleTaskDlgt =
|
||||
zoneSpec && (zoneSpec.onScheduleTask ? parentDelegate : parentDelegate._scheduleTaskDlgt);
|
||||
this._scheduleTaskDlgt = zoneSpec &&
|
||||
(zoneSpec.onScheduleTask ? parentDelegate : parentDelegate._scheduleTaskDlgt);
|
||||
this._scheduleTaskCurrZone =
|
||||
zoneSpec && (zoneSpec.onScheduleTask ? this.zone : parentDelegate.zone);
|
||||
this._invokeTaskZS =
|
||||
|
|
@ -2728,8 +2732,7 @@ var Zone$1 = (function (global) {
|
|||
callback;
|
||||
};
|
||||
ZoneDelegate.prototype.invoke = function (targetZone, callback, applyThis, applyArgs, source) {
|
||||
return this._invokeZS ?
|
||||
this._invokeZS.onInvoke(this._invokeDlgt, this._invokeCurrZone, targetZone, callback, applyThis, applyArgs, source) :
|
||||
return this._invokeZS ? this._invokeZS.onInvoke(this._invokeDlgt, this._invokeCurrZone, targetZone, callback, applyThis, applyArgs, source) :
|
||||
callback.apply(applyThis, applyArgs);
|
||||
};
|
||||
ZoneDelegate.prototype.handleError = function (targetZone, error) {
|
||||
|
|
@ -2761,8 +2764,7 @@ var Zone$1 = (function (global) {
|
|||
return returnTask;
|
||||
};
|
||||
ZoneDelegate.prototype.invokeTask = function (targetZone, task, applyThis, applyArgs) {
|
||||
return this._invokeTaskZS ?
|
||||
this._invokeTaskZS.onInvokeTask(this._invokeTaskDlgt, this._invokeTaskCurrZone, targetZone, task, applyThis, applyArgs) :
|
||||
return this._invokeTaskZS ? this._invokeTaskZS.onInvokeTask(this._invokeTaskDlgt, this._invokeTaskCurrZone, targetZone, task, applyThis, applyArgs) :
|
||||
task.callback.apply(applyThis, applyArgs);
|
||||
};
|
||||
ZoneDelegate.prototype.cancelTask = function (targetZone, task) {
|
||||
|
|
@ -2782,7 +2784,7 @@ var Zone$1 = (function (global) {
|
|||
// hasTask should not throw error so other ZoneDelegate
|
||||
// can still trigger hasTask callback
|
||||
try {
|
||||
return this._hasTaskZS &&
|
||||
this._hasTaskZS &&
|
||||
this._hasTaskZS.onHasTask(this._hasTaskDlgt, this._hasTaskCurrZone, targetZone, isEmpty);
|
||||
}
|
||||
catch (err) {
|
||||
|
|
@ -2872,14 +2874,12 @@ var Zone$1 = (function (global) {
|
|||
}
|
||||
}
|
||||
else {
|
||||
throw new Error(this.type + " '" + this.source + "': can not transition to '" + toState + "', expecting state '" + fromState1 + "'" + (fromState2 ?
|
||||
' or \'' + fromState2 + '\'' :
|
||||
'') + ", was '" + this._state + "'.");
|
||||
throw new Error(this.type + " '" + this.source + "': can not transition to '" + toState + "', expecting state '" + fromState1 + "'" + (fromState2 ? ' or \'' + fromState2 + '\'' : '') + ", was '" + this._state + "'.");
|
||||
}
|
||||
};
|
||||
ZoneTask.prototype.toString = function () {
|
||||
if (this.data && typeof this.data.handleId !== 'undefined') {
|
||||
return this.data.handleId;
|
||||
return this.data.handleId.toString();
|
||||
}
|
||||
else {
|
||||
return Object.prototype.toString.call(this);
|
||||
|
|
@ -2920,7 +2920,13 @@ var Zone$1 = (function (global) {
|
|||
}
|
||||
}
|
||||
if (nativeMicroTaskQueuePromise) {
|
||||
nativeMicroTaskQueuePromise[symbolThen](drainMicroTaskQueue);
|
||||
var nativeThen = nativeMicroTaskQueuePromise[symbolThen];
|
||||
if (!nativeThen) {
|
||||
// native Promise is not patchable, we need to use `then` directly
|
||||
// issue 1078
|
||||
nativeThen = nativeMicroTaskQueuePromise['then'];
|
||||
}
|
||||
nativeThen.call(nativeMicroTaskQueuePromise, drainMicroTaskQueue);
|
||||
}
|
||||
else {
|
||||
global[symbolSetTimeout](drainMicroTaskQueue, 0);
|
||||
|
|
@ -2967,12 +2973,13 @@ var Zone$1 = (function (global) {
|
|||
patchEventTarget: function () { return []; },
|
||||
patchOnProperties: noop,
|
||||
patchMethod: function () { return noop; },
|
||||
bindArguments: function () { return null; },
|
||||
bindArguments: function () { return []; },
|
||||
patchThen: function () { return noop; },
|
||||
setNativePromise: function (NativePromise) {
|
||||
// sometimes NativePromise.resolve static function
|
||||
// is not ready yet, (such as core-js/es6.promise)
|
||||
// so we need to check here.
|
||||
if (NativePromise && typeof NativePromise.resolve === FUNCTION) {
|
||||
if (NativePromise && typeof NativePromise.resolve === 'function') {
|
||||
nativeMicroTaskQueuePromise = NativePromise.resolve(0);
|
||||
}
|
||||
},
|
||||
|
|
@ -2988,6 +2995,16 @@ var Zone$1 = (function (global) {
|
|||
return global['Zone'] = Zone;
|
||||
})(typeof window !== 'undefined' && window || typeof self !== 'undefined' && self || global);
|
||||
|
||||
var __values = (undefined && undefined.__values) || function (o) {
|
||||
var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0;
|
||||
if (m) return m.call(o);
|
||||
return {
|
||||
next: function () {
|
||||
if (o && i >= o.length) o = void 0;
|
||||
return { value: o && o[i++], done: !o };
|
||||
}
|
||||
};
|
||||
};
|
||||
Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) {
|
||||
var ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
||||
var ObjectDefineProperty = Object.defineProperty;
|
||||
|
|
@ -3130,7 +3147,7 @@ Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) {
|
|||
var queue = promise[symbolValue];
|
||||
promise[symbolValue] = value;
|
||||
if (promise[symbolFinally] === symbolFinally) {
|
||||
// the promise is generated by Promise.prototype.finally
|
||||
// the promise is generated by Promise.prototype.finally
|
||||
if (state === RESOLVED) {
|
||||
// the state is resolved, should ignore the value
|
||||
// and use parent promise value
|
||||
|
|
@ -3214,7 +3231,9 @@ Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) {
|
|||
chainPromise[symbolParentPromiseState] = promiseState;
|
||||
}
|
||||
// should not pass value to finally callback
|
||||
var value = zone.run(delegate, undefined, isFinallyPromise && delegate !== forwardRejection && delegate !== forwardResolution ? [] : [parentPromiseValue]);
|
||||
var value = zone.run(delegate, undefined, isFinallyPromise && delegate !== forwardRejection && delegate !== forwardResolution ?
|
||||
[] :
|
||||
[parentPromiseValue]);
|
||||
resolvePromise(chainPromise, true, value);
|
||||
}
|
||||
catch (error) {
|
||||
|
|
@ -3249,6 +3268,7 @@ Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) {
|
|||
return resolvePromise(new this(null), REJECTED, error);
|
||||
};
|
||||
ZoneAwarePromise.race = function (values) {
|
||||
var e_1, _a;
|
||||
var resolve;
|
||||
var reject;
|
||||
var promise = new this(function (res, rej) {
|
||||
|
|
@ -3261,40 +3281,70 @@ Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) {
|
|||
function onReject(error) {
|
||||
promise && (promise = false || reject(error));
|
||||
}
|
||||
for (var _i = 0, values_1 = values; _i < values_1.length; _i++) {
|
||||
var value = values_1[_i];
|
||||
if (!isThenable(value)) {
|
||||
value = this.resolve(value);
|
||||
try {
|
||||
for (var values_1 = __values(values), values_1_1 = values_1.next(); !values_1_1.done; values_1_1 = values_1.next()) {
|
||||
var value = values_1_1.value;
|
||||
if (!isThenable(value)) {
|
||||
value = this.resolve(value);
|
||||
}
|
||||
value.then(onResolve, onReject);
|
||||
}
|
||||
value.then(onResolve, onReject);
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (values_1_1 && !values_1_1.done && (_a = values_1.return)) _a.call(values_1);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
}
|
||||
return promise;
|
||||
};
|
||||
ZoneAwarePromise.all = function (values) {
|
||||
var e_2, _a;
|
||||
var resolve;
|
||||
var reject;
|
||||
var promise = new this(function (res, rej) {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
var count = 0;
|
||||
// Start at 2 to prevent prematurely resolving if .then is called immediately.
|
||||
var unresolvedCount = 2;
|
||||
var valueIndex = 0;
|
||||
var resolvedValues = [];
|
||||
for (var _i = 0, values_2 = values; _i < values_2.length; _i++) {
|
||||
var value = values_2[_i];
|
||||
var _loop_2 = function (value) {
|
||||
if (!isThenable(value)) {
|
||||
value = this.resolve(value);
|
||||
value = this_1.resolve(value);
|
||||
}
|
||||
value.then((function (index) { return function (value) {
|
||||
resolvedValues[index] = value;
|
||||
count--;
|
||||
if (!count) {
|
||||
var curValueIndex = valueIndex;
|
||||
value.then(function (value) {
|
||||
resolvedValues[curValueIndex] = value;
|
||||
unresolvedCount--;
|
||||
if (unresolvedCount === 0) {
|
||||
resolve(resolvedValues);
|
||||
}
|
||||
}; })(count), reject);
|
||||
count++;
|
||||
}, reject);
|
||||
unresolvedCount++;
|
||||
valueIndex++;
|
||||
};
|
||||
var this_1 = this;
|
||||
try {
|
||||
for (var values_2 = __values(values), values_2_1 = values_2.next(); !values_2_1.done; values_2_1 = values_2.next()) {
|
||||
var value = values_2_1.value;
|
||||
_loop_2(value);
|
||||
}
|
||||
}
|
||||
if (!count)
|
||||
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (values_2_1 && !values_2_1.done && (_a = values_2.return)) _a.call(values_2);
|
||||
}
|
||||
finally { if (e_2) throw e_2.error; }
|
||||
}
|
||||
// Make the unresolvedCount zero-based again.
|
||||
unresolvedCount -= 2;
|
||||
if (unresolvedCount === 0) {
|
||||
resolve(resolvedValues);
|
||||
}
|
||||
return promise;
|
||||
};
|
||||
ZoneAwarePromise.prototype.then = function (onFulfilled, onRejected) {
|
||||
|
|
@ -3390,31 +3440,112 @@ Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) {
|
|||
};
|
||||
Ctor[symbolThenPatched] = true;
|
||||
}
|
||||
function zoneify(fn) {
|
||||
return function () {
|
||||
var resultPromise = fn.apply(this, arguments);
|
||||
if (resultPromise instanceof ZoneAwarePromise) {
|
||||
return resultPromise;
|
||||
}
|
||||
var ctor = resultPromise.constructor;
|
||||
if (!ctor[symbolThenPatched]) {
|
||||
patchThen(ctor);
|
||||
}
|
||||
return resultPromise;
|
||||
};
|
||||
}
|
||||
api.patchThen = patchThen;
|
||||
if (NativePromise) {
|
||||
patchThen(NativePromise);
|
||||
var fetch_1 = global['fetch'];
|
||||
if (typeof fetch_1 == 'function') {
|
||||
global['fetch'] = zoneify(fetch_1);
|
||||
}
|
||||
}
|
||||
// This is not part of public API, but it is useful for tests, so we expose it.
|
||||
Promise[Zone.__symbol__('uncaughtPromiseErrors')] = _uncaughtPromiseErrors;
|
||||
return ZoneAwarePromise;
|
||||
});
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
Zone.__load_patch('fetch', function (global, Zone, api) {
|
||||
var fetch = global['fetch'];
|
||||
var ZoneAwarePromise = global.Promise;
|
||||
var symbolThenPatched = api.symbol('thenPatched');
|
||||
var fetchTaskScheduling = api.symbol('fetchTaskScheduling');
|
||||
var fetchTaskAborting = api.symbol('fetchTaskAborting');
|
||||
if (typeof fetch !== 'function') {
|
||||
return;
|
||||
}
|
||||
var OriginalAbortController = global['AbortController'];
|
||||
var supportAbort = typeof OriginalAbortController === 'function';
|
||||
var abortNative = null;
|
||||
if (supportAbort) {
|
||||
global['AbortController'] = function () {
|
||||
var abortController = new OriginalAbortController();
|
||||
var signal = abortController.signal;
|
||||
signal.abortController = abortController;
|
||||
return abortController;
|
||||
};
|
||||
abortNative = api.patchMethod(OriginalAbortController.prototype, 'abort', function (delegate) { return function (self, args) {
|
||||
if (self.task) {
|
||||
return self.task.zone.cancelTask(self.task);
|
||||
}
|
||||
return delegate.apply(self, args);
|
||||
}; });
|
||||
}
|
||||
var placeholder = function () { };
|
||||
global['fetch'] = function () {
|
||||
var _this = this;
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
var options = args.length > 1 ? args[1] : null;
|
||||
var signal = options && options.signal;
|
||||
return new Promise(function (res, rej) {
|
||||
var task = Zone.current.scheduleMacroTask('fetch', placeholder, args, function () {
|
||||
var fetchPromise;
|
||||
var zone = Zone.current;
|
||||
try {
|
||||
zone[fetchTaskScheduling] = true;
|
||||
fetchPromise = fetch.apply(_this, args);
|
||||
}
|
||||
catch (error) {
|
||||
rej(error);
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
zone[fetchTaskScheduling] = false;
|
||||
}
|
||||
if (!(fetchPromise instanceof ZoneAwarePromise)) {
|
||||
var ctor = fetchPromise.constructor;
|
||||
if (!ctor[symbolThenPatched]) {
|
||||
api.patchThen(ctor);
|
||||
}
|
||||
}
|
||||
fetchPromise.then(function (resource) {
|
||||
if (task.state !== 'notScheduled') {
|
||||
task.invoke();
|
||||
}
|
||||
res(resource);
|
||||
}, function (error) {
|
||||
if (task.state !== 'notScheduled') {
|
||||
task.invoke();
|
||||
}
|
||||
rej(error);
|
||||
});
|
||||
}, function () {
|
||||
if (!supportAbort) {
|
||||
rej('No AbortController supported, can not cancel fetch');
|
||||
return;
|
||||
}
|
||||
if (signal && signal.abortController && !signal.aborted &&
|
||||
typeof signal.abortController.abort === 'function' && abortNative) {
|
||||
try {
|
||||
Zone.current[fetchTaskAborting] = true;
|
||||
abortNative.call(signal.abortController);
|
||||
}
|
||||
finally {
|
||||
Zone.current[fetchTaskAborting] = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
rej('cancel fetch need a AbortController.signal');
|
||||
}
|
||||
});
|
||||
if (signal && signal.abortController) {
|
||||
signal.abortController.task = task;
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
|
|
@ -3530,9 +3661,23 @@ var wrapFn = function (event) {
|
|||
}
|
||||
var target = this || event.target || _global;
|
||||
var listener = target[eventNameSymbol];
|
||||
var result = listener && listener.apply(this, arguments);
|
||||
if (result != undefined && !result) {
|
||||
event.preventDefault();
|
||||
var result;
|
||||
if (isBrowser && target === internalWindow && event.type === 'error') {
|
||||
// window.onerror have different signiture
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror#window.onerror
|
||||
// and onerror callback will prevent default when callback return true
|
||||
var errorEvent = event;
|
||||
result = listener &&
|
||||
listener.call(this, errorEvent.message, errorEvent.filename, errorEvent.lineno, errorEvent.colno, errorEvent.error);
|
||||
if (result === true) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = listener && listener.apply(this, arguments);
|
||||
if (result != undefined && !result) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
|
@ -3550,6 +3695,10 @@ function patchProperty(obj, prop, prototype) {
|
|||
if (!desc || !desc.configurable) {
|
||||
return;
|
||||
}
|
||||
var onPropPatchedSymbol = zoneSymbol('on' + prop + 'patched');
|
||||
if (obj.hasOwnProperty(onPropPatchedSymbol) && obj[onPropPatchedSymbol]) {
|
||||
return;
|
||||
}
|
||||
// A property descriptor cannot have getter/setter and be writable
|
||||
// deleting the writable and value properties avoids this error:
|
||||
//
|
||||
|
|
@ -3627,6 +3776,7 @@ function patchProperty(obj, prop, prototype) {
|
|||
return null;
|
||||
};
|
||||
ObjectDefineProperty(obj, prop, desc);
|
||||
obj[onPropPatchedSymbol] = true;
|
||||
}
|
||||
function patchOnProperties(obj, properties, prototype) {
|
||||
if (properties) {
|
||||
|
|
@ -3717,6 +3867,31 @@ function patchClass(className) {
|
|||
}
|
||||
}
|
||||
}
|
||||
function copySymbolProperties(src, dest) {
|
||||
if (typeof Object.getOwnPropertySymbols !== 'function') {
|
||||
return;
|
||||
}
|
||||
var symbols = Object.getOwnPropertySymbols(src);
|
||||
symbols.forEach(function (symbol) {
|
||||
var desc = Object.getOwnPropertyDescriptor(src, symbol);
|
||||
Object.defineProperty(dest, symbol, {
|
||||
get: function () {
|
||||
return src[symbol];
|
||||
},
|
||||
set: function (value) {
|
||||
if (desc && (!desc.writable || typeof desc.set !== 'function')) {
|
||||
// if src[symbol] is not writable or not have a setter, just return
|
||||
return;
|
||||
}
|
||||
src[symbol] = value;
|
||||
},
|
||||
enumerable: desc ? desc.enumerable : true,
|
||||
configurable: desc ? desc.configurable : true
|
||||
});
|
||||
});
|
||||
}
|
||||
var shouldCopySymbolProperties = false;
|
||||
|
||||
function patchMethod(target, name, patchFn) {
|
||||
var proto = target;
|
||||
while (proto && !proto.hasOwnProperty(name)) {
|
||||
|
|
@ -3727,7 +3902,7 @@ function patchMethod(target, name, patchFn) {
|
|||
proto = target;
|
||||
}
|
||||
var delegateName = zoneSymbol(name);
|
||||
var delegate;
|
||||
var delegate = null;
|
||||
if (proto && !(delegate = proto[delegateName])) {
|
||||
delegate = proto[delegateName] = proto[name];
|
||||
// check whether proto[name] is writable
|
||||
|
|
@ -3739,6 +3914,9 @@ function patchMethod(target, name, patchFn) {
|
|||
return patchDelegate_1(this, arguments);
|
||||
};
|
||||
attachOriginToPatched(proto[name], delegate);
|
||||
if (shouldCopySymbolProperties) {
|
||||
copySymbolProperties(delegate, proto[name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return delegate;
|
||||
|
|
@ -3757,7 +3935,7 @@ function patchMacroTask(obj, funcName, metaCreator) {
|
|||
setNative = patchMethod(obj, funcName, function (delegate) { return function (self, args) {
|
||||
var meta = metaCreator(self, args);
|
||||
if (meta.cbIdx >= 0 && typeof args[meta.cbIdx] === 'function') {
|
||||
return scheduleMacroTaskWithCurrentZone(meta.name, args[meta.cbIdx], meta, scheduleTask, null);
|
||||
return scheduleMacroTaskWithCurrentZone(meta.name, args[meta.cbIdx], meta, scheduleTask);
|
||||
}
|
||||
else {
|
||||
// cause an error by calling it directly.
|
||||
|
|
@ -3771,6 +3949,17 @@ function attachOriginToPatched(patched, original) {
|
|||
}
|
||||
var isDetectedIEOrEdge = false;
|
||||
var ieOrEdge = false;
|
||||
function isIE() {
|
||||
try {
|
||||
var ua = internalWindow.navigator.userAgent;
|
||||
if (ua.indexOf('MSIE ') !== -1 || ua.indexOf('Trident/') !== -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function isIEOrEdge() {
|
||||
if (isDetectedIEOrEdge) {
|
||||
return ieOrEdge;
|
||||
|
|
@ -3852,6 +4041,21 @@ Zone.__load_patch('toString', function (global) {
|
|||
* @fileoverview
|
||||
* @suppress {missingRequire}
|
||||
*/
|
||||
var passiveSupported = false;
|
||||
if (typeof window !== 'undefined') {
|
||||
try {
|
||||
var options = Object.defineProperty({}, 'passive', {
|
||||
get: function () {
|
||||
passiveSupported = true;
|
||||
}
|
||||
});
|
||||
window.addEventListener('test', options, options);
|
||||
window.removeEventListener('test', options, options);
|
||||
}
|
||||
catch (err) {
|
||||
passiveSupported = false;
|
||||
}
|
||||
}
|
||||
// an identifier to tell ZoneTask do not create a new invoke closure
|
||||
var OPTIMIZED_ZONE_EVENT_TASK_DATA = {
|
||||
useG: true
|
||||
|
|
@ -3987,6 +4191,7 @@ function patchEventTarget(_global, apis, patchOptions) {
|
|||
if (proto[zoneSymbolAddEventListener]) {
|
||||
return false;
|
||||
}
|
||||
var eventNameToString = patchOptions && patchOptions.eventNameToString;
|
||||
// a shared global taskData to pass data for scheduleEventTask
|
||||
// so we do not need to create a new object just for pass some data
|
||||
var taskData = {};
|
||||
|
|
@ -4002,12 +4207,24 @@ function patchEventTarget(_global, apis, patchOptions) {
|
|||
nativePrependEventListener = proto[zoneSymbol(patchOptions.prepend)] =
|
||||
proto[patchOptions.prepend];
|
||||
}
|
||||
var customScheduleGlobal = function () {
|
||||
function checkIsPassive(task) {
|
||||
if (!passiveSupported && typeof taskData.options !== 'boolean' &&
|
||||
typeof taskData.options !== 'undefined' && taskData.options !== null) {
|
||||
// options is a non-null non-undefined object
|
||||
// passive is not supported
|
||||
// don't pass options as object
|
||||
// just pass capture as a boolean
|
||||
task.options = !!taskData.options.capture;
|
||||
taskData.options = task.options;
|
||||
}
|
||||
}
|
||||
var customScheduleGlobal = function (task) {
|
||||
// if there is already a task for the eventName + capture,
|
||||
// just return, because we use the shared globalZoneAwareCallback here.
|
||||
if (taskData.isExisting) {
|
||||
return;
|
||||
}
|
||||
checkIsPassive(task);
|
||||
return nativeAddEventListener.call(taskData.target, taskData.eventName, taskData.capture ? globalZoneAwareCaptureCallback : globalZoneAwareCallback, taskData.options);
|
||||
};
|
||||
var customCancelGlobal = function (task) {
|
||||
|
|
@ -4048,6 +4265,7 @@ function patchEventTarget(_global, apis, patchOptions) {
|
|||
return nativeRemoveEventListener.call(task.target, task.eventName, task.capture ? globalZoneAwareCaptureCallback : globalZoneAwareCallback, task.options);
|
||||
};
|
||||
var customScheduleNonGlobal = function (task) {
|
||||
checkIsPassive(task);
|
||||
return nativeAddEventListener.call(taskData.target, taskData.eventName, task.invoke, taskData.options);
|
||||
};
|
||||
var customSchedulePrepend = function (task) {
|
||||
|
|
@ -4070,10 +4288,15 @@ function patchEventTarget(_global, apis, patchOptions) {
|
|||
if (prepend === void 0) { prepend = false; }
|
||||
return function () {
|
||||
var target = this || _global;
|
||||
var eventName = arguments[0];
|
||||
var delegate = arguments[1];
|
||||
if (!delegate) {
|
||||
return nativeListener.apply(this, arguments);
|
||||
}
|
||||
if (isNode && eventName === 'uncaughtException') {
|
||||
// don't patch uncaughtException of nodejs to prevent endless loop
|
||||
return nativeListener.apply(this, arguments);
|
||||
}
|
||||
// don't create the bind delegate function for handleEvent
|
||||
// case here to improve addEventListener performance
|
||||
// we will create the bind delegate when invoke
|
||||
|
|
@ -4087,7 +4310,6 @@ function patchEventTarget(_global, apis, patchOptions) {
|
|||
if (validateHandler && !validateHandler(nativeListener, delegate, target, arguments)) {
|
||||
return;
|
||||
}
|
||||
var eventName = arguments[0];
|
||||
var options = arguments[2];
|
||||
if (blackListedEvents) {
|
||||
// check black list
|
||||
|
|
@ -4117,8 +4339,8 @@ function patchEventTarget(_global, apis, patchOptions) {
|
|||
var symbolEventName;
|
||||
if (!symbolEventNames) {
|
||||
// the code is duplicate, but I just want to get some better performance
|
||||
var falseEventName = eventName + FALSE_STR;
|
||||
var trueEventName = eventName + TRUE_STR;
|
||||
var falseEventName = (eventNameToString ? eventNameToString(eventName) : eventName) + FALSE_STR;
|
||||
var trueEventName = (eventNameToString ? eventNameToString(eventName) : eventName) + TRUE_STR;
|
||||
var symbol = ZONE_SYMBOL_PREFIX + falseEventName;
|
||||
var symbolCapture = ZONE_SYMBOL_PREFIX + trueEventName;
|
||||
zoneSymbolEventNames$1[eventName] = {};
|
||||
|
|
@ -4153,7 +4375,8 @@ function patchEventTarget(_global, apis, patchOptions) {
|
|||
source = targetSource[eventName];
|
||||
}
|
||||
if (!source) {
|
||||
source = constructorName + addSource + eventName;
|
||||
source = constructorName + addSource +
|
||||
(eventNameToString ? eventNameToString(eventName) : eventName);
|
||||
}
|
||||
// do not create a new object as task.data to pass those things
|
||||
// just use the global shared one
|
||||
|
|
@ -4168,7 +4391,7 @@ function patchEventTarget(_global, apis, patchOptions) {
|
|||
taskData.capture = capture;
|
||||
taskData.eventName = eventName;
|
||||
taskData.isExisting = isExisting;
|
||||
var data = useGlobalCallback ? OPTIMIZED_ZONE_EVENT_TASK_DATA : null;
|
||||
var data = useGlobalCallback ? OPTIMIZED_ZONE_EVENT_TASK_DATA : undefined;
|
||||
// keep taskData into data to allow onScheduleEventTask to access the task information
|
||||
if (data) {
|
||||
data.taskData = taskData;
|
||||
|
|
@ -4186,7 +4409,11 @@ function patchEventTarget(_global, apis, patchOptions) {
|
|||
if (once) {
|
||||
options.once = true;
|
||||
}
|
||||
task.options = options;
|
||||
if (!(!passiveSupported && typeof task.options === 'boolean')) {
|
||||
// if not support passive, and we pass an option object
|
||||
// to addEventListener, we should save the options to task
|
||||
task.options = options;
|
||||
}
|
||||
task.target = target;
|
||||
task.capture = capture;
|
||||
task.eventName = eventName;
|
||||
|
|
@ -4271,7 +4498,7 @@ function patchEventTarget(_global, apis, patchOptions) {
|
|||
var target = this || _global;
|
||||
var eventName = arguments[0];
|
||||
var listeners = [];
|
||||
var tasks = findEventTasks(target, eventName);
|
||||
var tasks = findEventTasks(target, eventNameToString ? eventNameToString(eventName) : eventName);
|
||||
for (var i = 0; i < tasks.length; i++) {
|
||||
var task = tasks[i];
|
||||
var delegate = task.originalDelegate ? task.originalDelegate : task.callback;
|
||||
|
|
@ -4427,9 +4654,9 @@ function patchTimer(window, setName, cancelName, nameSuffix) {
|
|||
patchMethod(window, setName, function (delegate) { return function (self, args) {
|
||||
if (typeof args[0] === 'function') {
|
||||
var options = {
|
||||
handleId: null,
|
||||
isPeriodic: nameSuffix === 'Interval',
|
||||
delay: (nameSuffix === 'Timeout' || nameSuffix === 'Interval') ? args[1] || 0 : null,
|
||||
delay: (nameSuffix === 'Timeout' || nameSuffix === 'Interval') ? args[1] || 0 :
|
||||
undefined,
|
||||
args: args
|
||||
};
|
||||
var task = scheduleMacroTaskWithCurrentZone(setName, args[0], options, scheduleTask, clearTask);
|
||||
|
|
@ -4544,7 +4771,7 @@ function propertyPatch() {
|
|||
};
|
||||
Object.getOwnPropertyDescriptor = function (obj, prop) {
|
||||
var desc = _getOwnPropertyDescriptor(obj, prop);
|
||||
if (isUnconfigurable(obj, prop)) {
|
||||
if (desc && isUnconfigurable(obj, prop)) {
|
||||
desc.configurable = false;
|
||||
}
|
||||
return desc;
|
||||
|
|
@ -4772,10 +4999,10 @@ var globalEventHandlersEventNames = [
|
|||
'wheel'
|
||||
];
|
||||
var documentEventNames = [
|
||||
'afterscriptexecute', 'beforescriptexecute', 'DOMContentLoaded', 'fullscreenchange',
|
||||
'afterscriptexecute', 'beforescriptexecute', 'DOMContentLoaded', 'freeze', 'fullscreenchange',
|
||||
'mozfullscreenchange', 'webkitfullscreenchange', 'msfullscreenchange', 'fullscreenerror',
|
||||
'mozfullscreenerror', 'webkitfullscreenerror', 'msfullscreenerror', 'readystatechange',
|
||||
'visibilitychange'
|
||||
'visibilitychange', 'resume'
|
||||
];
|
||||
var windowEventNames = [
|
||||
'absolutedeviceorientation',
|
||||
|
|
@ -4887,7 +5114,7 @@ var websocketEventNames = ['close', 'error', 'open', 'message'];
|
|||
var workerEventNames = ['error', 'message'];
|
||||
var eventNames = globalEventHandlersEventNames.concat(webglEventNames, formEventNames, detailEventNames, documentEventNames, windowEventNames, htmlElementEventNames, ieElementEventNames);
|
||||
function filterProperties(target, onProperties, ignoreProperties) {
|
||||
if (!ignoreProperties) {
|
||||
if (!ignoreProperties || ignoreProperties.length === 0) {
|
||||
return onProperties;
|
||||
}
|
||||
var tip = ignoreProperties.filter(function (ip) { return ip.target === target; });
|
||||
|
|
@ -4912,13 +5139,14 @@ function propertyDescriptorPatch(api, _global) {
|
|||
}
|
||||
var supportsWebSocket = typeof WebSocket !== 'undefined';
|
||||
if (canPatchViaPropertyDescriptor()) {
|
||||
var ignoreProperties = _global.__Zone_ignore_on_properties;
|
||||
var ignoreProperties = _global['__Zone_ignore_on_properties'];
|
||||
// for browsers that we can patch the descriptor: Chrome & Firefox
|
||||
if (isBrowser) {
|
||||
var internalWindow = window;
|
||||
var ignoreErrorProperties = isIE ? [{ target: internalWindow, ignoreProperties: ['error'] }] : [];
|
||||
// in IE/Edge, onProp not exist in window object, but in WindowPrototype
|
||||
// so we need to pass WindowPrototype to check onProp exist or not
|
||||
patchFilteredProperties(internalWindow, eventNames.concat(['messageerror']), ignoreProperties, ObjectGetPrototypeOf(internalWindow));
|
||||
patchFilteredProperties(internalWindow, eventNames.concat(['messageerror']), ignoreProperties ? ignoreProperties.concat(ignoreErrorProperties) : ignoreProperties, ObjectGetPrototypeOf(internalWindow));
|
||||
patchFilteredProperties(Document.prototype, eventNames, ignoreProperties);
|
||||
if (typeof internalWindow['SVGElement'] !== 'undefined') {
|
||||
patchFilteredProperties(internalWindow['SVGElement'].prototype, eventNames, ignoreProperties);
|
||||
|
|
@ -4940,9 +5168,9 @@ function propertyDescriptorPatch(api, _global) {
|
|||
}
|
||||
}
|
||||
patchFilteredProperties(XMLHttpRequest.prototype, XMLHttpRequestEventNames, ignoreProperties);
|
||||
var XMLHttpRequestEventTarget = _global['XMLHttpRequestEventTarget'];
|
||||
if (XMLHttpRequestEventTarget) {
|
||||
patchFilteredProperties(XMLHttpRequestEventTarget && XMLHttpRequestEventTarget.prototype, XMLHttpRequestEventNames, ignoreProperties);
|
||||
var XMLHttpRequestEventTarget_1 = _global['XMLHttpRequestEventTarget'];
|
||||
if (XMLHttpRequestEventTarget_1) {
|
||||
patchFilteredProperties(XMLHttpRequestEventTarget_1 && XMLHttpRequestEventTarget_1.prototype, XMLHttpRequestEventNames, ignoreProperties);
|
||||
}
|
||||
if (typeof IDBIndex !== 'undefined') {
|
||||
patchFilteredProperties(IDBIndex.prototype, IDBIndexEventNames, ignoreProperties);
|
||||
|
|
@ -5157,16 +5385,16 @@ function patchEvent(global, api) {
|
|||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
function registerElementPatch(_global) {
|
||||
if ((!isBrowser && !isMix) || !('registerElement' in _global.document)) {
|
||||
function patchCallbacks(target, targetName, method, callbacks) {
|
||||
var symbol = Zone.__symbol__(method);
|
||||
if (target[symbol]) {
|
||||
return;
|
||||
}
|
||||
var _registerElement = document.registerElement;
|
||||
var callbacks = ['createdCallback', 'attachedCallback', 'detachedCallback', 'attributeChangedCallback'];
|
||||
document.registerElement = function (name, opts) {
|
||||
var nativeDelegate = target[symbol] = target[method];
|
||||
target[method] = function (name, opts, options) {
|
||||
if (opts && opts.prototype) {
|
||||
callbacks.forEach(function (callback) {
|
||||
var source = 'Document.registerElement::' + callback;
|
||||
var source = targetName + "." + method + "::" + callback;
|
||||
var prototype = opts.prototype;
|
||||
if (prototype.hasOwnProperty(callback)) {
|
||||
var descriptor = ObjectGetOwnPropertyDescriptor(prototype, callback);
|
||||
|
|
@ -5183,9 +5411,23 @@ function registerElementPatch(_global) {
|
|||
}
|
||||
});
|
||||
}
|
||||
return _registerElement.call(document, name, opts);
|
||||
return nativeDelegate.call(target, name, opts, options);
|
||||
};
|
||||
attachOriginToPatched(document.registerElement, _registerElement);
|
||||
attachOriginToPatched(target[method], nativeDelegate);
|
||||
}
|
||||
function registerElementPatch(_global) {
|
||||
if ((!isBrowser && !isMix) || !('registerElement' in _global.document)) {
|
||||
return;
|
||||
}
|
||||
var callbacks = ['createdCallback', 'attachedCallback', 'detachedCallback', 'attributeChangedCallback'];
|
||||
patchCallbacks(document, 'Document', 'registerElement', callbacks);
|
||||
}
|
||||
function patchCustomElements(_global) {
|
||||
if ((!isBrowser && !isMix) || !('customElements' in _global)) {
|
||||
return;
|
||||
}
|
||||
var callbacks = ['connectedCallback', 'disconnectedCallback', 'adoptedCallback', 'attributeChangedCallback'];
|
||||
patchCallbacks(_global.customElements, 'customElements', 'define', callbacks);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -5248,7 +5490,10 @@ Zone.__load_patch('EventTarget', function (global, Zone, api) {
|
|||
Zone.__load_patch('on_property', function (global, Zone, api) {
|
||||
propertyDescriptorPatch(api, global);
|
||||
propertyPatch();
|
||||
});
|
||||
Zone.__load_patch('customElements', function (global, Zone, api) {
|
||||
registerElementPatch(global);
|
||||
patchCustomElements(global);
|
||||
});
|
||||
Zone.__load_patch('canvas', function (global) {
|
||||
var HTMLCanvasElement = global['HTMLCanvasElement'];
|
||||
|
|
@ -5267,6 +5512,7 @@ Zone.__load_patch('XHR', function (global, Zone) {
|
|||
var XHR_LISTENER = zoneSymbol('xhrListener');
|
||||
var XHR_SCHEDULED = zoneSymbol('xhrScheduled');
|
||||
var XHR_URL = zoneSymbol('xhrURL');
|
||||
var XHR_ERROR_BEFORE_SCHEDULED = zoneSymbol('xhrErrorBeforeScheduled');
|
||||
function patchXHR(window) {
|
||||
var XMLHttpRequestPrototype = XMLHttpRequest.prototype;
|
||||
function findPendingTask(target) {
|
||||
|
|
@ -5275,9 +5521,9 @@ Zone.__load_patch('XHR', function (global, Zone) {
|
|||
var oriAddListener = XMLHttpRequestPrototype[ZONE_SYMBOL_ADD_EVENT_LISTENER];
|
||||
var oriRemoveListener = XMLHttpRequestPrototype[ZONE_SYMBOL_REMOVE_EVENT_LISTENER];
|
||||
if (!oriAddListener) {
|
||||
var XMLHttpRequestEventTarget = window['XMLHttpRequestEventTarget'];
|
||||
if (XMLHttpRequestEventTarget) {
|
||||
var XMLHttpRequestEventTargetPrototype = XMLHttpRequestEventTarget.prototype;
|
||||
var XMLHttpRequestEventTarget_1 = window['XMLHttpRequestEventTarget'];
|
||||
if (XMLHttpRequestEventTarget_1) {
|
||||
var XMLHttpRequestEventTargetPrototype = XMLHttpRequestEventTarget_1.prototype;
|
||||
oriAddListener = XMLHttpRequestEventTargetPrototype[ZONE_SYMBOL_ADD_EVENT_LISTENER];
|
||||
oriRemoveListener = XMLHttpRequestEventTargetPrototype[ZONE_SYMBOL_REMOVE_EVENT_LISTENER];
|
||||
}
|
||||
|
|
@ -5285,9 +5531,10 @@ Zone.__load_patch('XHR', function (global, Zone) {
|
|||
var READY_STATE_CHANGE = 'readystatechange';
|
||||
var SCHEDULED = 'scheduled';
|
||||
function scheduleTask(task) {
|
||||
XMLHttpRequest[XHR_SCHEDULED] = false;
|
||||
var data = task.data;
|
||||
var target = data.target;
|
||||
target[XHR_SCHEDULED] = false;
|
||||
target[XHR_ERROR_BEFORE_SCHEDULED] = false;
|
||||
// remove existing event listener
|
||||
var listener = target[XHR_LISTENER];
|
||||
if (!oriAddListener) {
|
||||
|
|
@ -5301,8 +5548,35 @@ Zone.__load_patch('XHR', function (global, Zone) {
|
|||
if (target.readyState === target.DONE) {
|
||||
// sometimes on some browsers XMLHttpRequest will fire onreadystatechange with
|
||||
// readyState=4 multiple times, so we need to check task state here
|
||||
if (!data.aborted && XMLHttpRequest[XHR_SCHEDULED] && task.state === SCHEDULED) {
|
||||
task.invoke();
|
||||
if (!data.aborted && target[XHR_SCHEDULED] && task.state === SCHEDULED) {
|
||||
// check whether the xhr has registered onload listener
|
||||
// if that is the case, the task should invoke after all
|
||||
// onload listeners finish.
|
||||
var loadTasks = target['__zone_symbol__loadfalse'];
|
||||
if (loadTasks && loadTasks.length > 0) {
|
||||
var oriInvoke_1 = task.invoke;
|
||||
task.invoke = function () {
|
||||
// need to load the tasks again, because in other
|
||||
// load listener, they may remove themselves
|
||||
var loadTasks = target['__zone_symbol__loadfalse'];
|
||||
for (var i = 0; i < loadTasks.length; i++) {
|
||||
if (loadTasks[i] === task) {
|
||||
loadTasks.splice(i, 1);
|
||||
}
|
||||
}
|
||||
if (!data.aborted && task.state === SCHEDULED) {
|
||||
oriInvoke_1.call(task);
|
||||
}
|
||||
};
|
||||
loadTasks.push(task);
|
||||
}
|
||||
else {
|
||||
task.invoke();
|
||||
}
|
||||
}
|
||||
else if (!data.aborted && target[XHR_SCHEDULED] === false) {
|
||||
// error occurs when xhr.send()
|
||||
target[XHR_ERROR_BEFORE_SCHEDULED] = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -5312,7 +5586,7 @@ Zone.__load_patch('XHR', function (global, Zone) {
|
|||
target[XHR_TASK] = task;
|
||||
}
|
||||
sendNative.apply(target, data.args);
|
||||
XMLHttpRequest[XHR_SCHEDULED] = true;
|
||||
target[XHR_SCHEDULED] = true;
|
||||
return task;
|
||||
}
|
||||
function placeholderCallback() { }
|
||||
|
|
@ -5329,24 +5603,32 @@ Zone.__load_patch('XHR', function (global, Zone) {
|
|||
return openNative.apply(self, args);
|
||||
}; });
|
||||
var XMLHTTPREQUEST_SOURCE = 'XMLHttpRequest.send';
|
||||
var fetchTaskAborting = zoneSymbol('fetchTaskAborting');
|
||||
var fetchTaskScheduling = zoneSymbol('fetchTaskScheduling');
|
||||
var sendNative = patchMethod(XMLHttpRequestPrototype, 'send', function () { return function (self, args) {
|
||||
if (Zone.current[fetchTaskScheduling] === true) {
|
||||
// a fetch is scheduling, so we are using xhr to polyfill fetch
|
||||
// and because we already schedule macroTask for fetch, we should
|
||||
// not schedule a macroTask for xhr again
|
||||
return sendNative.apply(self, args);
|
||||
}
|
||||
if (self[XHR_SYNC]) {
|
||||
// if the XHR is sync there is no task to schedule, just execute the code.
|
||||
return sendNative.apply(self, args);
|
||||
}
|
||||
else {
|
||||
var options = {
|
||||
target: self,
|
||||
url: self[XHR_URL],
|
||||
isPeriodic: false,
|
||||
delay: null,
|
||||
args: args,
|
||||
aborted: false
|
||||
};
|
||||
return scheduleMacroTaskWithCurrentZone(XMLHTTPREQUEST_SOURCE, placeholderCallback, options, scheduleTask, clearTask);
|
||||
var options = { target: self, url: self[XHR_URL], isPeriodic: false, args: args, aborted: false };
|
||||
var task = scheduleMacroTaskWithCurrentZone(XMLHTTPREQUEST_SOURCE, placeholderCallback, options, scheduleTask, clearTask);
|
||||
if (self && self[XHR_ERROR_BEFORE_SCHEDULED] === true && !options.aborted &&
|
||||
task.state === SCHEDULED) {
|
||||
// xhr request throw error when send
|
||||
// we should invoke task instead of leaving a scheduled
|
||||
// pending macroTask
|
||||
task.invoke();
|
||||
}
|
||||
}
|
||||
}; });
|
||||
var abortNative = patchMethod(XMLHttpRequestPrototype, 'abort', function () { return function (self) {
|
||||
var abortNative = patchMethod(XMLHttpRequestPrototype, 'abort', function () { return function (self, args) {
|
||||
var task = findPendingTask(self);
|
||||
if (task && typeof task.type == 'string') {
|
||||
// If the XHR has already completed, do nothing.
|
||||
|
|
@ -5358,6 +5640,10 @@ Zone.__load_patch('XHR', function (global, Zone) {
|
|||
}
|
||||
task.zone.cancelTask(task);
|
||||
}
|
||||
else if (Zone.current[fetchTaskAborting] === true) {
|
||||
// the abort is called from fetch polyfill, we need to call native abort of XHR.
|
||||
return abortNative.apply(self, args);
|
||||
}
|
||||
// Otherwise, we are trying to abort an XHR which has not yet been sent, so there is no
|
||||
// task
|
||||
// to cancel. Do nothing.
|
||||
|
|
@ -5497,8 +5783,8 @@ __webpack_require__.r(__webpack_exports__);
|
|||
/*! no static exports found */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
__webpack_require__(/*! D:\Projects\Zano\src\gui\qt-daemon\html_source\src\polyfills.ts */"./src/polyfills.ts");
|
||||
module.exports = __webpack_require__(/*! D:\Projects\Zano\src\gui\qt-daemon\html_source\node_modules\@angular-devkit\build-angular\src\angular-cli-files\models\jit-polyfills.js */"./node_modules/@angular-devkit/build-angular/src/angular-cli-files/models/jit-polyfills.js");
|
||||
__webpack_require__(/*! D:\zano_zano\src\gui\qt-daemon\html_source\src\polyfills.ts */"./src/polyfills.ts");
|
||||
module.exports = __webpack_require__(/*! D:\zano_zano\src\gui\qt-daemon\html_source\node_modules\@angular-devkit\build-angular\src\angular-cli-files\models\jit-polyfills.js */"./node_modules/@angular-devkit/build-angular/src/angular-cli-files/models/jit-polyfills.js");
|
||||
|
||||
|
||||
/***/ })
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -23,6 +23,7 @@
|
|||
"@angular/platform-browser": "~7.0.0",
|
||||
"@angular/platform-browser-dynamic": "~7.0.0",
|
||||
"@angular/router": "~7.0.0",
|
||||
"@ng-select/ng-select": "^2.16.2",
|
||||
"@ngx-translate/core": "^11.0.0",
|
||||
"@ngx-translate/http-loader": "^4.0.0",
|
||||
"angular-highcharts": "^7.0.2",
|
||||
|
|
@ -39,7 +40,7 @@
|
|||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^0.12.2",
|
||||
"@angular/cli": "~7.0.2",
|
||||
"@angular/compiler-cli": "~7.0.0",
|
||||
"@angular/compiler-cli": "^7.2.10",
|
||||
"@angular/language-service": "~7.0.0",
|
||||
"@types/highcharts": "^5.0.34",
|
||||
"@types/jasmine": "~2.8.8",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
import { InputDisableSelectionDirective } from './input-disable-selection.directive';
|
||||
|
||||
describe('InputDisableSelectionDirective', () => {
|
||||
it('should create an instance', () => {
|
||||
const directive = new InputDisableSelectionDirective();
|
||||
expect(directive).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import {Directive, HostListener} from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: 'input'
|
||||
})
|
||||
export class InputDisableSelectionDirective {
|
||||
|
||||
constructor() {}
|
||||
|
||||
@HostListener('mousedown', ['$event'])
|
||||
handleInput(event: Event) {
|
||||
if ((<HTMLInputElement>event.target).readOnly) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -43,6 +43,8 @@
|
|||
.message-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
margin-left: 2rem;
|
||||
|
||||
.title {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
<div class="progress-bar-container">
|
||||
<div class="progress-bar">
|
||||
<div class="progress-bar-full" [style.width]="width"></div>
|
||||
</div>
|
||||
<div class="progress-labels">
|
||||
<span *ngFor="let label of labels">
|
||||
{{ label | translate }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
.progress-bar-container {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 0 3rem;
|
||||
width: 100%;
|
||||
height: 3rem;
|
||||
|
||||
.progress-bar {
|
||||
position: absolute;
|
||||
top: -0.7rem;
|
||||
left: 0;
|
||||
margin: 0 3rem;
|
||||
width: calc(100% - 6rem);
|
||||
height: 0.7rem;
|
||||
|
||||
.progress-bar-full {
|
||||
height: 0.7rem;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-labels {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 1.2rem;
|
||||
height: 100%;
|
||||
|
||||
span {
|
||||
flex: 1 0 0;
|
||||
text-align: center;
|
||||
|
||||
&:first-child {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.progress-time {
|
||||
position: absolute;
|
||||
top: -3rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ProgressContainerComponent } from './progress-container.component';
|
||||
|
||||
describe('ProgressContainerComponent', () => {
|
||||
let component: ProgressContainerComponent;
|
||||
let fixture: ComponentFixture<ProgressContainerComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ProgressContainerComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ProgressContainerComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import { Component, Input, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-progress-container',
|
||||
templateUrl: './progress-container.component.html',
|
||||
styleUrls: ['./progress-container.component.scss']
|
||||
})
|
||||
export class ProgressContainerComponent implements OnInit {
|
||||
|
||||
@Input() width: string;
|
||||
@Input() labels: [];
|
||||
|
||||
constructor() {}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
}
|
||||
|
|
@ -119,7 +119,7 @@ export class Wallet {
|
|||
}
|
||||
}
|
||||
if (!exists) {
|
||||
if (this.history.length && items[i].timestamp > this.history[0].timestamp) {
|
||||
if (this.history.length && items[i].timestamp >= this.history[0].timestamp) {
|
||||
this.history.unshift(this.prepareHistoryItem(items[i]));
|
||||
} else {
|
||||
this.history.push(this.prepareHistoryItem(items[i]));
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export class ContractStatusMessagesPipe implements PipeTransform {
|
|||
state.part1 = this.translate.instant('CONTRACTS.STATUS_MESSAGES.SELLER.EXPIRED');
|
||||
break;
|
||||
}
|
||||
return state.part1 + ' ' + state.part2;
|
||||
return state.part1 + (state.part2.length ? '. ' + state.part2 : '');
|
||||
}
|
||||
|
||||
getStateBuyer(stateNum: number): string {
|
||||
|
|
@ -101,7 +101,7 @@ export class ContractStatusMessagesPipe implements PipeTransform {
|
|||
state.part1 = this.translate.instant('CONTRACTS.STATUS_MESSAGES.BUYER.EXPIRED');
|
||||
break;
|
||||
}
|
||||
return state.part1 + ' ' + state.part2;
|
||||
return state.part1 + (state.part2.length ? '. ' + state.part2 : '');
|
||||
}
|
||||
|
||||
transform(item: any, args?: any): any {
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ export class BackendService {
|
|||
}
|
||||
|
||||
private bigNumberParser(key, val) {
|
||||
if (val.constructor.name === 'BigNumber' && ['balance', 'unlocked_balance', 'amount', 'fee', 'b_fee', 'to_pay', 'a_pledge', 'b_pledge', 'coast'].indexOf(key) === -1) {
|
||||
if (val.constructor.name === 'BigNumber' && ['balance', 'unlocked_balance', 'amount', 'fee', 'b_fee', 'to_pay', 'a_pledge', 'b_pledge', 'coast', 'a'].indexOf(key) === -1) {
|
||||
return val.toNumber();
|
||||
}
|
||||
if (key === 'rcv' || key === 'spn') {
|
||||
|
|
@ -582,6 +582,12 @@ export class BackendService {
|
|||
this.runCommand('get_tx_pool_info', {}, callback);
|
||||
}
|
||||
|
||||
getVersion(callback) {
|
||||
this.runCommand('get_version', {}, (status, version) => {
|
||||
callback(version);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -615,11 +621,7 @@ export class BackendService {
|
|||
this.runCommand('reset_wallet_password', {wallet_id: wallet_id, pass: pass}, callback);
|
||||
},
|
||||
|
||||
getVersion: function (callback) {
|
||||
this.runCommand('get_version', {}, function (status, version) {
|
||||
callback(version)
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
getOsVersion: function (callback) {
|
||||
this.runCommand('get_os_version', {}, function (status, version) {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,9 @@ export class VariablesService {
|
|||
public default_fee_big = new BigNumber('10000000000');
|
||||
|
||||
public settings = {
|
||||
appLockTime: 15,
|
||||
theme: '',
|
||||
scale: 10,
|
||||
language: 'en',
|
||||
default_path: '/',
|
||||
viewedContracts: [],
|
||||
|
|
@ -41,6 +43,8 @@ export class VariablesService {
|
|||
public aliases: any = [];
|
||||
public aliasesChecked: any = {};
|
||||
public enableAliasSearch = false;
|
||||
public maxWalletNameLength = 25;
|
||||
public maxCommentLength = 255;
|
||||
|
||||
getHeightAppEvent = new BehaviorSubject(null);
|
||||
getRefreshStackingEvent = new BehaviorSubject(null);
|
||||
|
|
@ -48,7 +52,6 @@ export class VariablesService {
|
|||
|
||||
public idle = new Idle()
|
||||
.whenNotInteractive()
|
||||
.within(15)
|
||||
.do(() => {
|
||||
this.ngZone.run(() => {
|
||||
this.idle.stop();
|
||||
|
|
@ -59,6 +62,7 @@ export class VariablesService {
|
|||
|
||||
public allContextMenu: ContextMenuComponent;
|
||||
public onlyCopyContextMenu: ContextMenuComponent;
|
||||
public pasteSelectContextMenu: ContextMenuComponent;
|
||||
|
||||
constructor(private router: Router, private ngZone: NgZone, private contextMenuService: ContextMenuService) {
|
||||
}
|
||||
|
|
@ -96,13 +100,17 @@ export class VariablesService {
|
|||
}
|
||||
|
||||
startCountdown() {
|
||||
this.idle.start();
|
||||
this.idle.within(this.settings.appLockTime).start();
|
||||
}
|
||||
|
||||
stopCountdown() {
|
||||
this.idle.stop();
|
||||
}
|
||||
|
||||
restartCountdown() {
|
||||
this.idle.within(this.settings.appLockTime).restart();
|
||||
}
|
||||
|
||||
public onContextMenu($event: MouseEvent): void {
|
||||
$event.target['contextSelectionStart'] = $event.target['selectionStart'];
|
||||
$event.target['contextSelectionEnd'] = $event.target['selectionEnd'];
|
||||
|
|
@ -127,4 +135,23 @@ export class VariablesService {
|
|||
$event.stopPropagation();
|
||||
}
|
||||
|
||||
public onContextMenuPasteSelect($event: MouseEvent): void {
|
||||
$event.target['contextSelectionStart'] = $event.target['selectionStart'];
|
||||
$event.target['contextSelectionEnd'] = $event.target['selectionEnd'];
|
||||
|
||||
console.warn($event.target);
|
||||
console.warn($event.target['disabled']);
|
||||
|
||||
|
||||
if ($event.target && ($event.target['nodeName'].toUpperCase() === 'TEXTAREA' || $event.target['nodeName'].toUpperCase() === 'INPUT') && !$event.target['readOnly']) {
|
||||
this.contextMenuService.show.next({
|
||||
contextMenu: this.pasteSelectContextMenu,
|
||||
event: $event,
|
||||
item: $event.target,
|
||||
});
|
||||
$event.preventDefault();
|
||||
$event.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
<app-sidebar *ngIf="variablesService.appPass"></app-sidebar>
|
||||
|
||||
<div class="app-content scrolled-content">
|
||||
<router-outlet></router-outlet>
|
||||
<router-outlet *ngIf="[0, 1, 2].indexOf(variablesService.daemon_state) !== -1"></router-outlet>
|
||||
<div class="preloader" *ngIf="[3, 4, 5].indexOf(variablesService.daemon_state) !== -1">
|
||||
<span *ngIf="variablesService.daemon_state === 3">{{ 'SIDEBAR.SYNCHRONIZATION.LOADING' | translate }}</span>
|
||||
<span *ngIf="variablesService.daemon_state === 4">{{ 'SIDEBAR.SYNCHRONIZATION.ERROR' | translate }}</span>
|
||||
<span *ngIf="variablesService.daemon_state === 5">{{ 'SIDEBAR.SYNCHRONIZATION.COMPLETE' | translate }}</span>
|
||||
<span class="loading-bar"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<context-menu #allContextMenu>
|
||||
<ng-template contextMenuItem (execute)="contextMenuCopy($event.item)">{{ 'CONTEXT_MENU.COPY' | translate }}</ng-template>
|
||||
<ng-template contextMenuItem (execute)="contextMenuPaste($event.item)">{{ 'CONTEXT_MENU.PASTE' | translate }}</ng-template>
|
||||
|
|
@ -14,3 +19,8 @@
|
|||
<context-menu #onlyCopyContextMenu>
|
||||
<ng-template contextMenuItem (execute)="contextMenuOnlyCopy($event.item)">{{ 'CONTEXT_MENU.COPY' | translate }}</ng-template>
|
||||
</context-menu>
|
||||
|
||||
<context-menu #pasteSelectContextMenu>
|
||||
<ng-template contextMenuItem (execute)="contextMenuPaste($event.item)">{{ 'CONTEXT_MENU.PASTE' | translate }}</ng-template>
|
||||
<ng-template contextMenuItem (execute)="contextMenuSelect($event.item)">{{ 'CONTEXT_MENU.SELECT' | translate }}</ng-template>
|
||||
</context-menu>
|
||||
|
|
|
|||
|
|
@ -5,4 +5,54 @@
|
|||
overflow-x: overlay;
|
||||
overflow-y: hidden;
|
||||
width: 100%;
|
||||
|
||||
.preloader {
|
||||
align-self: center;
|
||||
color: #fff;
|
||||
font-size: 2rem;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
width: 50%;
|
||||
|
||||
.loading-bar {
|
||||
display: block;
|
||||
animation: move 5s linear infinite;
|
||||
background-image:
|
||||
-webkit-gradient(
|
||||
linear, 0 0, 100% 100%,
|
||||
color-stop(.125, rgba(0, 0, 0, .15)), color-stop(.125, transparent),
|
||||
color-stop(.250, transparent), color-stop(.250, rgba(0, 0, 0, .10)),
|
||||
color-stop(.375, rgba(0, 0, 0, .10)), color-stop(.375, transparent),
|
||||
color-stop(.500, transparent), color-stop(.500, rgba(0, 0, 0, .15)),
|
||||
color-stop(.625, rgba(0, 0, 0, .15)), color-stop(.625, transparent),
|
||||
color-stop(.750, transparent), color-stop(.750, rgba(0, 0, 0, .10)),
|
||||
color-stop(.875, rgba(0, 0, 0, .10)), color-stop(.875, transparent),
|
||||
to(transparent)
|
||||
),
|
||||
-webkit-gradient(
|
||||
linear, 0 100%, 100% 0,
|
||||
color-stop(.125, rgba(0, 0, 0, .30)), color-stop(.125, transparent),
|
||||
color-stop(.250, transparent), color-stop(.250, rgba(0, 0, 0, .25)),
|
||||
color-stop(.375, rgba(0, 0, 0, .25)), color-stop(.375, transparent),
|
||||
color-stop(.500, transparent), color-stop(.500, rgba(0, 0, 0, .30)),
|
||||
color-stop(.625, rgba(0, 0, 0, .30)), color-stop(.625, transparent),
|
||||
color-stop(.750, transparent), color-stop(.750, rgba(0, 0, 0, .25)),
|
||||
color-stop(.875, rgba(0, 0, 0, .25)), color-stop(.875, transparent),
|
||||
to(transparent)
|
||||
);
|
||||
background-size: 10rem 10rem;
|
||||
margin-top: 2rem;
|
||||
width: 100%;
|
||||
height: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes move {
|
||||
0% {
|
||||
background-position: 100% -10rem;
|
||||
}
|
||||
100% {
|
||||
background-position: 100% 10rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||
this.ngZone.run(() => {
|
||||
this.router.navigate(['/']);
|
||||
});
|
||||
this.variablesService.daemon_state = 5;
|
||||
this.backend.storeSecureAppData(() => {
|
||||
this.backend.storeAppData(() => {
|
||||
const recursionCloseWallets = () => {
|
||||
|
|
@ -472,6 +473,9 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||
} else {
|
||||
this.renderer.addClass(document.body, 'theme-' + this.variablesService.defaultTheme);
|
||||
}
|
||||
if (this.variablesService.settings.hasOwnProperty('scale') && [7.5, 10, 12.5, 15].indexOf(this.variablesService.settings.scale) !== -1) {
|
||||
this.renderer.setStyle(document.documentElement, 'font-size', this.variablesService.settings.scale + 'px');
|
||||
}
|
||||
} else {
|
||||
this.variablesService.settings.theme = this.variablesService.defaultTheme;
|
||||
this.renderer.addClass(document.body, 'theme-' + this.variablesService.settings.theme);
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import { HttpClient, HttpClientModule } from '@angular/common/http';
|
|||
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
|
||||
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { NgSelectModule } from '@ng-select/ng-select';
|
||||
|
||||
import { BackendService } from './_helpers/services/backend.service';
|
||||
import { ModalService } from './_helpers/services/modal.service';
|
||||
|
|
@ -51,14 +52,16 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
|
|||
|
||||
|
||||
import { ChartModule, HIGHCHARTS_MODULES, Highcharts} from 'angular-highcharts';
|
||||
import { ProgressContainerComponent } from './_helpers/directives/progress-container/progress-container.component';
|
||||
import { InputDisableSelectionDirective } from './_helpers/directives/input-disable-selection/input-disable-selection.directive';
|
||||
// import * as more from 'highcharts/highcharts-more.src';
|
||||
// import * as exporting from 'highcharts/modules/exporting.src';
|
||||
// import * as highstock from 'highcharts/modules/stock.src';
|
||||
|
||||
Highcharts.setOptions({
|
||||
// global: {
|
||||
// useUTC: false
|
||||
// }
|
||||
global: {
|
||||
useUTC: false
|
||||
}
|
||||
});
|
||||
|
||||
@NgModule({
|
||||
|
|
@ -94,7 +97,9 @@ Highcharts.setOptions({
|
|||
TooltipDirective,
|
||||
InputValidateDirective,
|
||||
ModalContainerComponent,
|
||||
TransactionDetailsComponent
|
||||
TransactionDetailsComponent,
|
||||
ProgressContainerComponent,
|
||||
InputDisableSelectionDirective
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
|
@ -109,6 +114,7 @@ Highcharts.setOptions({
|
|||
}),
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
NgSelectModule,
|
||||
ChartModule,
|
||||
ContextMenuModule.forRoot()
|
||||
],
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<span [routerLink]="['/wallet/' + wallet.wallet_id + '/history']">{{ wallet.name }}</span>
|
||||
<span>{{ 'BREADCRUMBS.ASSIGN_ALIAS' | translate }}</span>
|
||||
</div>
|
||||
<button class="back-btn" (click)="back()">
|
||||
<button type="button" class="back-btn" (click)="back()">
|
||||
<i class="icon back"></i>
|
||||
<span>{{ 'COMMON.BACK' | translate }}</span>
|
||||
</button>
|
||||
|
|
@ -45,7 +45,16 @@
|
|||
<label for="alias-comment" tooltip="{{ 'ASSIGN_ALIAS.COMMENT.TOOLTIP' | translate }}" placement="bottom-left" tooltipClass="table-tooltip assign-alias-tooltip" [delay]="500">
|
||||
{{ 'ASSIGN_ALIAS.COMMENT.LABEL' | translate }}
|
||||
</label>
|
||||
<textarea id="alias-comment" formControlName="comment" placeholder="{{ 'ASSIGN_ALIAS.COMMENT.PLACEHOLDER' | translate }}" (contextmenu)="variablesService.onContextMenu($event)"></textarea>
|
||||
<textarea id="alias-comment"
|
||||
class="scrolled-content"
|
||||
formControlName="comment"
|
||||
placeholder="{{ 'ASSIGN_ALIAS.COMMENT.PLACEHOLDER' | translate }}"
|
||||
[maxLength]="variablesService.maxCommentLength"
|
||||
(contextmenu)="variablesService.onContextMenu($event)">
|
||||
</textarea>
|
||||
<div class="error-block" *ngIf="assignForm.get('comment').value.length >= variablesService.maxCommentLength">
|
||||
{{ 'ASSIGN_ALIAS.FORM_ERRORS.MAX_LENGTH' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alias-cost">{{ "ASSIGN_ALIAS.COST" | translate : {value: alias.price | intToMoney, currency: variablesService.defaultCurrency} }}</div>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,13 @@ export class AssignAliasComponent implements OnInit, OnDestroy {
|
|||
wallet: Wallet;
|
||||
assignForm = new FormGroup({
|
||||
name: new FormControl('', [Validators.required, Validators.pattern(/^@?[a-z0-9\.\-]{6,25}$/)]),
|
||||
comment: new FormControl('')
|
||||
comment: new FormControl('', [(g: FormControl) => {
|
||||
if (g.value > this.variablesService.maxCommentLength) {
|
||||
return {'maxLength': true};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}])
|
||||
});
|
||||
assignFormSubscription: Subscription;
|
||||
alias = {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
<div>{{item.private_detailes.to_pay | intToMoney}} {{variablesService.defaultCurrency}}</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="status" tooltip="{{ item | contractStatusMessages }}" placement="top" tooltipClass="table-tooltip" [delay]="500">
|
||||
<div class="status" [class.error-text]="item.state === 4" tooltip="{{ item | contractStatusMessages }}" placement="top" tooltipClass="table-tooltip" [delay]="500">
|
||||
{{item | contractStatusMessages}}
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -68,8 +68,10 @@
|
|||
}
|
||||
|
||||
.status, .comment {
|
||||
display: inline-block;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,17 +5,17 @@
|
|||
<span [routerLink]="['/main']">{{ 'BREADCRUMBS.ADD_WALLET' | translate }}</span>
|
||||
<span>{{ 'BREADCRUMBS.CREATE_WALLET' | translate }}</span>
|
||||
</div>
|
||||
<a class="back-btn" [routerLink]="['/main']">
|
||||
<button type="button" class="back-btn" [routerLink]="['/main']">
|
||||
<i class="icon back"></i>
|
||||
<span>{{ 'COMMON.BACK' | translate }}</span>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form class="form-create" [formGroup]="createForm">
|
||||
|
||||
<div class="input-block">
|
||||
<label for="wallet-name">{{ 'CREATE_WALLET.NAME' | translate }}</label>
|
||||
<input type="text" id="wallet-name" formControlName="name" [attr.disabled]="walletSaved ? '' : null">
|
||||
<input type="text" id="wallet-name" formControlName="name" [attr.readonly]="walletSaved ? '' : null" [maxlength]="variablesService.maxWalletNameLength" (contextmenu)="variablesService.onContextMenu($event)">
|
||||
<div class="error-block" *ngIf="createForm.controls['name'].invalid && (createForm.controls['name'].dirty || createForm.controls['name'].touched)">
|
||||
<div *ngIf="createForm.controls['name'].errors['required']">
|
||||
{{ 'CREATE_WALLET.FORM_ERRORS.NAME_REQUIRED' | translate }}
|
||||
|
|
@ -24,16 +24,19 @@
|
|||
{{ 'CREATE_WALLET.FORM_ERRORS.NAME_DUPLICATE' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="error-block" *ngIf="createForm.get('name').value.length >= variablesService.maxWalletNameLength">
|
||||
{{ 'CREATE_WALLET.FORM_ERRORS.MAX_LENGTH' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-block">
|
||||
<label for="wallet-password">{{ 'CREATE_WALLET.PASS' | translate }}</label>
|
||||
<input type="password" id="wallet-password" formControlName="password" [attr.disabled]="walletSaved ? '' : null">
|
||||
<input type="password" id="wallet-password" formControlName="password" [attr.readonly]="walletSaved ? '' : null" (contextmenu)="variablesService.onContextMenuPasteSelect($event)">
|
||||
</div>
|
||||
|
||||
<div class="input-block">
|
||||
<label for="confirm-wallet-password">{{ 'CREATE_WALLET.CONFIRM' | translate }}</label>
|
||||
<input type="password" id="confirm-wallet-password" formControlName="confirm" [attr.disabled]="walletSaved ? '' : null">
|
||||
<input type="password" id="confirm-wallet-password" formControlName="confirm" [attr.readonly]="walletSaved ? '' : null" (contextmenu)="variablesService.onContextMenuPasteSelect($event)">
|
||||
<div class="error-block" *ngIf="createForm.controls['password'].dirty && createForm.controls['confirm'].dirty && createForm.errors">
|
||||
<div *ngIf="createForm.errors['confirm_mismatch']">
|
||||
{{ 'CREATE_WALLET.FORM_ERRORS.CONFIRM_NOT_MATCH' | translate }}
|
||||
|
|
@ -42,7 +45,7 @@
|
|||
</div>
|
||||
|
||||
<div class="wrap-buttons">
|
||||
<button type="button" class="transparent-button" *ngIf="walletSaved" disabled><i class="icon"></i>{{createForm.controls['name'].value}}</button>
|
||||
<button type="button" class="transparent-button" *ngIf="walletSaved" disabled><i class="icon"></i>{{walletSavedName}}</button>
|
||||
<button type="button" class="blue-button select-button" (click)="saveWallet()" [disabled]="!createForm.valid" *ngIf="!walletSaved">{{ 'CREATE_WALLET.BUTTON_SELECT' | translate }}</button>
|
||||
<button type="button" class="blue-button create-button" (click)="createWallet()" [disabled]="!walletSaved">{{ 'CREATE_WALLET.BUTTON_CREATE' | translate }}</button>
|
||||
</div>
|
||||
|
|
@ -50,3 +53,5 @@
|
|||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<app-progress-container [width]="progressWidth" [labels]="['PROGRESS.ADD_WALLET', 'PROGRESS.SELECT_LOCATION', 'PROGRESS.CREATE_WALLET']"></app-progress-container>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
:host {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.form-create {
|
||||
margin: 2.4rem 0;
|
||||
width: 50%;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ export class CreateWalletComponent implements OnInit {
|
|||
};
|
||||
|
||||
walletSaved = false;
|
||||
walletSavedName = '';
|
||||
progressWidth = '9rem';
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
|
|
@ -50,15 +52,17 @@ export class CreateWalletComponent implements OnInit {
|
|||
|
||||
createWallet() {
|
||||
this.ngZone.run(() => {
|
||||
this.progressWidth = '100%';
|
||||
this.router.navigate(['/seed-phrase'], {queryParams: {wallet_id: this.wallet.id}});
|
||||
});
|
||||
}
|
||||
|
||||
saveWallet() {
|
||||
if (this.createForm.valid) {
|
||||
if (this.createForm.valid && this.createForm.get('name').value.length <= this.variablesService.maxWalletNameLength) {
|
||||
this.backend.saveFileDialog(this.translate.instant('CREATE_WALLET.TITLE_SAVE'), '*', this.variablesService.settings.default_path, (file_status, file_data) => {
|
||||
if (file_status) {
|
||||
this.variablesService.settings.default_path = file_data.path.substr(0, file_data.path.lastIndexOf('/'));
|
||||
this.walletSavedName = file_data.path.substr(file_data.path.lastIndexOf('/') + 1, file_data.path.length - 1);
|
||||
this.backend.generateWallet(file_data.path, this.createForm.get('password').value, (generate_status, generate_data, errorCode) => {
|
||||
if (generate_status) {
|
||||
this.wallet.id = generate_data.wallet_id;
|
||||
|
|
@ -75,6 +79,7 @@ export class CreateWalletComponent implements OnInit {
|
|||
);
|
||||
this.ngZone.run(() => {
|
||||
this.walletSaved = true;
|
||||
this.progressWidth = '50%';
|
||||
});
|
||||
} else {
|
||||
if (errorCode && errorCode === 'ALREADY_EXISTS') {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<span [routerLink]="['/wallet/' + wallet.wallet_id + '/history']">{{ wallet.name }}</span>
|
||||
<span>{{ 'BREADCRUMBS.EDIT_ALIAS' | translate }}</span>
|
||||
</div>
|
||||
<button class="back-btn" (click)="back()">
|
||||
<button type="button" class="back-btn" (click)="back()">
|
||||
<i class="icon back"></i>
|
||||
<span>{{ 'COMMON.BACK' | translate }}</span>
|
||||
</button>
|
||||
|
|
@ -24,16 +24,26 @@
|
|||
<label for="alias-comment">
|
||||
{{ 'EDIT_ALIAS.COMMENT.LABEL' | translate }}
|
||||
</label>
|
||||
<textarea id="alias-comment" [(ngModel)]="alias.comment" [ngModelOptions]="{standalone: true}" placeholder="{{ 'EDIT_ALIAS.COMMENT.PLACEHOLDER' | translate }}" (contextmenu)="variablesService.onContextMenu($event)"></textarea>
|
||||
<textarea id="alias-comment"
|
||||
class="scrolled-content"
|
||||
[(ngModel)]="alias.comment"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
[maxlength]="variablesService.maxCommentLength"
|
||||
(contextmenu)="variablesService.onContextMenu($event)"
|
||||
placeholder="{{ 'EDIT_ALIAS.COMMENT.PLACEHOLDER' | translate }}">
|
||||
</textarea>
|
||||
<div class="error-block" *ngIf="alias.comment.length > 0 && notEnoughMoney">
|
||||
{{ 'EDIT_ALIAS.FORM_ERRORS.NO_MONEY' | translate }}
|
||||
</div>
|
||||
<div class="error-block" *ngIf="alias.comment.length >= variablesService.maxCommentLength">
|
||||
{{ 'EDIT_ALIAS.FORM_ERRORS.MAX_LENGTH' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alias-cost">{{ "EDIT_ALIAS.COST" | translate : {value: variablesService.default_fee, currency: variablesService.defaultCurrency} }}</div>
|
||||
|
||||
<div class="wrap-buttons">
|
||||
<button type="button" class="blue-button" (click)="updateAlias()" [disabled]="notEnoughMoney || oldAliasComment === alias.comment">{{ 'EDIT_ALIAS.BUTTON_EDIT' | translate }}</button>
|
||||
<button type="button" class="blue-button" (click)="updateAlias()" [disabled]="notEnoughMoney || (oldAliasComment === alias.comment) || alias.comment.length > variablesService.maxCommentLength">{{ 'EDIT_ALIAS.BUTTON_EDIT' | translate }}</button>
|
||||
<button type="button" class="blue-button" (click)="back()">{{ 'EDIT_ALIAS.BUTTON_CANCEL' | translate }}</button>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export class EditAliasComponent implements OnInit {
|
|||
}
|
||||
|
||||
updateAlias() {
|
||||
if (this.requestProcessing || this.notEnoughMoney || this.oldAliasComment === this.alias.comment) {
|
||||
if (this.requestProcessing || this.notEnoughMoney || this.oldAliasComment === this.alias.comment || this.alias.comment.length > this.variablesService.maxCommentLength) {
|
||||
return;
|
||||
}
|
||||
this.requestProcessing = true;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
<div class="input-block">
|
||||
<label for="master-pass">{{ 'LOGIN.SETUP_MASTER_PASS' | translate }}</label>
|
||||
<input type="password" id="master-pass" formControlName="password">
|
||||
<input type="password" id="master-pass" formControlName="password" (contextmenu)="variablesService.onContextMenuPasteSelect($event)">
|
||||
<div class="error-block" *ngIf="regForm.controls['password'].invalid && (regForm.controls['password'].dirty || regForm.controls['password'].touched)">
|
||||
<div *ngIf="regForm.controls['password'].errors['required']">
|
||||
{{ 'LOGIN.FORM_ERRORS.PASS_REQUIRED' | translate }}
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
<div class="input-block">
|
||||
<label for="confirm-pass">{{ 'LOGIN.SETUP_CONFIRM_PASS' | translate }}</label>
|
||||
<input type="password" id="confirm-pass" formControlName="confirmation">
|
||||
<input type="password" id="confirm-pass" formControlName="confirmation" (contextmenu)="variablesService.onContextMenuPasteSelect($event)">
|
||||
<div class="error-block" *ngIf="regForm.controls['confirmation'].invalid && (regForm.controls['confirmation'].dirty || regForm.controls['confirmation'].touched)">
|
||||
<div *ngIf="regForm.controls['confirmation'].errors['required']">
|
||||
{{ 'LOGIN.FORM_ERRORS.CONFIRM_REQUIRED' | translate }}
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
|
||||
<div class="input-block">
|
||||
<label for="master-pass-login">{{ 'LOGIN.MASTER_PASS' | translate }}</label>
|
||||
<input type="password" id="master-pass-login" formControlName="password" autofocus>
|
||||
<input type="password" id="master-pass-login" formControlName="password" autofocus (contextmenu)="variablesService.onContextMenuPasteSelect($event)">
|
||||
<div class="error-block" *ngIf="authForm.controls['password'].invalid && (authForm.controls['password'].dirty || authForm.controls['password'].touched)">
|
||||
<div *ngIf="authForm.controls['password'].errors['required']">
|
||||
{{ 'LOGIN.FORM_ERRORS.PASS_REQUIRED' | translate }}
|
||||
|
|
|
|||
|
|
@ -78,48 +78,46 @@ export class LoginComponent implements OnInit, OnDestroy {
|
|||
let openWallets = 0;
|
||||
let runWallets = 0;
|
||||
data.forEach((wallet, wallet_index) => {
|
||||
this.backend.openWallet(wallet.path, wallet.pass, true, (open_status, open_data) => {
|
||||
if (open_status) {
|
||||
this.backend.openWallet(wallet.path, wallet.pass, true, (open_status, open_data, open_error) => {
|
||||
if (open_status || open_error === 'FILE_RESTORED') {
|
||||
openWallets++;
|
||||
this.ngZone.run(() => {
|
||||
const new_wallet = new Wallet(
|
||||
open_data.wallet_id,
|
||||
wallet.name,
|
||||
wallet.pass,
|
||||
open_data['wi'].path,
|
||||
open_data['wi'].address,
|
||||
open_data['wi'].balance,
|
||||
open_data['wi'].unlocked_balance,
|
||||
open_data['wi'].mined_total,
|
||||
open_data['wi'].tracking_hey
|
||||
);
|
||||
new_wallet.alias = this.backend.getWalletAlias(new_wallet.address);
|
||||
if (open_data.recent_history && open_data.recent_history.history) {
|
||||
new_wallet.prepareHistory(open_data.recent_history.history);
|
||||
}
|
||||
this.backend.getContracts(open_data.wallet_id, (contracts_status, contracts_data) => {
|
||||
if (contracts_status && contracts_data.hasOwnProperty('contracts')) {
|
||||
this.ngZone.run(() => {
|
||||
new_wallet.prepareContractsAfterOpen(contracts_data.contracts, this.variablesService.exp_med_ts, this.variablesService.height_app, this.variablesService.settings.viewedContracts, this.variablesService.settings.notViewedContracts);
|
||||
});
|
||||
}
|
||||
});
|
||||
this.variablesService.wallets.push(new_wallet);
|
||||
if (this.variablesService.wallets.length === 1) {
|
||||
this.router.navigate(['/wallet/' + this.variablesService.wallets[0].wallet_id]);
|
||||
}
|
||||
});
|
||||
this.backend.runWallet(open_data.wallet_id, (run_status) => {
|
||||
if (run_status) {
|
||||
runWallets++;
|
||||
this.ngZone.run(() => {
|
||||
const new_wallet = new Wallet(
|
||||
open_data.wallet_id,
|
||||
wallet.name,
|
||||
wallet.pass,
|
||||
open_data['wi'].path,
|
||||
open_data['wi'].address,
|
||||
open_data['wi'].balance,
|
||||
open_data['wi'].unlocked_balance,
|
||||
open_data['wi'].mined_total,
|
||||
open_data['wi'].tracking_hey
|
||||
);
|
||||
new_wallet.alias = this.backend.getWalletAlias(new_wallet.address);
|
||||
if (open_data.recent_history && open_data.recent_history.history) {
|
||||
new_wallet.prepareHistory(open_data.recent_history.history);
|
||||
}
|
||||
this.backend.getContracts(open_data.wallet_id, (contracts_status, contracts_data) => {
|
||||
if (contracts_status && contracts_data.hasOwnProperty('contracts')) {
|
||||
this.ngZone.run(() => {
|
||||
new_wallet.prepareContractsAfterOpen(contracts_data.contracts, this.variablesService.exp_med_ts, this.variablesService.height_app, this.variablesService.settings.viewedContracts, this.variablesService.settings.notViewedContracts);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.variablesService.wallets.push(new_wallet);
|
||||
if (this.variablesService.wallets.length === 1) {
|
||||
this.router.navigate(['/wallet/' + this.variablesService.wallets[0].wallet_id]);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (wallet_index === data.length - 1 && runWallets === 0) {
|
||||
this.ngZone.run(() => {
|
||||
this.router.navigate(['/']);
|
||||
});
|
||||
}
|
||||
// console.log(run_data['error_code']);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,12 @@
|
|||
<div class="content">
|
||||
|
||||
<div class="head" *ngIf="variablesService.wallets.length > 0">
|
||||
<button type="button" class="back-btn" (click)="back()">
|
||||
<i class="icon back"></i>
|
||||
<span>{{ 'COMMON.BACK' | translate }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="add-wallet">
|
||||
<h3 class="add-wallet-title">{{ 'MAIN.TITLE' | translate }}</h3>
|
||||
<div class="add-wallet-buttons">
|
||||
|
|
@ -10,4 +18,5 @@
|
|||
<i class="icon"></i><span>{{ 'MAIN.HELP' | translate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@
|
|||
.content {
|
||||
padding: 3rem;
|
||||
min-height: 100%;
|
||||
|
||||
.head {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
.add-wallet {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import {Component, NgZone, OnInit} from '@angular/core';
|
||||
import {Location} from "@angular/common";
|
||||
import {BackendService} from '../_helpers/services/backend.service';
|
||||
import {VariablesService} from '../_helpers/services/variables.service';
|
||||
import {Router} from '@angular/router';
|
||||
|
|
@ -13,6 +14,7 @@ export class MainComponent implements OnInit {
|
|||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private location: Location,
|
||||
private backend: BackendService,
|
||||
private variablesService: VariablesService,
|
||||
private ngZone: NgZone,
|
||||
|
|
@ -35,7 +37,11 @@ export class MainComponent implements OnInit {
|
|||
}
|
||||
|
||||
openInBrowser() {
|
||||
this.backend.openUrlInBrowser('zano.org');
|
||||
this.backend.openUrlInBrowser('docs.zano.org/v1.0/docs/how-to-create-wallet');
|
||||
}
|
||||
|
||||
back() {
|
||||
this.location.back()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,17 +5,17 @@
|
|||
<span [routerLink]="['/main']">{{ 'BREADCRUMBS.ADD_WALLET' | translate }}</span>
|
||||
<span>{{ 'BREADCRUMBS.OPEN_WALLET' | translate }}</span>
|
||||
</div>
|
||||
<a class="back-btn" [routerLink]="['/main']">
|
||||
<button type="button" class="back-btn" [routerLink]="['/main']">
|
||||
<i class="icon back"></i>
|
||||
<span>{{ 'COMMON.BACK' | translate }}</span>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form class="form-open" [formGroup]="openForm">
|
||||
|
||||
<div class="input-block">
|
||||
<label for="wallet-name">{{ 'OPEN_WALLET.NAME' | translate }}</label>
|
||||
<input type="text" id="wallet-name" formControlName="name">
|
||||
<input type="text" id="wallet-name" formControlName="name" [maxLength]="variablesService.maxWalletNameLength" (contextmenu)="variablesService.onContextMenu($event)">
|
||||
<div class="error-block" *ngIf="openForm.controls['name'].invalid && (openForm.controls['name'].dirty || openForm.controls['name'].touched)">
|
||||
<div *ngIf="openForm.controls['name'].errors['required']">
|
||||
{{ 'OPEN_WALLET.FORM_ERRORS.NAME_REQUIRED' | translate }}
|
||||
|
|
@ -24,11 +24,14 @@
|
|||
{{ 'OPEN_WALLET.FORM_ERRORS.NAME_DUPLICATE' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="error-block" *ngIf="openForm.get('name').value.length >= variablesService.maxWalletNameLength">
|
||||
{{ 'OPEN_WALLET.FORM_ERRORS.MAX_LENGTH' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-block">
|
||||
<label for="wallet-password">{{ 'OPEN_WALLET.PASS' | translate }}</label>
|
||||
<input type="password" id="wallet-password" formControlName="password">
|
||||
<input type="password" id="wallet-password" formControlName="password" (contextmenu)="variablesService.onContextMenuPasteSelect($event)">
|
||||
</div>
|
||||
|
||||
<div class="wrap-buttons">
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ export class OpenWalletComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
openWallet() {
|
||||
if (this.openForm.valid) {
|
||||
if (this.openForm.valid && this.openForm.get('name').value.length <= this.variablesService.maxWalletNameLength) {
|
||||
this.backend.openWallet(this.filePath, this.openForm.get('password').value, false, (open_status, open_data, open_error) => {
|
||||
if (open_error && open_error === 'FILE_NOT_FOUND') {
|
||||
let error_translate = this.translate.instant('OPEN_WALLET.FILE_NOT_FOUND1');
|
||||
|
|
@ -85,31 +85,31 @@ export class OpenWalletComponent implements OnInit, OnDestroy {
|
|||
});
|
||||
});
|
||||
} else {
|
||||
const new_wallet = new Wallet(
|
||||
open_data.wallet_id,
|
||||
this.openForm.get('name').value,
|
||||
this.openForm.get('password').value,
|
||||
open_data['wi'].path,
|
||||
open_data['wi'].address,
|
||||
open_data['wi'].balance,
|
||||
open_data['wi'].unlocked_balance,
|
||||
open_data['wi'].mined_total,
|
||||
open_data['wi'].tracking_hey
|
||||
);
|
||||
new_wallet.alias = this.backend.getWalletAlias(new_wallet.address);
|
||||
if (open_data.recent_history && open_data.recent_history.history) {
|
||||
new_wallet.prepareHistory(open_data.recent_history.history);
|
||||
}
|
||||
this.backend.getContracts(open_data.wallet_id, (contracts_status, contracts_data) => {
|
||||
if (contracts_status && contracts_data.hasOwnProperty('contracts')) {
|
||||
this.ngZone.run(() => {
|
||||
new_wallet.prepareContractsAfterOpen(contracts_data.contracts, this.variablesService.exp_med_ts, this.variablesService.height_app, this.variablesService.settings.viewedContracts, this.variablesService.settings.notViewedContracts);
|
||||
});
|
||||
}
|
||||
});
|
||||
this.variablesService.wallets.push(new_wallet);
|
||||
this.backend.runWallet(open_data.wallet_id, (run_status, run_data) => {
|
||||
if (run_status) {
|
||||
const new_wallet = new Wallet(
|
||||
open_data.wallet_id,
|
||||
this.openForm.get('name').value,
|
||||
this.openForm.get('password').value,
|
||||
open_data['wi'].path,
|
||||
open_data['wi'].address,
|
||||
open_data['wi'].balance,
|
||||
open_data['wi'].unlocked_balance,
|
||||
open_data['wi'].mined_total,
|
||||
open_data['wi'].tracking_hey
|
||||
);
|
||||
new_wallet.alias = this.backend.getWalletAlias(new_wallet.address);
|
||||
if (open_data.recent_history && open_data.recent_history.history) {
|
||||
new_wallet.prepareHistory(open_data.recent_history.history);
|
||||
}
|
||||
this.backend.getContracts(open_data.wallet_id, (contracts_status, contracts_data) => {
|
||||
if (contracts_status && contracts_data.hasOwnProperty('contracts')) {
|
||||
this.ngZone.run(() => {
|
||||
new_wallet.prepareContractsAfterOpen(contracts_data.contracts, this.variablesService.exp_med_ts, this.variablesService.height_app, this.variablesService.settings.viewedContracts, this.variablesService.settings.notViewedContracts);
|
||||
});
|
||||
}
|
||||
});
|
||||
this.variablesService.wallets.push(new_wallet);
|
||||
this.backend.storeSecureAppData((status, data) => {
|
||||
console.log('Store App Data', status, data);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
<div class="input-blocks-row">
|
||||
<div class="input-block">
|
||||
<label for="purchase-seller">{{ 'PURCHASE.SELLER' | translate }}</label>
|
||||
<input type="text" id="purchase-seller" formControlName="seller" [readonly]="!newPurchase" (contextmenu)="variablesService.onContextMenu($event)">
|
||||
<input type="text" id="purchase-seller" formControlName="seller" [readonly]="!newPurchase" appInputDisableSelection (contextmenu)="(!newPurchase) ? variablesService.onContextMenuOnlyCopy($event, purchaseForm.controls['seller'].value) : variablesService.onContextMenu($event)">
|
||||
<div class="error-block" *ngIf="purchaseForm.controls['seller'].invalid && (purchaseForm.controls['seller'].dirty || purchaseForm.controls['seller'].touched)">
|
||||
<div *ngIf="purchaseForm.controls['seller'].errors['required']">
|
||||
{{ 'PURCHASE.FORM_ERRORS.SELLER_REQUIRED' | translate }}
|
||||
|
|
@ -57,18 +57,21 @@
|
|||
|
||||
<div class="input-blocks-row">
|
||||
<div class="input-block">
|
||||
<label for="purchase-your-deposit">{{ 'PURCHASE.YOUR_DEPOSIT' | translate }}</label>
|
||||
<label for="purchase-your-deposit">{{ ( (currentContract && !currentContract.is_a) ? 'PURCHASE.BUYER_DEPOSIT' : 'PURCHASE.YOUR_DEPOSIT') | translate }}</label>
|
||||
<input type="text" id="purchase-your-deposit" formControlName="yourDeposit" appInputValidate="money" [readonly]="!newPurchase" (contextmenu)="variablesService.onContextMenu($event)">
|
||||
<div class="error-block" *ngIf="purchaseForm.controls['yourDeposit'].invalid && (purchaseForm.controls['yourDeposit'].dirty || purchaseForm.controls['yourDeposit'].touched)">
|
||||
<div *ngIf="purchaseForm.controls['yourDeposit'].errors['required']">
|
||||
{{ 'PURCHASE.FORM_ERRORS.YOUR_DEPOSIT_REQUIRED' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="error-block" *ngIf="purchaseForm.invalid && (purchaseForm.controls['yourDeposit'].dirty || purchaseForm.controls['amount'].touched) && purchaseForm.errors && purchaseForm.errors['your_deposit_too_small']">
|
||||
{{ 'PURCHASE.FORM_ERRORS.YOUR_DEPOSIT_TOO_SMALL' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-block">
|
||||
<div class="wrap-label">
|
||||
<label for="purchase-seller-deposit">{{ 'PURCHASE.SELLER_DEPOSIT' | translate }}</label>
|
||||
<label for="purchase-seller-deposit">{{ ( (currentContract && !currentContract.is_a) ? 'PURCHASE.YOUR_DEPOSIT' : 'PURCHASE.SELLER_DEPOSIT') | translate }}</label>
|
||||
<div class="checkbox-block">
|
||||
<input type="checkbox" id="purchase-same-amount" class="style-checkbox" formControlName="sameAmount" (change)="sameAmountChange()">
|
||||
<label for="purchase-same-amount">{{ 'PURCHASE.SAME_AMOUNT' | translate }}</label>
|
||||
|
|
@ -107,11 +110,15 @@
|
|||
</div>
|
||||
<div class="input-block" *ngIf="newPurchase">
|
||||
<label for="purchase-time">{{ 'PURCHASE.WAITING_TIME' | translate }}</label>
|
||||
<select id="purchase-time" formControlName="time">
|
||||
<option *ngFor="let title of [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24]" [value]="title">
|
||||
{{title}}
|
||||
</option>
|
||||
</select>
|
||||
<ng-select id="purchase-time" class="lock-selection-select"
|
||||
[clearable]="false"
|
||||
[searchable]="false"
|
||||
formControlName="time">
|
||||
<ng-option [value]="1">1 {{ 'PURCHASE.HOUR' | translate }}</ng-option>
|
||||
<ng-option *ngFor="let title of [2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24]" [value]="title">
|
||||
{{title}} {{ 'PURCHASE.HOURS' | translate }}
|
||||
</ng-option>
|
||||
</ng-select>
|
||||
</div>
|
||||
<div class="input-block">
|
||||
<label for="purchase-payment">{{ 'PURCHASE.PAYMENT' | translate }}</label>
|
||||
|
|
@ -148,7 +155,7 @@
|
|||
<span *ngIf="currentContract.state == 3">{{ 'PURCHASE.RECEIVED' | translate }}</span>
|
||||
|
||||
<span *ngIf="currentContract.state == 4">{{ 'PURCHASE.NOT_RECEIVED' | translate }}</span>
|
||||
<span *ngIf="currentContract.state == 4">{{ 'PURCHASE.NULLIFIED' | translate }}</span>
|
||||
<span *ngIf="currentContract.state == 4" class="error-text">{{ 'PURCHASE.NULLIFIED' | translate }}</span>
|
||||
|
||||
<span *ngIf="currentContract.state == 5">{{ 'PURCHASE.PROPOSAL_CANCEL_SELLER' | translate }}</span>
|
||||
<!--<span *ngIf="currentContract.state == 5" ng-bind="'(' + (contract.cancel_expiration_time | buyingTime : 2) + ')'"></span>-->
|
||||
|
|
@ -178,7 +185,7 @@
|
|||
<span *ngIf="currentContract.state == 3">{{ 'PURCHASE.RECEIVED' | translate }}</span>
|
||||
|
||||
<span *ngIf="currentContract.state == 4">{{ 'PURCHASE.NOT_RECEIVED' | translate }}</span>
|
||||
<span *ngIf="currentContract.state == 4">{{ 'PURCHASE.NULLIFIED' | translate }}</span>
|
||||
<span *ngIf="currentContract.state == 4" class="error-text">{{ 'PURCHASE.NULLIFIED' | translate }}</span>
|
||||
|
||||
<span *ngIf="currentContract.state == 5">{{ 'PURCHASE.PROPOSAL_CANCEL_BUYER' | translate }}</span>
|
||||
<!--<span *ngIf="currentContract.state == 5" ng-bind="'(' + (contract.cancel_expiration_time | buyingTime : 1) + ')'"></span>-->
|
||||
|
|
@ -206,14 +213,14 @@
|
|||
<button type="button" class="turquoise-button" (click)="ignoredContract();">{{'PURCHASE.BUTTON_IGNORE' | translate}}</button>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="currentContract.is_a && (currentContract.state == 201 || currentContract.state == 2 || currentContract.state == 120 || currentContract.state == 130)">
|
||||
<button type="button" class="blue-button" (click)="productNotGot();" [disabled]="currentContract.cancel_expiration_time == 0 && (currentContract.height == 0 || (variablesService.height_app - currentContract.height) < 10)">
|
||||
<ng-container *ngIf="!showNullify && !showTimeSelect && currentContract.is_a && (currentContract.state == 201 || currentContract.state == 2 || currentContract.state == 120 || currentContract.state == 130)">
|
||||
<button type="button" class="blue-button error-text" (click)="showNullify = true;" [disabled]="currentContract.cancel_expiration_time == 0 && (currentContract.height == 0 || (variablesService.height_app - currentContract.height) < 10)">
|
||||
{{'PURCHASE.BUTTON_NULLIFY' | translate}}
|
||||
</button>
|
||||
<button type="button" class="turquoise-button" (click)="dealsDetailsFinish();" [disabled]="currentContract.cancel_expiration_time == 0 && (currentContract.height == 0 || (variablesService.height_app - currentContract.height) < 10)">
|
||||
{{'PURCHASE.BUTTON_RECEIVED' | translate}}
|
||||
</button>
|
||||
<button type="button" class="green-button" (click)="dealsDetailsCancel();" [disabled]="currentContract.cancel_expiration_time == 0 && (currentContract.height == 0 || (variablesService.height_app - currentContract.height) < 10)">
|
||||
<button type="button" class="green-button" (click)="showTimeSelect = true;" [disabled]="currentContract.cancel_expiration_time == 0 && (currentContract.height == 0 || (variablesService.height_app - currentContract.height) < 10)">
|
||||
{{'PURCHASE.BUTTON_CANCEL_BUYER' | translate}}
|
||||
</button>
|
||||
</ng-container>
|
||||
|
|
@ -225,14 +232,33 @@
|
|||
|
||||
</div>
|
||||
|
||||
<div style="display: flex; justify-content: center;" *ngIf="!newPurchase && currentContract.is_a && (currentContract.state == 201 || currentContract.state == 2 || currentContract.state == 120 || currentContract.state == 130)">
|
||||
<div class="nullify-block-row" *ngIf="showNullify">
|
||||
<div class="input-block">
|
||||
<div>{{'PURCHASE.NULLIFY_QUESTION' | translate}}</div>
|
||||
<div class="nullify-block">
|
||||
<button type="button" class="blue-button" (click)="showNullify = false;">{{ 'PURCHASE.CANCEL' | translate }}</button>
|
||||
<button type="button" class="blue-button" (click)="productNotGot();">{{ 'PURCHASE.BUTTON_NULLIFY_SHORT' | translate }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="time-cancel-block-row" *ngIf="showTimeSelect && !newPurchase && currentContract.is_a && (currentContract.state == 201 || currentContract.state == 2 || currentContract.state == 120 || currentContract.state == 130)">
|
||||
<div class="input-block">
|
||||
<div>{{ 'PURCHASE.WAITING_TIME_QUESTION' | translate }}</div>
|
||||
<label for="purchase-timeCancel">{{ 'PURCHASE.WAITING_TIME' | translate }}</label>
|
||||
<select id="purchase-timeCancel" formControlName="timeCancel">
|
||||
<option *ngFor="let title of [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24]" [value]="title">
|
||||
{{title}}
|
||||
</option>
|
||||
</select>
|
||||
<ng-select id="purchase-timeCancel" class="lock-selection-select"
|
||||
[clearable]="false"
|
||||
[searchable]="false"
|
||||
formControlName="timeCancel">
|
||||
<ng-option [value]="1">1 {{ 'PURCHASE.HOUR' | translate }}</ng-option>
|
||||
<ng-option *ngFor="let title of [2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24]" [value]="title">
|
||||
{{title}} {{ 'PURCHASE.HOURS' | translate }}
|
||||
</ng-option>
|
||||
</ng-select>
|
||||
<div class="time-cancel-block">
|
||||
<button type="button" class="blue-button" (click)="showTimeSelect = false;">{{ 'PURCHASE.CANCEL' | translate }}</button>
|
||||
<button type="button" class="blue-button" (click)="dealsDetailsCancel();">{{ 'PURCHASE.BUTTON_CANCEL_BUYER' | translate }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -110,6 +110,49 @@
|
|||
margin: 0 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.nullify-block-row {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.input-block {
|
||||
align-items: center;
|
||||
|
||||
.nullify-block {
|
||||
margin: 1rem 0;
|
||||
|
||||
.blue-button {
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.time-cancel-block-row {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.input-block {
|
||||
align-items: center;
|
||||
margin-bottom: 0;
|
||||
|
||||
select {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.time-cancel-block {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
margin: 1rem 0;
|
||||
|
||||
.blue-button {
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.progress-bar-container {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {ModalService} from '../_helpers/services/modal.service';
|
|||
import {Location} from '@angular/common';
|
||||
import {IntToMoneyPipe} from '../_helpers/pipes/int-to-money.pipe';
|
||||
import {TranslateService} from '@ngx-translate/core';
|
||||
import {BigNumber} from 'bignumber.js';
|
||||
|
||||
@Component({
|
||||
selector: 'app-purchase',
|
||||
|
|
@ -53,14 +54,18 @@ export class PurchaseComponent implements OnInit, OnDestroy {
|
|||
sameAmount: new FormControl({value: false, disabled: false}),
|
||||
comment: new FormControl(''),
|
||||
fee: new FormControl(this.variablesService.default_fee),
|
||||
time: new FormControl({value: '12', disabled: false}),
|
||||
timeCancel: new FormControl({value: '12', disabled: false}),
|
||||
time: new FormControl({value: 12, disabled: false}),
|
||||
timeCancel: new FormControl({value: 12, disabled: false}),
|
||||
payment: new FormControl('')
|
||||
}, function (g: FormGroup) {
|
||||
return (new BigNumber(g.get('yourDeposit').value)).isLessThan(g.get('amount').value) ? {'your_deposit_too_small': true} : null;
|
||||
});
|
||||
|
||||
additionalOptions = false;
|
||||
currentContract = null;
|
||||
heightAppEvent;
|
||||
showTimeSelect = false;
|
||||
showNullify = false;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
|
|
@ -100,8 +105,8 @@ export class PurchaseComponent implements OnInit, OnDestroy {
|
|||
sameAmount: this.currentContract.private_detailes.to_pay.isEqualTo(this.currentContract.private_detailes.b_pledge),
|
||||
comment: this.currentContract.private_detailes.c,
|
||||
fee: this.variablesService.default_fee,
|
||||
time: '12',
|
||||
timeCancel: '12',
|
||||
time: 12,
|
||||
timeCancel: 12,
|
||||
payment: this.currentContract.payment_id
|
||||
});
|
||||
this.purchaseForm.get('sameAmount').disable();
|
||||
|
|
@ -153,13 +158,13 @@ export class PurchaseComponent implements OnInit, OnDestroy {
|
|||
this.currentContract.is_new = true;
|
||||
this.variablesService.currentWallet.recountNewContracts();
|
||||
}
|
||||
if (!this.newPurchase && this.currentContract.is_a && (this.currentContract.state === 201 || this.currentContract.state === 2 || this.currentContract.state === 120 || this.currentContract.state === 130)) {
|
||||
if (this.currentContract.cancel_expiration_time === 0 && (this.currentContract.height === 0 || (this.variablesService.height_app - this.currentContract.height) < 10)) {
|
||||
this.purchaseForm.get('timeCancel').disable();
|
||||
} else {
|
||||
this.purchaseForm.get('timeCancel').enable();
|
||||
}
|
||||
}
|
||||
// if (!this.newPurchase && this.currentContract.is_a && (this.currentContract.state === 201 || this.currentContract.state === 2 || this.currentContract.state === 120 || this.currentContract.state === 130)) {
|
||||
// if (this.currentContract.cancel_expiration_time === 0 && (this.currentContract.height === 0 || (this.variablesService.height_app - this.currentContract.height) < 10)) {
|
||||
// this.purchaseForm.get('timeCancel').disable();
|
||||
// } else {
|
||||
// this.purchaseForm.get('timeCancel').enable();
|
||||
// }
|
||||
// }
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,17 +5,17 @@
|
|||
<span [routerLink]="['/main']">{{ 'BREADCRUMBS.ADD_WALLET' | translate }}</span>
|
||||
<span>{{ 'BREADCRUMBS.RESTORE_WALLET' | translate }}</span>
|
||||
</div>
|
||||
<a class="back-btn" [routerLink]="['/main']">
|
||||
<button type="button" class="back-btn" [routerLink]="['/main']">
|
||||
<i class="icon back"></i>
|
||||
<span>{{ 'COMMON.BACK' | translate }}</span>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form class="form-restore" [formGroup]="restoreForm">
|
||||
|
||||
<div class="input-block half-block">
|
||||
<label for="wallet-name">{{ 'RESTORE_WALLET.LABEL_NAME' | translate }}</label>
|
||||
<input type="text" id="wallet-name" formControlName="name" [attr.disabled]="walletSaved ? '' : null">
|
||||
<input type="text" id="wallet-name" formControlName="name" [attr.readonly]="walletSaved ? '' : null" [maxLength]="variablesService.maxWalletNameLength" (contextmenu)="variablesService.onContextMenu($event)">
|
||||
<div class="error-block" *ngIf="restoreForm.controls['name'].invalid && (restoreForm.controls['name'].dirty || restoreForm.controls['name'].touched)">
|
||||
<div *ngIf="restoreForm.controls['name'].errors['required']">
|
||||
{{ 'RESTORE_WALLET.FORM_ERRORS.NAME_REQUIRED' | translate }}
|
||||
|
|
@ -24,16 +24,19 @@
|
|||
{{ 'RESTORE_WALLET.FORM_ERRORS.NAME_DUPLICATE' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="error-block" *ngIf="restoreForm.get('name').value.length >= variablesService.maxWalletNameLength">
|
||||
{{ 'RESTORE_WALLET.FORM_ERRORS.MAX_LENGTH' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-block half-block">
|
||||
<label for="wallet-password">{{ 'RESTORE_WALLET.PASS' | translate }}</label>
|
||||
<input type="password" id="wallet-password" formControlName="password" [attr.disabled]="walletSaved ? '' : null">
|
||||
<input type="password" id="wallet-password" formControlName="password" [attr.readonly]="walletSaved ? '' : null" (contextmenu)="variablesService.onContextMenuPasteSelect($event)">
|
||||
</div>
|
||||
|
||||
<div class="input-block half-block">
|
||||
<label for="confirm-wallet-password">{{ 'RESTORE_WALLET.CONFIRM' | translate }}</label>
|
||||
<input type="password" id="confirm-wallet-password" formControlName="confirm" [attr.disabled]="walletSaved ? '' : null">
|
||||
<input type="password" id="confirm-wallet-password" formControlName="confirm" [attr.readonly]="walletSaved ? '' : null" (contextmenu)="variablesService.onContextMenuPasteSelect($event)">
|
||||
<div class="error-block" *ngIf="restoreForm.controls['password'].dirty && restoreForm.controls['confirm'].dirty && restoreForm.errors">
|
||||
<div *ngIf="restoreForm.errors['confirm_mismatch']">
|
||||
{{ 'RESTORE_WALLET.FORM_ERRORS.CONFIRM_NOT_MATCH' | translate }}
|
||||
|
|
@ -43,7 +46,7 @@
|
|||
|
||||
<div class="input-block">
|
||||
<label for="phrase-key">{{ 'RESTORE_WALLET.LABEL_PHRASE_KEY' | translate }}</label>
|
||||
<input type="text" id="phrase-key" formControlName="key" [attr.disabled]="walletSaved ? '' : null" (contextmenu)="variablesService.onContextMenu($event)">
|
||||
<input type="text" id="phrase-key" formControlName="key" [attr.readonly]="walletSaved ? '' : null" (contextmenu)="variablesService.onContextMenu($event)">
|
||||
<div class="error-block" *ngIf="restoreForm.controls['key'].invalid && (restoreForm.controls['key'].dirty || restoreForm.controls['key'].touched)">
|
||||
<div *ngIf="restoreForm.controls['key'].errors['required']">
|
||||
{{ 'RESTORE_WALLET.FORM_ERRORS.KEY_REQUIRED' | translate }}
|
||||
|
|
@ -55,7 +58,7 @@
|
|||
</div>
|
||||
|
||||
<div class="wrap-buttons">
|
||||
<button type="button" class="transparent-button" *ngIf="walletSaved">{{restoreForm.controls['name'].value}}</button>
|
||||
<button type="button" class="transparent-button" *ngIf="walletSaved" disabled><i class="icon"></i>{{walletSavedName}}</button>
|
||||
<button type="button" class="blue-button select-button" (click)="saveWallet()" [disabled]="!restoreForm.valid" *ngIf="!walletSaved">{{ 'RESTORE_WALLET.BUTTON_SELECT' | translate }}</button>
|
||||
<button type="button" class="blue-button create-button" (click)="createWallet()" [disabled]="!walletSaved">{{ 'RESTORE_WALLET.BUTTON_CREATE' | translate }}</button>
|
||||
</div>
|
||||
|
|
@ -63,3 +66,5 @@
|
|||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<app-progress-container [width]="progressWidth" [labels]="['PROGRESS.ADD_WALLET', 'PROGRESS.SELECT_LOCATION', 'PROGRESS.RESTORE_WALLET']"></app-progress-container>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
:host {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.form-restore {
|
||||
margin: 2.4rem 0;
|
||||
width: 100%;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue