Merge pull request #1195 from jagerman/ledger-updates

Ledger updates
This commit is contained in:
Jason Rhinelander 2020-12-14 18:26:15 -04:00 committed by GitHub
commit 83d07ba55d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
98 changed files with 5424 additions and 6729 deletions

View File

@ -61,9 +61,6 @@ namespace epee
//! Append `src` as hex to `out`.
static void buffer(std::ostream& out, const span<const std::uint8_t> src);
//! Append `< + src + >` as hex to `out`.
static void formatted(std::ostream& out, const span<const std::uint8_t> src);
private:
template<typename T> T static convert(const span<const std::uint8_t> src);

View File

@ -75,13 +75,6 @@ namespace epee
write_hex(std::ostreambuf_iterator<char>{out}, src);
}
void to_hex::formatted(std::ostream& out, const span<const std::uint8_t> src)
{
out.put('<');
buffer(out, src);
out.put('>');
}
void to_hex::buffer_unchecked(char* out, const span<const std::uint8_t> src) noexcept
{
return write_hex(out, src);

View File

@ -33,6 +33,7 @@
#define LOKI_MINUTES(val) val * 60
#include <cstddef>
#include <utility>
#define LOKI_RPC_DOC_INTROSPECT
namespace loki
@ -51,6 +52,11 @@ public:
void invoke() { lambda(); cancelled = true; } // Invoke early instead of at destruction
void cancel() { cancelled = true; } // Cancel invocation at destruction
~deferred() { if (!cancelled) lambda(); }
deferred(deferred<lambda_t>&& d) : lambda{std::move(d.lambda)}, cancelled{d.cancelled} { d.cancel(); }
deferred& operator=(deferred<lambda_t>&& d) { lambda = std::move(d.lambda); cancelled = d.cancelled; d.cancel(); return *this; }
deferred(const deferred<lambda_t>&) = delete;
deferred& operator=(const deferred<lambda_t>&) = delete;
};
template <typename lambda_t>

View File

@ -41,10 +41,13 @@
#include "common/varint.h"
#include "epee/warnings.h"
#include "crypto.h"
extern "C" {
#include "keccak.h"
}
#include "hash.h"
namespace {
static void local_abort(const char *msg)
void local_abort(const char *msg)
{
fprintf(stderr, "%s\n", msg);
#ifdef NDEBUG
@ -69,66 +72,85 @@ namespace crypto {
#include "random.h"
}
// These nasty dirty hacks are unspeakable disgusting. This is only here because all of these
// have a `.data` element, but it is a `char` instead of an `unsigned char`. So rather than
// change it to `unsigned char`, the author decided that he should overload `&` to do a
// reinterpret_cast. WTF.
//
// TODO: fix this garbage by making the ec_ types use unsigned char instead of char.
// EW!
static inline unsigned char *operator &(ec_point &point) {
return &reinterpret_cast<unsigned char &>(point);
}
// EW!
static inline const unsigned char *operator &(const ec_point &point) {
return &reinterpret_cast<const unsigned char &>(point);
}
// EW!
static inline unsigned char *operator &(ec_scalar &scalar) {
return &reinterpret_cast<unsigned char &>(scalar);
}
// EW!
static inline const unsigned char *operator &(const ec_scalar &scalar) {
return &reinterpret_cast<const unsigned char &>(scalar);
}
static auto get_random_lock()
{
static std::mutex random_mutex;
return std::lock_guard{random_mutex};
}
static std::mutex random_mutex;
void generate_random_bytes_thread_safe(size_t N, uint8_t *bytes)
{
auto lock = get_random_lock();
std::lock_guard lock{random_mutex};
generate_random_bytes_not_thread_safe(N, bytes);
}
void add_extra_entropy_thread_safe(const void *ptr, size_t bytes)
{
auto lock = get_random_lock();
std::lock_guard lock{random_mutex};
add_extra_entropy_not_thread_safe(ptr, bytes);
}
static inline bool less32(const unsigned char *k0, const unsigned char *k1)
// 2^252+27742317777372353535851937790883648493
static constexpr unsigned char L[32] = {
0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10
};
// Returns true iff 32-byte, little-endian unsigned integer a is less than L
static inline bool sc_is_canonical(const unsigned char* a)
{
for (int n = 31; n >= 0; --n)
for (size_t n = 31; n < 32; --n)
{
if (k0[n] < k1[n])
if (a[n] < L[n])
return true;
if (k0[n] > k1[n])
if (a[n] > L[n])
return false;
}
return false;
}
void random32_unbiased(unsigned char *bytes)
void random_scalar(unsigned char *bytes)
{
// l = 2^252 + 27742317777372353535851937790883648493.
// it fits 15 in 32 bytes
static const unsigned char limit[32] = { 0xe3, 0x6a, 0x67, 0x72, 0x8b, 0xce, 0x13, 0x29, 0x8f, 0x30, 0x82, 0x8c, 0x0b, 0xa4, 0x10, 0x39, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0 };
std::lock_guard lock{random_mutex};
do
{
generate_random_bytes_thread_safe(32, bytes);
} while (!sc_isnonzero(bytes) && !less32(bytes, limit)); // should be good about 15/16 of the time
sc_reduce32(bytes);
generate_random_bytes_not_thread_safe(32, bytes);
bytes[31] &= 0b0001'1111; // Mask the 3 most significant bits off because no acceptable value ever has them set (the value would be >L)
} while (!(sc_is_canonical(bytes) && sc_isnonzero(bytes)));
}
/* generate a random 32-byte (256-bit) integer and copy it to res */
static inline void random_scalar(ec_scalar &res) {
random32_unbiased((unsigned char*)res.data);
/* generate a random ]0..L[ scalar */
void random_scalar(ec_scalar &res) {
random_scalar(reinterpret_cast<unsigned char*>(res.data));
}
ec_scalar random_scalar() {
ec_scalar res;
random_scalar(res);
return res;
}
void hash_to_scalar(const void *data, size_t length, ec_scalar &res) {
@ -138,8 +160,6 @@ namespace crypto {
/*
* generate public and secret keys from a random 256-bit integer
* TODO: allow specifying random value (for wallet recovery)
*
*/
secret_key generate_keys(public_key &pub, secret_key &sec, const secret_key& recovery_key, bool recover) {
ge_p3 point;
@ -290,8 +310,11 @@ namespace crypto {
sc_mulsub(&sig.r, &sig.c, &unwrap(sec), &k);
if (!sc_isnonzero((const unsigned char*)sig.r.data))
goto try_again;
memwipe(&k, sizeof(k));
}
static constexpr ec_point infinity = {{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
bool check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig) {
ge_p2 tmp2;
ge_p3 tmp3;
@ -306,9 +329,8 @@ namespace crypto {
if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0 || !sc_isnonzero(&sig.c)) {
return false;
}
ge_double_scalarmult_base_vartime(&tmp2, &sig.c, &tmp3, &sig.r);
ge_double_scalarmult_base_vartime(&tmp2, &sig.c, &tmp3, &sig.r); // tmp2 = sig.c A + sig.r G
ge_tobytes(&buf.comm, &tmp2);
static const ec_point infinity = {{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
if (memcmp(&buf.comm, &infinity, 32) == 0)
return false;
hash_to_scalar(&buf, sizeof(s_comm), c);
@ -354,8 +376,7 @@ namespace crypto {
#endif
// pick random k
ec_scalar k;
random_scalar(k);
ec_scalar k = random_scalar();
s_comm_2 buf;
buf.msg = prefix_hash;
@ -386,6 +407,8 @@ namespace crypto {
// sig.r = k - sig.c*r
sc_mulsub(&sig.r, &sig.c, &unwrap(r), &k);
memwipe(&k, sizeof(k));
}
bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const std::optional<public_key> &B, const public_key &D, const signature &sig) {
@ -493,33 +516,35 @@ namespace crypto {
ge_tobytes(&image, &point2);
}
PUSH_WARNINGS
DISABLE_VS_WARNINGS(4200)
struct ec_point_pair {
ec_point a, b;
};
struct rs_comm {
hash h;
struct ec_point_pair ab[];
hash prefix;
std::vector<std::pair<ec_point, ec_point>> ab;
rs_comm(const hash& h, size_t pubs_count) : prefix{h}, ab{pubs_count} {}
ec_scalar hash_to_scalar() const {
KECCAK_CTX state;
keccak_init(&state);
keccak_update(&state, reinterpret_cast<const uint8_t*>(&prefix), sizeof(prefix));
static_assert(sizeof(ab[0]) == 64); // Ensure no padding
keccak_update(&state, reinterpret_cast<const uint8_t*>(ab.data()), 64*ab.size());
ec_scalar result;
keccak_finish(&state, reinterpret_cast<uint8_t*>(&result));
sc_reduce32(&result);
return result;
};
};
POP_WARNINGS
static inline size_t rs_comm_size(size_t pubs_count) {
return sizeof(rs_comm) + pubs_count * sizeof(ec_point_pair);
}
void generate_ring_signature(
const hash& prefix_hash,
const key_image& image,
const std::vector<const public_key*>& pubs,
const secret_key& sec,
size_t sec_index,
signature* sig) {
assert(sec_index < pubs.size());
void generate_ring_signature(const hash &prefix_hash, const key_image &image,
const public_key *const *pubs, size_t pubs_count,
const secret_key &sec, size_t sec_index,
signature *sig) {
size_t i;
ge_p3 image_unp;
ge_dsmp image_pre;
ec_scalar sum, k, h;
std::shared_ptr<rs_comm> buf(reinterpret_cast<rs_comm *>(malloc(rs_comm_size(pubs_count))), free);
if (!buf)
local_abort("malloc failure");
assert(sec_index < pubs_count);
#if !defined(NDEBUG)
{
ge_p3 t;
@ -531,68 +556,76 @@ POP_WARNINGS
assert(*pubs[sec_index] == t2);
generate_key_image(*pubs[sec_index], sec, t3);
assert(image == t3);
for (i = 0; i < pubs_count; i++) {
for (size_t i = 0; i < pubs.size(); i++) {
assert(check_key(*pubs[i]));
}
}
#endif
ge_p3 image_unp; // I
if (ge_frombytes_vartime(&image_unp, &image) != 0) {
local_abort("invalid key image");
}
ge_dsmp image_pre;
ge_dsm_precomp(image_pre, &image_unp);
sc_0(&sum);
buf->h = prefix_hash;
for (i = 0; i < pubs_count; i++) {
ec_scalar sum;
sc_0(&sum); // will be sum of cj, j≠s
rs_comm rs{prefix_hash, pubs.size()};
ec_scalar qs;
for (size_t i = 0; i < pubs.size(); i++) {
ge_p2 tmp2;
ge_p3 tmp3;
if (i == sec_index) {
random_scalar(k);
ge_scalarmult_base(&tmp3, &k);
ge_p3_tobytes(&buf->ab[i].a, &tmp3);
hash_to_ec(*pubs[i], tmp3);
ge_scalarmult(&tmp2, &k, &tmp3);
ge_tobytes(&buf->ab[i].b, &tmp2);
if (i == sec_index) { // this is the true key image
random_scalar(qs); // qs = random
ge_scalarmult_base(&tmp3, &qs); // Ls = qs G
ge_p3_tobytes(&rs.ab[i].first, &tmp3);
hash_to_ec(*pubs[i], tmp3); // Hp(Ps)
ge_scalarmult(&tmp2, &qs, &tmp3); // Rs = qs Hp(Ps)
ge_tobytes(&rs.ab[i].second, &tmp2);
// We don't set ci, ri yet because we first need the sum of all the other cj's/rj's
} else {
random_scalar(sig[i].c);
random_scalar(sig[i].r);
random_scalar(sig[i].c); // ci = wi = random
random_scalar(sig[i].r); // ri = qi = random
if (ge_frombytes_vartime(&tmp3, &*pubs[i]) != 0) {
memwipe(&qs, sizeof(qs));
local_abort("invalid pubkey");
}
ge_double_scalarmult_base_vartime(&tmp2, &sig[i].c, &tmp3, &sig[i].r);
ge_tobytes(&buf->ab[i].a, &tmp2);
hash_to_ec(*pubs[i], tmp3);
ge_double_scalarmult_precomp_vartime(&tmp2, &sig[i].r, &tmp3, &sig[i].c, image_pre);
ge_tobytes(&buf->ab[i].b, &tmp2);
ge_double_scalarmult_base_vartime(&tmp2, &sig[i].c, &tmp3, &sig[i].r); // Li = cj Pj + rj G = qj G + wj Pj
ge_tobytes(&rs.ab[i].first, &tmp2);
hash_to_ec(*pubs[i], tmp3); // Hp(Pj)
ge_double_scalarmult_precomp_vartime(&tmp2, &sig[i].r, &tmp3, &sig[i].c, image_pre); // Ri = qj Hp(Pj) + wj I
ge_tobytes(&rs.ab[i].second, &tmp2);
sc_add(&sum, &sum, &sig[i].c);
}
}
hash_to_scalar(buf.get(), rs_comm_size(pubs_count), h);
sc_sub(&sig[sec_index].c, &h, &sum);
sc_mulsub(&sig[sec_index].r, &sig[sec_index].c, &unwrap(sec), &k);
ec_scalar c = rs.hash_to_scalar(); // c = Hs(prefix_hash || L0 || ... || L{n-1} || R0 || ... || R{n-1})
sc_sub(&sig[sec_index].c, &c, &sum); // cs = c - sum(ci, i≠s) = c - sum(wi)
sc_mulsub(&sig[sec_index].r, &sig[sec_index].c, &unwrap(sec), &qs); // rs = qs - cs*x
memwipe(&qs, sizeof(qs));
}
bool check_ring_signature(const hash &prefix_hash, const key_image &image,
const public_key *const *pubs, size_t pubs_count,
const signature *sig) {
size_t i;
ge_p3 image_unp;
ge_dsmp image_pre;
ec_scalar sum, h;
std::shared_ptr<rs_comm> buf(reinterpret_cast<rs_comm *>(malloc(rs_comm_size(pubs_count))), free);
if (!buf)
return false;
bool check_ring_signature(
const hash& prefix_hash,
const key_image& image,
const std::vector<const public_key*>& pubs,
const signature* sig) {
#if !defined(NDEBUG)
for (i = 0; i < pubs_count; i++) {
for (size_t i = 0; i < pubs.size(); i++) {
assert(check_key(*pubs[i]));
}
#endif
ge_p3 image_unp;
if (ge_frombytes_vartime(&image_unp, &image) != 0) {
return false;
}
ge_dsmp image_pre;
ge_dsm_precomp(image_pre, &image_unp);
ec_scalar sum;
sc_0(&sum);
buf->h = prefix_hash;
for (i = 0; i < pubs_count; i++) {
rs_comm rs{prefix_hash, pubs.size()};
for (size_t i = 0; i < pubs.size(); i++) {
ge_p2 tmp2;
ge_p3 tmp3;
if (sc_check(&sig[i].c) != 0 || sc_check(&sig[i].r) != 0) {
@ -602,14 +635,81 @@ POP_WARNINGS
return false;
}
ge_double_scalarmult_base_vartime(&tmp2, &sig[i].c, &tmp3, &sig[i].r);
ge_tobytes(&buf->ab[i].a, &tmp2);
ge_tobytes(&rs.ab[i].first, &tmp2);
hash_to_ec(*pubs[i], tmp3);
ge_double_scalarmult_precomp_vartime(&tmp2, &sig[i].r, &tmp3, &sig[i].c, image_pre);
ge_tobytes(&buf->ab[i].b, &tmp2);
ge_tobytes(&rs.ab[i].second, &tmp2);
sc_add(&sum, &sum, &sig[i].c);
}
hash_to_scalar(buf.get(), rs_comm_size(pubs_count), h);
ec_scalar h = rs.hash_to_scalar();
sc_sub(&h, &h, &sum);
return sc_isnonzero(&h) == 0;
}
void generate_key_image_signature(
const key_image& image, // I
const public_key& pub, // A
const secret_key& sec, // a
signature& sig) {
static_assert(sizeof(hash) == sizeof(key_image));
ec_scalar k = random_scalar(); // k = random
rs_comm rs{reinterpret_cast<const hash&>(image), 1};
ge_p3 tmp3;
ge_scalarmult_base(&tmp3, &k); // L = kG
ge_p3_tobytes(&rs.ab[0].first, &tmp3); // store L
hash_to_ec(pub, tmp3); // H(A)
ge_p2 tmp2;
ge_scalarmult(&tmp2, &k, &tmp3); // R = kH(A)
ge_tobytes(&rs.ab[0].second, &tmp2); // store R
sig.c = rs.hash_to_scalar(); // c = H(I || L || R) = H(I || kG || kH(A))
sc_mulsub(&sig.r, &sig.c, &unwrap(sec), &k); // r = k - ac = k - aH(I || kG || kH(A))
memwipe(&k, sizeof(k));
}
bool check_key_image_signature(
const key_image& image,
const public_key& pub,
const signature& sig) {
assert(check_key(pub));
ge_p3 image_unp;
if (ge_frombytes_vartime(&image_unp, &image) != 0 || sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0)
return false;
ge_dsmp image_pre;
ge_dsm_precomp(image_pre, &image_unp);
rs_comm rs{reinterpret_cast<const hash&>(image), 1};
ge_p3 tmp3;
if (ge_frombytes_vartime(&tmp3, &pub) != 0)
return false;
ge_p2 tmp2;
// Step one: reconstruct the signer's L = kG.
// The signature r was constructed as r = k - ac, so:
// k = ac + r
// kG = cA + rG = L
ge_double_scalarmult_base_vartime(&tmp2, &sig.c, &tmp3, &sig.r); // L = cA + rG
ge_tobytes(&rs.ab[0].first, &tmp2); // store L
// Step two: reconstruct the signer's R = kH(A)
// The signature r was constructed as r = k - ac, so:
// rH(A) = kH(A) - acH(A)
// and since aH(A) == I (the key image, by definition):
// kH(A) = rH(A) + cI = R
hash_to_ec(pub, tmp3); // H(A)
ge_double_scalarmult_precomp_vartime(&tmp2, &sig.r, &tmp3, &sig.c, image_pre); // R = rH(A) + cI
ge_tobytes(&rs.ab[0].second, &tmp2); // store R
// Now we can calculate our own H(I || L || R), and compare it to the signature's c (which was
// set to the signer's H(I || L || R) calculation).
ec_scalar h = rs.hash_to_scalar();
sc_sub(&h, &h, &sig.c);
return sc_isnonzero(&h) == 0;
}
}

View File

@ -40,8 +40,7 @@
#include "epee/memwipe.h"
#include "epee/mlocker.h"
#include "generic-ops.h"
#include "epee/hex.h"
#include "epee/span.h"
#include "common/hex.h"
#include "hash.h"
namespace crypto {
@ -126,7 +125,9 @@ namespace crypto {
using x25519_secret_key = epee::mlocked<tools::scrubbed<x25519_secret_key_>>;
void hash_to_scalar(const void *data, size_t length, ec_scalar &res);
void random32_unbiased(unsigned char *bytes);
void random_scalar(unsigned char* bytes);
void random_scalar(ec_scalar& res);
ec_scalar random_scalar();
static_assert(sizeof(ec_point) == 32 && sizeof(ec_scalar) == 32 &&
sizeof(public_key) == 32 && sizeof(secret_key) == 32 &&
@ -201,9 +202,43 @@ namespace crypto {
void derive_secret_key(const key_derivation &derivation, std::size_t output_index, const secret_key &base, secret_key &derived_key);
bool derive_subaddress_public_key(const public_key &out_key, const key_derivation &derivation, std::size_t output_index, public_key &result);
/* Generation and checking of a standard signature.
/* Generation and checking of a non-standard Monero curve 25519 signature. This is a custom
* scheme that is not Ed25519 because it uses a random "r" (unlike Ed25519's use of a
* deterministic value), it requires pre-hashing the message (Ed25519 does not), and produces
* signatures that cannot be verified using Ed25519 verification methods (because the order of
* hashed values differs).
*
* Given M = H(msg)
* r = random scalar
* R = rG
* c = H(M || A || R)
*
* and then the signature for this is:
*
* s = r - ac
* Signature is: (c, s) (but in the struct these are named "c" and "r")
*
* Contrast this with standard Ed25519 signature:
*
* Given M = msg
* r = H(seed_hash_2nd_half || M)
* R = rG
* c = H(R || A || M)
* s = r + ac
* Signature is: (R, s)
*
* For verification:
*
* Monero: given signature (c, s), message hash M, and pubkey A:
*
* R = sG + cA
* Check: H(M||A||R) == c
*
* Ed25519: given signature (R, s), (unhashed) message M, pubkey A:
* Check: sB == R + H(R||A||M)A
*/
void generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig);
// See above.
bool check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig);
/* Generation and checking of a tx proof; given a tx pubkey R, the recipient's view pubkey A, and the key
@ -220,48 +255,53 @@ namespace crypto {
* To check the signature, it is necessary to collect all the keys that were used to generate it. To detect double spends, it is necessary to check that each key image is used at most once.
*/
void generate_key_image(const public_key &pub, const secret_key &sec, key_image &image);
void generate_ring_signature(const hash &prefix_hash, const key_image &image,
const public_key *const *pubs, std::size_t pubs_count,
const secret_key &sec, std::size_t sec_index,
signature *sig);
bool check_ring_signature(const hash &prefix_hash, const key_image &image,
const public_key *const *pubs, std::size_t pubs_count,
const signature *sig);
void generate_ring_signature(
const hash& prefix_hash,
const key_image& image,
const std::vector<const public_key*>& pubs,
const secret_key& sec,
std::size_t sec_index,
signature* sig);
bool check_ring_signature(
const hash& prefix_hash,
const key_image& image,
const std::vector<const public_key*>& pubs,
const signature* sig);
/* Variants with vector<const public_key *> parameters.
*/
inline void generate_ring_signature(const hash &prefix_hash, const key_image &image,
const std::vector<const public_key *> &pubs,
const secret_key &sec, std::size_t sec_index,
signature *sig) {
generate_ring_signature(prefix_hash, image, pubs.data(), pubs.size(), sec, sec_index, sig);
}
inline bool check_ring_signature(const hash &prefix_hash, const key_image &image,
const std::vector<const public_key *> &pubs,
const signature *sig) {
return check_ring_signature(prefix_hash, image, pubs.data(), pubs.size(), sig);
}
// Signature on a single key image. The does the same thing as generate_ring_signature with 1
// pubkey (and secret index of 0), but slightly more efficiently, and with hardware device
// implementation. (This is still used for key image export and for exposing key images in stake
// transactions).
void generate_key_image_signature(
const key_image& image,
const public_key& pub,
const secret_key& sec,
signature& sig);
bool check_key_image_signature(
const key_image& image,
const public_key& pub,
const signature& sig);
inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) {
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
return o << '<' << tools::type_to_hex(v) << '>';
}
inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) {
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
return o << '<' << tools::type_to_hex(v) << '>';
}
inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) {
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
return o << '<' << tools::type_to_hex(v) << '>';
}
inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) {
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
return o << '<' << tools::type_to_hex(v) << '>';
}
inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) {
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
return o << '<' << tools::type_to_hex(v) << '>';
}
inline std::ostream &operator <<(std::ostream &o, const crypto::ed25519_public_key &v) {
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
return o << '<' << tools::type_to_hex(v) << '>';
}
inline std::ostream &operator <<(std::ostream &o, const crypto::x25519_public_key &v) {
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
return o << '<' << tools::type_to_hex(v) << '>';
}
constexpr inline crypto::public_key null_pkey{};
const inline crypto::secret_key null_skey{};

View File

@ -30,12 +30,11 @@
#pragma once
#include <stddef.h>
#include <iostream>
#include <cstddef>
#include <ostream>
#include "generic-ops.h"
#include "epee/hex.h"
#include "epee/span.h"
#include "common/hex.h"
#include "crypto/cn_heavy_hash.hpp"
namespace crypto {
@ -158,10 +157,10 @@ namespace crypto {
}
inline std::ostream &operator <<(std::ostream &o, const crypto::hash &v) {
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
return o << '<' << tools::type_to_hex(v) << '>';
}
inline std::ostream &operator <<(std::ostream &o, const crypto::hash8 &v) {
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
return o << '<' << tools::type_to_hex(v) << '>';
}
constexpr inline crypto::hash null_hash = {};

View File

@ -29,6 +29,13 @@ typedef uint64_t uint64;
#endif
// Some versions of GCC produce warnings in here, which seems completely justifiable given how dirty
// this code is. Switch the triggering warnings off.
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
typedef struct {
int hashbitlen; /*the message digest size*/
unsigned long long databitlen; /*the message size in bits*/

View File

@ -29,11 +29,13 @@
add_library(cryptonote_basic
account.cpp
cryptonote_basic.cpp
cryptonote_basic_impl.cpp
cryptonote_format_utils.cpp
difficulty.cpp
hardfork.cpp
miner.cpp)
miner.cpp
tx_extra.cpp)
target_link_libraries(cryptonote_basic
PRIVATE

View File

@ -0,0 +1,127 @@
#include "cryptonote_basic.h"
namespace cryptonote {
void transaction_prefix::set_null() {
version = txversion::v1;
unlock_time = 0;
vin.clear();
vout.clear();
extra.clear();
output_unlock_times.clear();
type = txtype::standard;
}
transaction::transaction(const transaction &t) :
transaction_prefix(t),
hash_valid(false),
blob_size_valid(false),
signatures(t.signatures),
rct_signatures(t.rct_signatures),
pruned(t.pruned),
unprunable_size(t.unprunable_size.load()),
prefix_size(t.prefix_size.load())
{
if (t.is_hash_valid()) {
hash = t.hash;
set_hash_valid(true);
}
if (t.is_blob_size_valid()) {
blob_size = t.blob_size;
set_blob_size_valid(true);
}
}
transaction& transaction::operator=(const transaction& t) {
transaction_prefix::operator=(t);
set_hash_valid(false);
set_blob_size_valid(false);
signatures = t.signatures;
rct_signatures = t.rct_signatures;
if (t.is_hash_valid()) {
hash = t.hash;
set_hash_valid(true);
}
if (t.is_blob_size_valid()) {
blob_size = t.blob_size;
set_blob_size_valid(true);
}
pruned = t.pruned;
unprunable_size = t.unprunable_size.load();
prefix_size = t.prefix_size.load();
return *this;
}
void transaction::set_null()
{
transaction_prefix::set_null();
signatures.clear();
rct_signatures = {};
rct_signatures.type = rct::RCTType::Null;
set_hash_valid(false);
set_blob_size_valid(false);
pruned = false;
unprunable_size = 0;
prefix_size = 0;
}
void transaction::invalidate_hashes()
{
set_hash_valid(false);
set_blob_size_valid(false);
}
size_t transaction::get_signature_size(const txin_v& tx_in)
{
if (std::holds_alternative<txin_to_key>(tx_in))
return var::get<txin_to_key>(tx_in).key_offsets.size();
return 0;
}
block::block(const block& b) :
block_header(b),
miner_tx{b.miner_tx},
tx_hashes{b.tx_hashes},
signatures{b.signatures}
{
copy_hash(b);
}
block::block(block&& b) :
block_header(std::move(b)),
miner_tx{std::move(b.miner_tx)},
tx_hashes{std::move(b.tx_hashes)},
signatures{std::move(b.signatures)}
{
copy_hash(b);
}
block& block::operator=(const block& b)
{
block_header::operator=(b);
miner_tx = b.miner_tx;
tx_hashes = b.tx_hashes;
signatures = b.signatures;
copy_hash(b);
return *this;
}
block& block::operator=(block&& b)
{
block_header::operator=(std::move(b));
miner_tx = std::move(b.miner_tx);
tx_hashes = std::move(b.tx_hashes);
signatures = std::move(b.signatures);
copy_hash(b);
return *this;
}
bool block::is_hash_valid() const
{
return hash_valid.load(std::memory_order_acquire);
}
void block::set_hash_valid(bool v) const
{
hash_valid.store(v,std::memory_order_release);
}
}

View File

@ -44,22 +44,21 @@
#include "crypto/hash.h"
#include "ringct/rctTypes.h"
#include "device/device.hpp"
#include "txtypes.h"
namespace service_nodes
{
struct quorum_signature
{
uint16_t voter_index;
char padding[6];
char padding[6] = {0};
crypto::signature signature;
quorum_signature() = default;
quorum_signature(uint16_t voter_index, crypto::signature const &signature)
: voter_index(voter_index)
, signature(signature)
{
std::memset(padding, 0, sizeof(padding));
}
quorum_signature(uint16_t voter_index, crypto::signature const &signature) :
voter_index(voter_index),
signature(signature)
{}
BEGIN_SERIALIZE_OBJECT()
FIELD(voter_index)
@ -89,7 +88,7 @@ namespace cryptonote
struct txout_to_key
{
txout_to_key() { }
txout_to_key() = default;
txout_to_key(const crypto::public_key &_key) : key(_key) { }
crypto::public_key key;
};
@ -166,23 +165,6 @@ namespace cryptonote
};
enum class txversion : uint16_t {
v0 = 0,
v1,
v2_ringct,
v3_per_output_unlock_times,
v4_tx_types,
_count,
};
enum class txtype : uint16_t {
standard,
state_change,
key_image_unlock,
stake,
loki_name_system,
_count
};
// Blink quorum statuses. Note that the underlying numeric values is used in the RPC. `none` is
// only used in places like the RPC where we return a value even if not a blink at all.
enum class blink_result { none = 0, rejected, accepted, timeout };
@ -191,12 +173,12 @@ namespace cryptonote
{
public:
static char const *version_to_string(txversion v);
static char const *type_to_string(txtype type);
static char const* version_to_string(txversion v);
static char const* type_to_string(txtype type);
static txversion get_min_version_for_hf(uint8_t hf_version);
static txversion get_max_version_for_hf(uint8_t hf_version);
static txtype get_max_type_for_hf (uint8_t hf_version);
static constexpr txversion get_min_version_for_hf(uint8_t hf_version);
static txversion get_max_version_for_hf(uint8_t hf_version);
static constexpr txtype get_max_type_for_hf (uint8_t hf_version);
// tx information
txversion version;
@ -232,20 +214,12 @@ namespace cryptonote
ENUM_FIELD_N("type", type, type < txtype::_count);
END_SERIALIZE()
transaction_prefix(){ set_null(); }
void set_null()
{
version = txversion::v1;
unlock_time = 0;
vin.clear();
vout.clear();
extra.clear();
output_unlock_times.clear();
type = txtype::standard;
}
transaction_prefix() { set_null(); }
void set_null();
uint64_t get_unlock_time(size_t out_index) const
{
// This function is inlined because device_ledger code needs to call it, but doesn't link
// against cryptonote_basic.
uint64_t get_unlock_time(size_t out_index) const {
if (version >= txversion::v3_per_output_unlock_times)
{
if (out_index >= output_unlock_times.size())
@ -257,9 +231,10 @@ namespace cryptonote
}
return unlock_time;
}
};
class transaction: public transaction_prefix
class transaction final : public transaction_prefix
{
private:
// hash cache
@ -267,7 +242,7 @@ namespace cryptonote
mutable std::atomic<bool> blob_size_valid;
public:
std::vector<std::vector<crypto::signature> > signatures; //count signatures always the same as inputs count
std::vector<std::vector<crypto::signature>> signatures; //count signatures always the same as inputs count
rct::rctSig rct_signatures;
// hash cache
@ -279,10 +254,9 @@ namespace cryptonote
std::atomic<unsigned int> unprunable_size;
std::atomic<unsigned int> prefix_size;
transaction();
transaction(const transaction &t): transaction_prefix(t), hash_valid(false), blob_size_valid(false), signatures(t.signatures), rct_signatures(t.rct_signatures), pruned(t.pruned), unprunable_size(t.unprunable_size.load()), prefix_size(t.prefix_size.load()) { if (t.is_hash_valid()) { hash = t.hash; set_hash_valid(true); } if (t.is_blob_size_valid()) { blob_size = t.blob_size; set_blob_size_valid(true); } }
transaction &operator=(const transaction &t) { transaction_prefix::operator=(t); set_hash_valid(false); set_blob_size_valid(false); signatures = t.signatures; rct_signatures = t.rct_signatures; if (t.is_hash_valid()) { hash = t.hash; set_hash_valid(true); } if (t.is_blob_size_valid()) { blob_size = t.blob_size; set_blob_size_valid(true); } pruned = t.pruned; unprunable_size = t.unprunable_size.load(); prefix_size = t.prefix_size.load(); return *this; }
virtual ~transaction();
transaction() { set_null(); }
transaction(const transaction &t);
transaction& operator=(const transaction& t);
void set_null();
void invalidate_hashes();
bool is_hash_valid() const { return hash_valid.load(std::memory_order_acquire); }
@ -353,7 +327,7 @@ namespace cryptonote
if (Binary)
unprunable_size = ar.streampos() - start_pos;
if (!pruned && rct_signatures.type != rct::RCTTypeNull)
if (!pruned && rct_signatures.type != rct::RCTType::Null)
{
ar.tag("rctsig_prunable");
auto obj = ar.begin_object();
@ -389,46 +363,6 @@ namespace cryptonote
};
inline
transaction::transaction()
{
set_null();
}
inline
transaction::~transaction()
{
}
inline
void transaction::set_null()
{
transaction_prefix::set_null();
signatures.clear();
rct_signatures = {};
rct_signatures.type = rct::RCTTypeNull;
set_hash_valid(false);
set_blob_size_valid(false);
pruned = false;
unprunable_size = 0;
prefix_size = 0;
}
inline
void transaction::invalidate_hashes()
{
set_hash_valid(false);
set_blob_size_valid(false);
}
inline
size_t transaction::get_signature_size(const txin_v& tx_in)
{
if (std::holds_alternative<txin_to_key>(tx_in))
return var::get<txin_to_key>(tx_in).key_offsets.size();
return 0;
}
/************************************************************************/
/* */
/************************************************************************/
@ -485,13 +419,13 @@ namespace cryptonote
public:
block() = default;
block(const block &b): block_header(b), miner_tx{b.miner_tx}, tx_hashes{b.tx_hashes}, signatures{b.signatures} { copy_hash(b); }
block &operator=(const block &b) { block_header::operator=(b); miner_tx = b.miner_tx; tx_hashes = b.tx_hashes; signatures = b.signatures; copy_hash(b); return *this; }
block(block &&b) : block_header(std::move(b)), miner_tx{std::move(b.miner_tx)}, tx_hashes{std::move(b.tx_hashes)}, signatures{std::move(b.signatures)} { copy_hash(b); }
block &operator=(block &&b) { block_header::operator=(std::move(b)); miner_tx = std::move(b.miner_tx); tx_hashes = std::move(b.tx_hashes); signatures = std::move(b.signatures); copy_hash(b); return *this; }
block(const block& b);
block(block&& b);
block& operator=(const block& b);
block& operator=(block&& b);
void invalidate_hashes() { set_hash_valid(false); }
bool is_hash_valid() const { return hash_valid.load(std::memory_order_acquire); }
void set_hash_valid(bool v) const { hash_valid.store(v,std::memory_order_release); }
bool is_hash_valid() const;
void set_hash_valid(bool v) const;
transaction miner_tx;
std::vector<crypto::hash> tx_hashes;
@ -544,43 +478,55 @@ namespace cryptonote
return !(*this == rhs);
}
};
constexpr account_public_address const null_address{};
inline constexpr account_public_address null_address{};
struct keypair
{
crypto::public_key pub;
crypto::secret_key sec;
static inline keypair generate(hw::device &hwdev)
{
keypair k;
hwdev.generate_keys(k.pub, k.sec);
return k;
}
keypair() = default;
// Constructs from a copied public/secret key
keypair(const crypto::public_key& pub, const crypto::secret_key& sec) : pub{pub}, sec{sec} {}
// Default copy and move
keypair(const keypair&) = default;
keypair(keypair&&) = default;
keypair& operator=(const keypair&) = default;
keypair& operator=(keypair&&) = default;
// Constructs by generating a keypair via the given hardware device:
explicit keypair(hw::device& hwdev) { hwdev.generate_keys(pub, sec); }
};
using byte_and_output_fees = std::pair<uint64_t, uint64_t>;
//---------------------------------------------------------------
inline txversion transaction_prefix::get_min_version_for_hf(uint8_t hf_version)
constexpr txversion transaction_prefix::get_min_version_for_hf(uint8_t hf_version)
{
if (hf_version >= cryptonote::network_version_7 && hf_version <= cryptonote::network_version_10_bulletproofs)
return txversion::v2_ringct;
return txversion::v4_tx_types;
}
// Used in the test suite to disable the older max version values below so that some test suite
// tests can still use particular hard forks without needing to actually generate pre-v4 txes.
namespace hack { inline bool test_suite_permissive_txes = false; }
inline txversion transaction_prefix::get_max_version_for_hf(uint8_t hf_version)
{
if (hf_version >= cryptonote::network_version_7 && hf_version <= cryptonote::network_version_8)
return txversion::v2_ringct;
if (!hack::test_suite_permissive_txes) {
if (hf_version >= cryptonote::network_version_7 && hf_version <= cryptonote::network_version_8)
return txversion::v2_ringct;
if (hf_version >= cryptonote::network_version_9_service_nodes && hf_version <= cryptonote::network_version_10_bulletproofs)
return txversion::v3_per_output_unlock_times;
if (hf_version >= cryptonote::network_version_9_service_nodes && hf_version <= cryptonote::network_version_10_bulletproofs)
return txversion::v3_per_output_unlock_times;
}
return txversion::v4_tx_types;
}
inline txtype transaction_prefix::get_max_type_for_hf(uint8_t hf_version)
constexpr txtype transaction_prefix::get_max_type_for_hf(uint8_t hf_version)
{
txtype result = txtype::standard;
if (hf_version >= network_version_15_lns) result = txtype::loki_name_system;
@ -591,7 +537,7 @@ namespace cryptonote
return result;
}
inline char const *transaction_prefix::version_to_string(txversion v)
inline const char* transaction_prefix::version_to_string(txversion v)
{
switch(v)
{
@ -603,7 +549,7 @@ namespace cryptonote
}
}
inline char const *transaction_prefix::type_to_string(txtype type)
inline const char* transaction_prefix::type_to_string(txtype type)
{
switch(type)
{

View File

@ -199,7 +199,7 @@ namespace boost
else
{
a & (rct::rctSigBase&)x.rct_signatures;
if (x.rct_signatures.type != rct::RCTTypeNull)
if (x.rct_signatures.type != rct::RCTType::Null)
a & x.rct_signatures.p;
}
}
@ -330,13 +330,13 @@ namespace boost
inline void serialize(Archive &a, rct::rctSigBase &x, const boost::serialization::version_type ver)
{
a & x.type;
if (x.type == rct::RCTTypeNull)
if (x.type == rct::RCTType::Null)
return;
if (!tools::equals_any(x.type, rct::RCTTypeFull, rct::RCTTypeSimple, rct::RCTTypeBulletproof, rct::RCTTypeBulletproof2, rct::RCTTypeCLSAG))
if (!tools::equals_any(x.type, rct::RCTType::Full, rct::RCTType::Simple, rct::RCTType::Bulletproof, rct::RCTType::Bulletproof2, rct::RCTType::CLSAG))
throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type");
// a & x.message; message is not serialized, as it can be reconstructed from the tx data
// a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets
if (x.type == rct::RCTTypeSimple) // moved to prunable with bulletproofs
if (x.type == rct::RCTType::Simple) // moved to prunable with bulletproofs
a & x.pseudoOuts;
a & x.ecdhInfo;
serializeOutPk(a, x.outPk, ver);
@ -360,13 +360,13 @@ namespace boost
inline void serialize(Archive &a, rct::rctSig &x, const boost::serialization::version_type ver)
{
a & x.type;
if (x.type == rct::RCTTypeNull)
if (x.type == rct::RCTType::Null)
return;
if (!tools::equals_any(x.type, rct::RCTTypeFull, rct::RCTTypeSimple, rct::RCTTypeBulletproof, rct::RCTTypeBulletproof2, rct::RCTTypeCLSAG))
if (!tools::equals_any(x.type, rct::RCTType::Full, rct::RCTType::Simple, rct::RCTType::Bulletproof, rct::RCTType::Bulletproof2, rct::RCTType::CLSAG))
throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type");
// a & x.message; message is not serialized, as it can be reconstructed from the tx data
// a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets
if (x.type == rct::RCTTypeSimple)
if (x.type == rct::RCTType::Simple)
a & x.pseudoOuts;
a & x.ecdhInfo;
serializeOutPk(a, x.outPk, ver);

View File

@ -134,7 +134,7 @@ namespace cryptonote
if (tx.version >= txversion::v2_ringct && !is_coinbase(tx))
{
rct::rctSig &rv = tx.rct_signatures;
if (rv.type == rct::RCTTypeNull)
if (rv.type == rct::RCTType::Null)
return true;
if (rv.outPk.size() != tx.vout.size())
{
@ -452,7 +452,7 @@ namespace cryptonote
{
CHECK_AND_ASSERT_MES(tx.pruned, std::numeric_limits<uint64_t>::max(), "get_pruned_transaction_weight does not support non pruned txes");
CHECK_AND_ASSERT_MES(tx.version >= txversion::v2_ringct, std::numeric_limits<uint64_t>::max(), "get_pruned_transaction_weight does not support v1 txes");
CHECK_AND_ASSERT_MES(tx.rct_signatures.type >= rct::RCTTypeBulletproof2,
CHECK_AND_ASSERT_MES(tx.rct_signatures.type >= rct::RCTType::Bulletproof2,
std::numeric_limits<uint64_t>::max(), "get_pruned_transaction_weight does not support older range proof types");
CHECK_AND_ASSERT_MES(!tx.vin.empty(), std::numeric_limits<uint64_t>::max(), "empty vin");
CHECK_AND_ASSERT_MES(std::holds_alternative<cryptonote::txin_to_key>(tx.vin[0]), std::numeric_limits<uint64_t>::max(), "empty vin");
@ -473,7 +473,7 @@ namespace cryptonote
// calculate deterministic CLSAG/MLSAG data size
const size_t ring_size = var::get<cryptonote::txin_to_key>(tx.vin[0]).key_offsets.size();
if (tx.rct_signatures.type == rct::RCTTypeCLSAG)
if (tx.rct_signatures.type == rct::RCTType::CLSAG)
extra = tx.vin.size() * (ring_size + 2) * 32;
else
extra = tx.vin.size() * (ring_size * (1 + 1) * 32 + 32 /* cc */);
@ -1185,15 +1185,13 @@ namespace cryptonote
}
else
{
transaction &tt = const_cast<transaction&>(t);
serialization::binary_string_archiver ba;
const size_t inputs = t.vin.size();
const size_t outputs = t.vout.size();
size_t mixin = 0;
if (t.vin.size() > 0 && std::holds_alternative<txin_to_key>(t.vin[0]))
mixin = var::get<txin_to_key>(t.vin[0]).key_offsets.size() - 1;
try {
tt.rct_signatures.p.serialize_rctsig_prunable(ba, t.rct_signatures.type, inputs, outputs, mixin);
const_cast<transaction&>(t).rct_signatures.p.serialize_rctsig_prunable(
ba, t.rct_signatures.type, t.vin.size(), t.vout.size(), mixin);
} catch (const std::exception& e) {
LOG_ERROR("Failed to serialize rct signatures (prunable): " << e.what());
return false;
@ -1234,7 +1232,7 @@ namespace cryptonote
}
// prunable rct
if (t.rct_signatures.type == rct::RCTTypeNull)
if (t.rct_signatures.type == rct::RCTType::Null)
hashes[2] = crypto::null_hash;
else
hashes[2] = pruned_data_hash;
@ -1289,7 +1287,7 @@ namespace cryptonote
}
// prunable rct
if (t.rct_signatures.type == rct::RCTTypeNull)
if (t.rct_signatures.type == rct::RCTType::Null)
{
hashes[2] = crypto::null_hash;
}

View File

@ -0,0 +1,79 @@
#include "tx_extra.h"
namespace cryptonote {
tx_extra_loki_name_system tx_extra_loki_name_system::make_buy(
lns::generic_owner const& owner,
lns::generic_owner const* backup_owner,
lns::mapping_type type,
const crypto::hash& name_hash,
const std::string& encrypted_value,
const crypto::hash& prev_txid)
{
tx_extra_loki_name_system result{};
result.fields = lns::extra_field::buy;
result.owner = owner;
if (backup_owner)
result.backup_owner = *backup_owner;
else
result.fields = lns::extra_field::buy_no_backup;
result.type = type;
result.name_hash = name_hash;
result.encrypted_value = encrypted_value;
result.prev_txid = prev_txid;
return result;
}
tx_extra_loki_name_system tx_extra_loki_name_system::make_renew(
lns::mapping_type type, crypto::hash const &name_hash, crypto::hash const &prev_txid)
{
assert(is_lokinet_type(type) && prev_txid);
tx_extra_loki_name_system result{};
result.fields = lns::extra_field::none;
result.type = type;
result.name_hash = name_hash;
result.prev_txid = prev_txid;
return result;
}
tx_extra_loki_name_system tx_extra_loki_name_system::make_update(
const lns::generic_signature& signature,
lns::mapping_type type,
const crypto::hash& name_hash,
std::string_view encrypted_value,
const lns::generic_owner* owner,
const lns::generic_owner* backup_owner,
const crypto::hash& prev_txid)
{
tx_extra_loki_name_system result{};
result.signature = signature;
result.type = type;
result.name_hash = name_hash;
result.fields |= lns::extra_field::signature;
if (encrypted_value.size())
{
result.fields |= lns::extra_field::encrypted_value;
result.encrypted_value = std::string{encrypted_value};
}
if (owner)
{
result.fields |= lns::extra_field::owner;
result.owner = *owner;
}
if (backup_owner)
{
result.fields |= lns::extra_field::backup_owner;
result.backup_owner = *backup_owner;
}
result.prev_txid = prev_txid;
return result;
}
}

View File

@ -35,6 +35,7 @@
#include "serialization/binary_utils.h"
#include "serialization/variant.h"
#include "crypto/crypto.h"
#include "common/hex.h"
#include "loki_economy.h"
#include "cryptonote_basic.h"
@ -146,10 +147,14 @@ struct generic_signature
FIELD(ed25519);
END_SERIALIZE()
};
static_assert(sizeof(crypto::ed25519_signature) == sizeof(crypto::signature), "LNS allows storing either ed25519 or monero style signatures, we store all signatures into crypto::signature in LNS");
inline std::ostream &operator<<(std::ostream &o, const generic_signature &v) { epee::to_hex::formatted(o, epee::as_byte_span(v.data)); return o; }
inline std::ostream &operator<<(std::ostream &o, const generic_signature &v) {
return o << '<' << tools::type_to_hex(v.data) << '>';
}
} // namespace lns
namespace std {
static_assert(sizeof(lns::generic_owner) >= sizeof(std::size_t) && alignof(lns::generic_owner) >= alignof(std::size_t),
"Size and alignment of hash must be at least that of size_t");
@ -431,7 +436,20 @@ namespace cryptonote
{
crypto::key_image key_image;
crypto::signature signature;
uint32_t nonce;
uint32_t nonce; // TODO: remove this nonce value if we ever have to make other changes to this structure
// The value we sign when signing an unlock request. For backwards compatibility we send this as a
// "nonce" (although it isn't and never was a nonce), which is required to be an unsigned 32-bit
// value. We could just as easily sign with crypto::null_hash, but using a distinct value makes it
// slightly less likely that we could end up using the same message as some other signing process.
static constexpr crypto::hash HASH{
'U','N','L','K','U','N','L','K','U','N','L','K','U','N','L','K',
'U','N','L','K','U','N','L','K','U','N','L','K','U','N','L','K'};
// For now, we still have to send that (not a) "nonce" value in the unlock tx on the wire, but
// future HF versions could remove it from the wire (though at 4 bytes it isn't worth doing
// until we also need to make some other change to unlocks here). So for now, we always send
// this in `nonce`.
static constexpr uint32_t FAKE_NONCE = 0x4B4C4E55;
// Compares equal if this represents the same key image unlock (but does *not* require equality of signature/nonce)
bool operator==(const tx_extra_tx_key_image_unlock &other) const { return key_image == other.key_image; }
@ -475,71 +493,24 @@ namespace cryptonote
// and has a non-null txid set (which should point to the most recent registration or update).
bool is_renewing() const { return fields == lns::extra_field::none && prev_txid && is_lokinet_type(type); }
static tx_extra_loki_name_system make_buy(lns::generic_owner const &owner, lns::generic_owner const *backup_owner, lns::mapping_type type, crypto::hash const &name_hash, std::string const &encrypted_value, crypto::hash const &prev_txid)
{
tx_extra_loki_name_system result = {};
result.fields = lns::extra_field::buy;
result.owner = owner;
static tx_extra_loki_name_system make_buy(
lns::generic_owner const& owner,
lns::generic_owner const* backup_owner,
lns::mapping_type type,
const crypto::hash& name_hash,
const std::string& encrypted_value,
const crypto::hash& prev_txid);
if (backup_owner)
result.backup_owner = *backup_owner;
else
result.fields = lns::extra_field::buy_no_backup;
static tx_extra_loki_name_system make_renew(lns::mapping_type type, const crypto::hash& name_hash, const crypto::hash& prev_txid);
result.type = type;
result.name_hash = name_hash;
result.encrypted_value = encrypted_value;
result.prev_txid = prev_txid;
return result;
}
static tx_extra_loki_name_system make_renew(lns::mapping_type type, crypto::hash const &name_hash, crypto::hash const &prev_txid)
{
assert(is_lokinet_type(type) && prev_txid);
tx_extra_loki_name_system result{};
result.fields = lns::extra_field::none;
result.type = type;
result.name_hash = name_hash;
result.prev_txid = prev_txid;
return result;
}
static tx_extra_loki_name_system make_update(lns::generic_signature const &signature,
lns::mapping_type type,
crypto::hash const &name_hash,
std::string_view encrypted_value,
lns::generic_owner const *owner,
lns::generic_owner const *backup_owner,
crypto::hash const &prev_txid)
{
tx_extra_loki_name_system result = {};
result.signature = signature;
result.type = type;
result.name_hash = name_hash;
result.fields |= lns::extra_field::signature;
if (encrypted_value.size())
{
result.fields |= lns::extra_field::encrypted_value;
result.encrypted_value = std::string(reinterpret_cast<char const *>(encrypted_value.data()), encrypted_value.size());
}
if (owner)
{
result.fields |= lns::extra_field::owner;
result.owner = *owner;
}
if (backup_owner)
{
result.fields |= lns::extra_field::backup_owner;
result.backup_owner = *backup_owner;
}
result.prev_txid = prev_txid;
return result;
}
static tx_extra_loki_name_system make_update(
const lns::generic_signature& signature,
lns::mapping_type type,
const crypto::hash& name_hash,
std::string_view encrypted_value,
const lns::generic_owner* owner,
const lns::generic_owner* backup_owner,
const crypto::hash& prev_txid);
BEGIN_SERIALIZE()
FIELD(version)

View File

@ -0,0 +1,24 @@
#pragma once
#include <cstdint>
namespace cryptonote {
enum class txversion : uint16_t {
v0 = 0,
v1,
v2_ringct,
v3_per_output_unlock_times,
v4_tx_types,
_count,
};
enum class txtype : uint16_t {
standard,
state_change,
key_image_unlock,
stake,
loki_name_system,
_count
};
}

View File

@ -35,6 +35,7 @@
#include "common/rules.h"
#include "common/hex.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "cryptonote_core/cryptonote_tx_utils.h"
#include "ringct/rctTypes.h"
@ -1300,7 +1301,7 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height,
}
if (hf_version >= HF_VERSION_REJECT_SIGS_IN_COINBASE) // Enforce empty rct signatures for miner transactions,
CHECK_AND_ASSERT_MES(b.miner_tx.rct_signatures.type == rct::RCTTypeNull, false, "RingCT signatures not allowed in coinbase transactions");
CHECK_AND_ASSERT_MES(b.miner_tx.rct_signatures.type == rct::RCTType::Null, false, "RingCT signatures not allowed in coinbase transactions");
//check outs overflow
//NOTE: not entirely sure this is necessary, given that this function is
@ -3059,6 +3060,11 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
}
}
// Test suite hack: allow some tests to violate these restrictions (necessary when old HF rules
// are specifically required because older TX types can't be constructed anymore).
if (hack::test_suite_permissive_txes)
return true;
// from v10, allow bulletproofs
const uint8_t hf_version = m_hardfork->get_current_version();
if (hf_version < network_version_10_bulletproofs) {
@ -3095,9 +3101,9 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
}
if (hf_version < HF_VERSION_SMALLER_BP) {
if (tx.rct_signatures.type == rct::RCTTypeBulletproof2)
if (tx.rct_signatures.type == rct::RCTType::Bulletproof2)
{
MERROR_VER("Ringct type " << (unsigned)rct::RCTTypeBulletproof2 << " is not allowed before v" << HF_VERSION_SMALLER_BP);
MERROR_VER("Ringct type " << (unsigned)rct::RCTType::Bulletproof2 << " is not allowed before v" << HF_VERSION_SMALLER_BP);
tvc.m_invalid_output = true;
return false;
}
@ -3106,9 +3112,9 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
if (hf_version > HF_VERSION_SMALLER_BP) {
if (tx.version >= txversion::v4_tx_types && tx.is_transfer())
{
if (tx.rct_signatures.type == rct::RCTTypeBulletproof)
if (tx.rct_signatures.type == rct::RCTType::Bulletproof)
{
MERROR_VER("Ringct type " << (unsigned)rct::RCTTypeBulletproof << " is not allowed from v" << (HF_VERSION_SMALLER_BP + 1));
MERROR_VER("Ringct type " << (unsigned)rct::RCTType::Bulletproof << " is not allowed from v" << (HF_VERSION_SMALLER_BP + 1));
tvc.m_invalid_output = true;
return false;
}
@ -3118,9 +3124,9 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
// Disallow CLSAGs before the CLSAG hardfork
if (hf_version < HF_VERSION_CLSAG) {
if (tx.version >= txversion::v4_tx_types && tx.is_transfer()) {
if (tx.rct_signatures.type == rct::RCTTypeCLSAG)
if (tx.rct_signatures.type == rct::RCTType::CLSAG)
{
MERROR_VER("Ringct type " << (unsigned)rct::RCTTypeCLSAG << " is not allowed before v" << HF_VERSION_CLSAG);
MERROR_VER("Ringct type " << (unsigned)rct::RCTType::CLSAG << " is not allowed before v" << HF_VERSION_CLSAG);
tvc.m_invalid_output = true;
return false;
}
@ -3130,7 +3136,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
// Require CLSAGs starting 10 blocks after the CLSAG-enabling hard fork (the 10 block buffer is to
// allow staggling txes around fork time to still make it into a block).
if (hf_version >= HF_VERSION_CLSAG
&& tx.rct_signatures.type < rct::RCTTypeCLSAG
&& tx.rct_signatures.type < rct::RCTType::CLSAG
&& tx.version >= txversion::v4_tx_types && tx.is_transfer()
&& (hf_version > HF_VERSION_CLSAG || get_current_blockchain_height() >= 10 + m_hardfork->get_earliest_ideal_height_for_version(HF_VERSION_CLSAG)))
{
@ -3164,7 +3170,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
rv.message = rct::hash2rct(tx_prefix_hash);
// mixRing - full and simple store it in opposite ways
if (rv.type == rct::RCTTypeFull)
if (rv.type == rct::RCTType::Full)
{
CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys");
rv.mixRing.resize(pubkeys[0].size());
@ -3179,7 +3185,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
}
}
}
else if (tools::equals_any(rv.type, rct::RCTTypeSimple, rct::RCTTypeBulletproof, rct::RCTTypeBulletproof2, rct::RCTTypeCLSAG))
else if (tools::equals_any(rv.type, rct::RCTType::Simple, rct::RCTType::Bulletproof, rct::RCTType::Bulletproof2, rct::RCTType::CLSAG))
{
CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys");
rv.mixRing.resize(pubkeys.size());
@ -3192,28 +3198,20 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
}
}
}
else if (rv.type == rct::RCTTypeCLSAG)
{
CHECK_AND_ASSERT_MES(rv.p.CLSAGs.size() == tx.vin.size(), false, "Bad CLSAGs size");
for (size_t n = 0; n < tx.vin.size(); ++n)
{
rv.p.CLSAGs[n].I = rct::ki2rct(var::get<txin_to_key>(tx.vin[n]).k_image);
}
}
else
{
CHECK_AND_ASSERT_MES(false, false, "Unsupported rct tx type: " + std::to_string(rv.type));
CHECK_AND_ASSERT_MES(false, false, "Unsupported rct tx type: " + std::to_string(std::underlying_type_t<rct::RCTType>(rv.type)));
}
// II
if (rv.type == rct::RCTTypeFull)
if (rv.type == rct::RCTType::Full)
{
rv.p.MGs.resize(1);
rv.p.MGs[0].II.resize(tx.vin.size());
for (size_t n = 0; n < tx.vin.size(); ++n)
rv.p.MGs[0].II[n] = rct::ki2rct(var::get<txin_to_key>(tx.vin[n]).k_image);
}
else if (tools::equals_any(rv.type, rct::RCTTypeSimple, rct::RCTTypeBulletproof, rct::RCTTypeBulletproof2))
else if (tools::equals_any(rv.type, rct::RCTType::Simple, rct::RCTType::Bulletproof, rct::RCTType::Bulletproof2))
{
CHECK_AND_ASSERT_MES(rv.p.MGs.size() == tx.vin.size(), false, "Bad MGs size");
for (size_t n = 0; n < tx.vin.size(); ++n)
@ -3222,7 +3220,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
rv.p.MGs[n].II[0] = rct::ki2rct(var::get<txin_to_key>(tx.vin[n]).k_image);
}
}
else if (rv.type == rct::RCTTypeCLSAG)
else if (rv.type == rct::RCTType::CLSAG)
{
if (!tx.pruned)
{
@ -3235,7 +3233,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
}
else
{
CHECK_AND_ASSERT_MES(false, false, "Unsupported rct tx type: " + std::to_string(rv.type));
CHECK_AND_ASSERT_MES(false, false, "Unsupported rct tx type: " + std::to_string(static_cast<std::underlying_type_t<rct::RCTType>>(rv.type)));
}
// outPk was already done by handle_incoming_tx
@ -3391,15 +3389,15 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
const rct::rctSig &rv = tx.rct_signatures;
switch (rv.type)
{
case rct::RCTTypeNull: {
case rct::RCTType::Null: {
// we only accept no signatures for coinbase txes
MERROR_VER("Null rct signature on non-coinbase tx");
return false;
}
case rct::RCTTypeSimple:
case rct::RCTTypeBulletproof:
case rct::RCTTypeBulletproof2:
case rct::RCTTypeCLSAG:
case rct::RCTType::Simple:
case rct::RCTType::Bulletproof:
case rct::RCTType::Bulletproof2:
case rct::RCTType::CLSAG:
{
// check all this, either reconstructed (so should really pass), or not
{
@ -3435,7 +3433,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
}
}
const size_t n_sigs = rv.type == rct::RCTTypeCLSAG ? rv.p.CLSAGs.size() : rv.p.MGs.size();
const size_t n_sigs = rv.type == rct::RCTType::CLSAG ? rv.p.CLSAGs.size() : rv.p.MGs.size();
if (n_sigs != tx.vin.size())
{
MERROR_VER("Failed to check ringct signatures: mismatched MGs/vin sizes");
@ -3444,7 +3442,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
for (size_t n = 0; n < tx.vin.size(); ++n)
{
bool error;
if (rv.type == rct::RCTTypeCLSAG)
if (rv.type == rct::RCTType::CLSAG)
error = memcmp(&var::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.CLSAGs[n].I, 32);
else
error = rv.p.MGs[n].II.empty() || memcmp(&var::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.MGs[n].II[0], 32);
@ -3462,7 +3460,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
}
break;
}
case rct::RCTTypeFull:
case rct::RCTType::Full:
{
// check all this, either reconstructed (so should really pass), or not
{
@ -3522,7 +3520,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
break;
}
default:
MERROR_VER(__func__ << ": Unsupported rct type: " << rv.type);
MERROR_VER(__func__ << ": Unsupported rct type: " << (int)rv.type);
return false;
}
@ -3531,7 +3529,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
{
for (const rct::Bulletproof &proof: rv.p.bulletproofs)
{
if (proof.V.size() > 1)
if (proof.V.size() > 1 && !hack::test_suite_permissive_txes)
{
MERROR_VER("Multi output bulletproofs are invalid before v10");
return false;
@ -3602,10 +3600,10 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
return hf_version < cryptonote::network_version_12_checkpointing; // NOTE: Used to be allowed pre HF12.
}
service_nodes::service_node_info const &service_node_info = *service_node_array[0].info;
const auto& service_node_info = *service_node_array[0].info;
if (!service_node_info.can_transition_to_state(hf_version, state_change.block_height, state_change.state))
{
MERROR_VER("State change trying to vote Service Node into the same state it already is in, (aka double spend)");
MERROR_VER("State change trying to vote Service Node into the same state it invalid (expired, already applied, or impossible)");
tvc.m_double_spend = true;
return false;
}
@ -3628,8 +3626,8 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
return false;
}
crypto::hash const hash = service_nodes::generate_request_stake_unlock_hash(unlock.nonce);
if (!crypto::check_signature(hash, contribution.key_image_pub_key, unlock.signature))
if (!crypto::check_signature(service_nodes::generate_request_stake_unlock_hash(unlock.nonce),
contribution.key_image_pub_key, unlock.signature))
{
MERROR("Could not verify key image unlock transaction signature for tx: " << get_transaction_hash(tx));
return false;

View File

@ -1208,14 +1208,14 @@ namespace cryptonote
continue;
const rct::rctSig &rv = tx_info[n].tx.rct_signatures;
switch (rv.type) {
case rct::RCTTypeNull:
case rct::RCTType::Null:
// coinbase should not come here, so we reject for all other types
MERROR_VER("Unexpected Null rctSig type");
set_semantics_failed(tx_info[n].tx_hash);
tx_info[n].tvc.m_verifivation_failed = true;
tx_info[n].result = false;
break;
case rct::RCTTypeSimple:
case rct::RCTType::Simple:
if (!rct::verRctSemanticsSimple(rv))
{
MERROR_VER("rct signature semantics check failed");
@ -1225,7 +1225,7 @@ namespace cryptonote
break;
}
break;
case rct::RCTTypeFull:
case rct::RCTType::Full:
if (!rct::verRct(rv, true))
{
MERROR_VER("rct signature semantics check failed");
@ -1235,9 +1235,9 @@ namespace cryptonote
break;
}
break;
case rct::RCTTypeBulletproof:
case rct::RCTTypeBulletproof2:
case rct::RCTTypeCLSAG:
case rct::RCTType::Bulletproof:
case rct::RCTType::Bulletproof2:
case rct::RCTType::CLSAG:
if (!is_canonical_bulletproof_layout(rv.p.bulletproofs))
{
MERROR_VER("Bulletproof does not have canonical form");
@ -1249,7 +1249,7 @@ namespace cryptonote
rvv.push_back(&rv); // delayed batch verification
break;
default:
MERROR_VER("Unknown rct type: " << rv.type);
MERROR_VER("Unknown rct type: " << (int)rv.type);
set_semantics_failed(tx_info[n].tx_hash);
tx_info[n].tvc.m_verifivation_failed = true;
tx_info[n].result = false;

View File

@ -273,7 +273,7 @@ namespace cryptonote
tx.type = txtype::standard;
tx.version = transaction::get_max_version_for_hf(hard_fork_version);
keypair const txkey = keypair::generate(hw::get_device("default"));
keypair const txkey{hw::get_device("default")};
keypair const gov_key = get_deterministic_keypair_from_height(height); // NOTE: Always need since we use same key for service node
// NOTE: TX Extra
@ -621,9 +621,12 @@ namespace cryptonote
}
tx.version = transaction::get_max_version_for_hf(tx_params.hf_version);
tx.type = tx_params.tx_type;
if (tx.version <= txversion::v2_ringct)
tx.unlock_time = unlock_time;
CHECK_AND_ASSERT_MES(tx.version >= txversion::v4_tx_types, false, "Cannot construct pre-v4 transactions");
CHECK_AND_ASSERT_MES(rct_config.range_proof_type == rct::RangeProofType::PaddedBulletproof &&
(rct_config.bp_version == 0 || rct_config.bp_version >= 3),
false, "Cannot construct pre-CLSAG transactions");
tx.type = tx_params.tx_type;
if (tx_params.burn_percent)
{
@ -631,17 +634,16 @@ namespace cryptonote
return false;
}
if (tx_params.burn_fixed && tx_params.hf_version < cryptonote::network_version_14_blink)
{
LOG_ERROR("cannot construct tx: burn can not be specified before hard fork 14");
return false;
}
tx.extra = extra;
crypto::public_key txkey_pub;
if (tx.type == txtype::stake)
add_tx_secret_key_to_tx_extra(tx.extra, tx_key);
if (tx.type == txtype::stake) {
crypto::secret_key tx_sk{tx_key};
bool added = hwdev.update_staking_tx_secret_key(tx_sk);
CHECK_AND_NO_ASSERT_MES(added, false, "Failed to add tx secret key to stake transaction");
cryptonote::add_tx_secret_key_to_tx_extra(tx.extra, tx_sk);
}
// if we have a stealth payment id, find it and encrypt it with the tx key now
std::vector<tx_extra_field> tx_extra_fields;
@ -829,7 +831,6 @@ namespace cryptonote
bool found_change_already = false;
for(const tx_destination_entry& dst_entr: destinations)
{
CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version >= txversion::v2_ringct, false, "Destination with wrong amount: " << dst_entr.amount);
crypto::public_key out_eph_public_key;
bool this_dst_is_change_addr = false;
@ -838,7 +839,7 @@ namespace cryptonote
need_additional_txkeys, additional_tx_keys,
additional_tx_public_keys, amount_keys, out_eph_public_key);
if (tx.version >= txversion::v3_per_output_unlock_times)
// Per-output unlock times:
{
if (change_addr && *change_addr == dst_entr && this_dst_is_change_addr && !found_change_already)
{
@ -859,18 +860,15 @@ namespace cryptonote
if (!(change_addr && *change_addr == dst_entr))
{
tx_extra_tx_key_image_proofs::proof proof = {};
keypair ephemeral_keys = {};
const subaddress_index zeroth_address = {};
auto& proof = key_image_proofs.proofs.emplace_back();
keypair ephemeral_keys{};
if(!generate_key_image_helper(sender_account_keys, subaddresses, out_eph_public_key, txkey_pub, additional_tx_public_keys, output_index, ephemeral_keys, proof.key_image, hwdev))
{
LOG_ERROR("Key image generation failed for staking TX!");
return false;
}
crypto::public_key const *out_eph_public_key_ptr = &out_eph_public_key;
crypto::generate_ring_signature((const crypto::hash&)proof.key_image, proof.key_image, &out_eph_public_key_ptr, 1, ephemeral_keys.sec, 0, &proof.signature);
key_image_proofs.proofs.push_back(proof);
hwdev.generate_key_image_signature(proof.key_image, out_eph_public_key, ephemeral_keys.sec, proof.signature);
}
}
@ -924,170 +922,83 @@ namespace cryptonote
MDEBUG("Null secret key, skipping signatures");
}
if (tx.version == txversion::v1)
uint64_t amount_in = 0, amount_out = 0;
rct::ctkeyV inSk;
inSk.reserve(sources.size());
// mixRing indexing is done the other way round for simple
rct::ctkeyM mixRing(sources.size());
rct::keyV dest_keys;
std::vector<uint64_t> inamounts, outamounts;
std::vector<unsigned int> index;
std::vector<rct::multisig_kLRki> kLRki;
for (size_t i = 0; i < sources.size(); ++i)
{
//generate ring signatures
crypto::hash tx_prefix_hash;
get_transaction_prefix_hash(tx, tx_prefix_hash);
std::stringstream ss_ring_s;
size_t i = 0;
for(const tx_source_entry& src_entr: sources)
rct::ctkey ctkey;
amount_in += sources[i].amount;
inamounts.push_back(sources[i].amount);
index.push_back(sources[i].real_output);
// inSk: (secret key, mask)
ctkey.dest = rct::sk2rct(in_contexts[i].in_ephemeral.sec);
ctkey.mask = sources[i].mask;
inSk.push_back(ctkey);
memwipe(&ctkey, sizeof(rct::ctkey));
// inPk: (public key, commitment)
// will be done when filling in mixRing
if (msout)
{
ss_ring_s << "pub_keys:\n";
std::vector<const crypto::public_key*> keys_ptrs;
std::vector<crypto::public_key> keys(src_entr.outputs.size());
size_t ii = 0;
for(const tx_source_entry::output_entry& o: src_entr.outputs)
{
keys[ii] = rct2pk(o.second.dest);
keys_ptrs.push_back(&keys[ii]);
ss_ring_s << o.second.dest << "\n";
++ii;
}
tx.signatures.push_back(std::vector<crypto::signature>());
std::vector<crypto::signature>& sigs = tx.signatures.back();
sigs.resize(src_entr.outputs.size());
if (!zero_secret_key)
crypto::generate_ring_signature(tx_prefix_hash, var::get<txin_to_key>(tx.vin[i]).k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data());
ss_ring_s << "signatures:\n";
std::for_each(sigs.begin(), sigs.end(), [&](const crypto::signature& s){ss_ring_s << s << "\n";});
ss_ring_s << "prefix_hash:" << tx_prefix_hash << "\nin_ephemeral_key: " << in_contexts[i].in_ephemeral.sec << "\nreal_output: " << src_entr.real_output << "\n";
i++;
kLRki.push_back(sources[i].multisig_kLRki);
}
MCINFO("construct_tx", "transaction_created: " << get_transaction_hash(tx) << "\n" << obj_to_json_str(tx) << "\n" << ss_ring_s.str());
}
else
for (size_t i = 0; i < tx.vout.size(); ++i)
{
size_t n_total_outs = sources[0].outputs.size(); // only for non-simple rct
// the non-simple version is slightly smaller, but assumes all real inputs
// are on the same index, so can only be used if there just one ring.
bool use_simple_rct = sources.size() > 1 || rct_config.range_proof_type != rct::RangeProofBorromean;
if (!use_simple_rct)
{
// non simple ringct requires all real inputs to be at the same index for all inputs
for(const tx_source_entry& src_entr: sources)
{
if(src_entr.real_output != sources.begin()->real_output)
{
LOG_ERROR("All inputs must have the same index for non-simple ringct");
return false;
}
}
// enforce same mixin for all outputs
for (size_t i = 1; i < sources.size(); ++i) {
if (n_total_outs != sources[i].outputs.size()) {
LOG_ERROR("Non-simple ringct transaction has varying ring size");
return false;
}
}
}
uint64_t amount_in = 0, amount_out = 0;
rct::ctkeyV inSk;
inSk.reserve(sources.size());
// mixRing indexing is done the other way round for simple
rct::ctkeyM mixRing(use_simple_rct ? sources.size() : n_total_outs);
rct::keyV destinations;
std::vector<uint64_t> inamounts, outamounts;
std::vector<unsigned int> index;
std::vector<rct::multisig_kLRki> kLRki;
for (size_t i = 0; i < sources.size(); ++i)
{
rct::ctkey ctkey;
amount_in += sources[i].amount;
inamounts.push_back(sources[i].amount);
index.push_back(sources[i].real_output);
// inSk: (secret key, mask)
ctkey.dest = rct::sk2rct(in_contexts[i].in_ephemeral.sec);
ctkey.mask = sources[i].mask;
inSk.push_back(ctkey);
memwipe(&ctkey, sizeof(rct::ctkey));
// inPk: (public key, commitment)
// will be done when filling in mixRing
if (msout)
{
kLRki.push_back(sources[i].multisig_kLRki);
}
}
for (size_t i = 0; i < tx.vout.size(); ++i)
{
destinations.push_back(rct::pk2rct(var::get<txout_to_key>(tx.vout[i].target).key));
outamounts.push_back(tx.vout[i].amount);
amount_out += tx.vout[i].amount;
}
if (use_simple_rct)
{
// mixRing indexing is done the other way round for simple
for (size_t i = 0; i < sources.size(); ++i)
{
mixRing[i].resize(sources[i].outputs.size());
for (size_t n = 0; n < sources[i].outputs.size(); ++n)
{
mixRing[i][n] = sources[i].outputs[n].second;
}
}
}
else
{
for (size_t i = 0; i < n_total_outs; ++i) // same index assumption
{
mixRing[i].resize(sources.size());
for (size_t n = 0; n < sources.size(); ++n)
{
mixRing[i][n] = sources[n].outputs[i].second;
}
}
}
// fee
if (!use_simple_rct && amount_in > amount_out)
outamounts.push_back(amount_in - amount_out);
if (tx_params.burn_fixed)
{
if (amount_in < amount_out + tx_params.burn_fixed)
{
LOG_ERROR("invalid burn amount: tx does not have enough unspent funds available; amount_in: " << std::to_string(amount_in) << "; amount_out + tx_params.burn_fixed: " << std::to_string(amount_out) << " + " << std::to_string(tx_params.burn_fixed));
return false;
}
remove_field_from_tx_extra<tx_extra_burn>(tx.extra); // doesn't have to be present (but the wallet puts a dummy here as a safety to avoid growing the tx)
if (!add_burned_amount_to_tx_extra(tx.extra, tx_params.burn_fixed))
{
LOG_ERROR("failed to add burn amount to tx extra");
return false;
}
}
// zero out all amounts to mask rct outputs, real amounts are now encrypted
for (size_t i = 0; i < tx.vin.size(); ++i)
{
if (sources[i].rct)
var::get<txin_to_key>(tx.vin[i]).amount = 0;
}
for (size_t i = 0; i < tx.vout.size(); ++i)
tx.vout[i].amount = 0;
crypto::hash tx_prefix_hash;
get_transaction_prefix_hash(tx, tx_prefix_hash, hwdev);
rct::ctkeyV outSk;
if (use_simple_rct)
tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, msout ? &kLRki : NULL, msout, index, outSk, rct_config, hwdev);
else
tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, msout ? &kLRki[0] : NULL, msout, sources[0].real_output, outSk, rct_config, hwdev); // same index assumption
memwipe(inSk.data(), inSk.size() * sizeof(rct::ctkey));
CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout");
MCINFO("construct_tx", "transaction_created: " << get_transaction_hash(tx) << "\n" << obj_to_json_str(tx) << "\n");
dest_keys.push_back(rct::pk2rct(var::get<txout_to_key>(tx.vout[i].target).key));
outamounts.push_back(tx.vout[i].amount);
amount_out += tx.vout[i].amount;
}
for (size_t i = 0; i < sources.size(); ++i)
{
mixRing[i].resize(sources[i].outputs.size());
for (size_t n = 0; n < sources[i].outputs.size(); ++n)
{
mixRing[i][n] = sources[i].outputs[n].second;
}
}
if (tx_params.burn_fixed)
{
if (amount_in < amount_out + tx_params.burn_fixed)
{
LOG_ERROR("invalid burn amount: tx does not have enough unspent funds available; amount_in: " << std::to_string(amount_in) << "; amount_out + tx_params.burn_fixed: " << std::to_string(amount_out) << " + " << std::to_string(tx_params.burn_fixed));
return false;
}
remove_field_from_tx_extra<tx_extra_burn>(tx.extra); // doesn't have to be present (but the wallet puts a dummy here as a safety to avoid growing the tx)
if (!add_burned_amount_to_tx_extra(tx.extra, tx_params.burn_fixed))
{
LOG_ERROR("failed to add burn amount to tx extra");
return false;
}
}
// zero out all amounts to mask rct outputs, real amounts are now encrypted
for (size_t i = 0; i < tx.vin.size(); ++i)
{
if (sources[i].rct)
var::get<txin_to_key>(tx.vin[i]).amount = 0;
}
for (size_t i = 0; i < tx.vout.size(); ++i)
tx.vout[i].amount = 0;
crypto::hash tx_prefix_hash;
get_transaction_prefix_hash(tx, tx_prefix_hash, hwdev);
rct::ctkeyV outSk;
tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, dest_keys, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, msout ? &kLRki : NULL, msout, index, outSk, rct_config, hwdev);
memwipe(inSk.data(), inSk.size() * sizeof(rct::ctkey));
CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout");
MCINFO("construct_tx", "transaction_created: " << get_transaction_hash(tx) << "\n" << obj_to_json_str(tx) << "\n");
tx.invalidate_hashes();
return true;
@ -1096,7 +1007,7 @@ namespace cryptonote
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const std::optional<cryptonote::tx_destination_entry>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, const rct::RCTConfig &rct_config, rct::multisig_out *msout, loki_construct_tx_params const &tx_params)
{
hw::device &hwdev = sender_account_keys.get_device();
hwdev.open_tx(tx_key);
hwdev.open_tx(tx_key, transaction::get_max_version_for_hf(tx_params.hf_version), tx_params.tx_type);
try {
// figure out if we need to make additional tx pubkeys
size_t num_stdaddresses = 0;
@ -1108,7 +1019,7 @@ namespace cryptonote
{
additional_tx_keys.clear();
for (const auto &d: destinations)
additional_tx_keys.push_back(keypair::generate(sender_account_keys.get_device()).sec);
additional_tx_keys.push_back(keypair{sender_account_keys.get_device()}.sec);
}
bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct_config, msout, true /*shuffle_outs*/, tx_params);
@ -1128,10 +1039,10 @@ namespace cryptonote
std::vector<crypto::secret_key> additional_tx_keys;
std::vector<tx_destination_entry> destinations_copy = destinations;
rct::RCTConfig rct_config{
tx_params.hf_version < network_version_10_bulletproofs ? rct::RangeProofBorromean : rct::RangeProofPaddedBulletproof,
tx_params.hf_version >= HF_VERSION_CLSAG ? 3 : tx_params.hf_version >= HF_VERSION_SMALLER_BP ? 2 : 1
};
// Always construct CLSAG transactions. They weren't actually acceptable before HF 16, but
// they are now for our fake networks (which we need to do because we no longer have pre-CLSAG
// tx generation code).
rct::RCTConfig rct_config{rct::RangeProofType::PaddedBulletproof, 3};
return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct_config, NULL, tx_params);
}

View File

@ -132,7 +132,7 @@ namespace cryptonote
struct tx_source_entry
{
typedef std::pair<uint64_t, rct::ctkey> output_entry;
using output_entry = std::pair<uint64_t, rct::ctkey>;
std::vector<output_entry> outputs; //index + key + optional ringct commitment
size_t real_output; //index in outputs vector of real output_entry
@ -223,8 +223,8 @@ namespace cryptonote
//---------------------------------------------------------------
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const std::optional<cryptonote::tx_destination_entry>& change_addr);
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const std::optional<cryptonote::tx_destination_entry>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const loki_construct_tx_params &tx_params = {});
bool construct_tx_with_tx_key (const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const std::optional<cryptonote::tx_destination_entry>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0}, rct::multisig_out *msout = NULL, bool shuffle_outs = true, loki_construct_tx_params const &tx_params = {});
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const std::optional<cryptonote::tx_destination_entry>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0}, rct::multisig_out *msout = NULL, loki_construct_tx_params const &tx_params = {});
bool construct_tx_with_tx_key (const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const std::optional<cryptonote::tx_destination_entry>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const rct::RCTConfig &rct_config, rct::multisig_out *msout = NULL, bool shuffle_outs = true, loki_construct_tx_params const &tx_params = {});
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const std::optional<cryptonote::tx_destination_entry>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, const rct::RCTConfig &rct_config, rct::multisig_out *msout = NULL, loki_construct_tx_params const &tx_params = {});
bool generate_output_ephemeral_keys(const size_t tx_version, bool &found_change,
const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key,
const cryptonote::tx_destination_entry &dst_entr, const std::optional<cryptonote::tx_destination_entry> &change_addr, const size_t output_index,

View File

@ -639,64 +639,32 @@ std::optional<uint64_t> expiry_blocks(cryptonote::network_type nettype, mapping_
return result;
}
static uint8_t *memcpy_helper(uint8_t *dest, void const *src, size_t size)
static void append_owner(std::string& buffer, const lns::generic_owner* owner)
{
std::memcpy(dest, src, size);
return dest + size;
}
static uint8_t *memcpy_generic_owner_helper(uint8_t *dest, lns::generic_owner const *owner)
{
if (!owner) return dest;
uint8_t *result = memcpy_helper(dest, reinterpret_cast<uint8_t const *>(&owner->type), sizeof(owner->type));
void const *src = &owner->wallet.address;
size_t src_len = sizeof(owner->wallet.address);
if (owner->type == lns::generic_owner_sig_type::ed25519)
{
src = &owner->ed25519;
src_len = sizeof(owner->ed25519);
if (owner) {
buffer += static_cast<char>(owner->type);
buffer += owner->type == lns::generic_owner_sig_type::ed25519
? tools::view_guts(owner->ed25519)
: tools::view_guts(owner->wallet.address);
}
result = memcpy_helper(result, src, src_len);
return result;
}
crypto::hash tx_extra_signature_hash(std::string_view value, lns::generic_owner const *owner, lns::generic_owner const *backup_owner, crypto::hash const &prev_txid)
std::string tx_extra_signature(std::string_view value, lns::generic_owner const *owner, lns::generic_owner const *backup_owner, crypto::hash const &prev_txid)
{
static_assert(sizeof(crypto::hash) == crypto_generichash_BYTES, "Using libsodium generichash for signature hash, require we fit into crypto::hash");
crypto::hash result = {};
if (value.size() > mapping_value::BUFFER_SIZE)
{
MERROR("Unexpected value len=" << value.size() << " greater than the expected capacity=" << mapping_value::BUFFER_SIZE);
return result;
return ""s;
}
uint8_t buffer[mapping_value::BUFFER_SIZE + sizeof(*owner) + sizeof(*backup_owner) + sizeof(prev_txid)] = {};
uint8_t *ptr = memcpy_helper(buffer, value.data(), value.size());
ptr = memcpy_generic_owner_helper(ptr, owner);
ptr = memcpy_generic_owner_helper(ptr, backup_owner);
ptr = memcpy_helper(ptr, prev_txid.data, sizeof(prev_txid));
std::string result;
result.reserve(mapping_value::BUFFER_SIZE + sizeof(*owner) + sizeof(*backup_owner) + sizeof(prev_txid));
result += value;
append_owner(result, owner);
append_owner(result, backup_owner);
result += tools::view_guts(prev_txid);
if (ptr > (buffer + sizeof(buffer)))
{
assert(ptr < buffer + sizeof(buffer));
MERROR("Unexpected buffer overflow");
return {};
}
size_t buffer_len = ptr - buffer;
static_assert(sizeof(owner->type) == sizeof(char), "Require byte alignment to avoid unaligned access exceptions");
crypto_generichash(reinterpret_cast<unsigned char *>(result.data), sizeof(result), buffer, buffer_len, NULL /*key*/, 0 /*key_len*/);
return result;
}
lns::generic_signature make_monero_signature(crypto::hash const &hash, crypto::public_key const &pkey, crypto::secret_key const &skey)
{
lns::generic_signature result = {};
result.type = lns::generic_owner_sig_type::monero;
generate_signature(hash, pkey, skey, result.monero);
return result;
}
@ -1091,21 +1059,21 @@ static bool validate_against_previous_mapping(lns::name_system_db &lns_db, uint6
return false;
// Validate signature
{
crypto::hash hash = tx_extra_signature_hash(lns_extra.encrypted_value,
lns_extra.field_is_set(lns::extra_field::owner) ? &lns_extra.owner : nullptr,
lns_extra.field_is_set(lns::extra_field::backup_owner) ? &lns_extra.backup_owner : nullptr,
expected_prev_txid);
if (check_condition(!hash, reason, tx, ", ", lns_extra_string(lns_db.network_type(), lns_extra), " unexpectedly failed to generate signature hash, please inform the Loki developers"))
return false;
auto data = tx_extra_signature(
lns_extra.encrypted_value,
lns_extra.field_is_set(lns::extra_field::owner) ? &lns_extra.owner : nullptr,
lns_extra.field_is_set(lns::extra_field::backup_owner) ? &lns_extra.backup_owner : nullptr,
expected_prev_txid);
if (check_condition(data.empty(), reason, tx, ", ", lns_extra_string(lns_db.network_type(), lns_extra), " unexpectedly failed to generate signature, please inform the Loki developers"))
return false;
if (check_condition(!verify_lns_signature(hash, lns_extra.signature, mapping.owner) &&
!verify_lns_signature(hash, lns_extra.signature, mapping.backup_owner), reason,
tx, ", ", lns_extra_string(lns_db.network_type(), lns_extra), " failed to verify signature for LNS update, current owner=", mapping.owner.to_string(lns_db.network_type()), ", backup owner=", mapping.backup_owner.to_string(lns_db.network_type())))
{
return false;
}
}
crypto::hash hash;
crypto_generichash(reinterpret_cast<unsigned char*>(hash.data), sizeof(hash), reinterpret_cast<const unsigned char*>(data.data()), data.size(), nullptr /*key*/, 0 /*key_len*/);
if (check_condition(!verify_lns_signature(hash, lns_extra.signature, mapping.owner) &&
!verify_lns_signature(hash, lns_extra.signature, mapping.backup_owner), reason,
tx, ", ", lns_extra_string(lns_db.network_type(), lns_extra), " failed to verify signature for LNS update, current owner=", mapping.owner.to_string(lns_db.network_type()), ", backup owner=", mapping.backup_owner.to_string(lns_db.network_type())))
return false;
}
else if (lns_extra.is_buying())
{

View File

@ -162,12 +162,11 @@ std::optional<std::string> name_hash_input_to_base64(std::string_view input);
bool validate_lns_name(mapping_type type, std::string name, std::string *reason = nullptr);
generic_signature make_monero_signature(crypto::hash const &hash, crypto::public_key const &pkey, crypto::secret_key const &skey);
generic_signature make_ed25519_signature(crypto::hash const &hash, crypto::ed25519_secret_key const &skey);
generic_owner make_monero_owner(cryptonote::account_public_address const &owner, bool is_subaddress);
generic_owner make_ed25519_owner(crypto::ed25519_public_key const &pkey);
bool parse_owner_to_generic_owner(cryptonote::network_type nettype, std::string_view owner, generic_owner &key, std::string *reason);
crypto::hash tx_extra_signature_hash(std::string_view value, generic_owner const *owner, generic_owner const *backup_owner, crypto::hash const &prev_txid);
std::string tx_extra_signature(std::string_view value, generic_owner const *owner, generic_owner const *backup_owner, crypto::hash const &prev_txid);
enum struct lns_tx_type { lookup, buy, update, renew };
// Converts a human readable case-insensitive string denoting the mapping type into a value suitable for storing into the LNS DB.

View File

@ -386,18 +386,18 @@ namespace service_nodes
{
switch (tx.rct_signatures.type)
{
case rct::RCTTypeSimple:
case rct::RCTTypeBulletproof:
case rct::RCTTypeBulletproof2:
case rct::RCTTypeCLSAG:
money_transferred = rct::decodeRctSimple(tx.rct_signatures, rct::sk2rct(scalar1), i, mask, hwdev);
break;
case rct::RCTTypeFull:
money_transferred = rct::decodeRct(tx.rct_signatures, rct::sk2rct(scalar1), i, mask, hwdev);
break;
default:
LOG_PRINT_L0(__func__ << ": Unsupported rct type: " << (int)tx.rct_signatures.type);
return 0;
case rct::RCTType::Simple:
case rct::RCTType::Bulletproof:
case rct::RCTType::Bulletproof2:
case rct::RCTType::CLSAG:
money_transferred = rct::decodeRctSimple(tx.rct_signatures, rct::sk2rct(scalar1), i, mask, hwdev);
break;
case rct::RCTType::Full:
money_transferred = rct::decodeRct(tx.rct_signatures, rct::sk2rct(scalar1), i, mask, hwdev);
break;
default:
LOG_PRINT_L0(__func__ << ": Unsupported rct type: " << (int)tx.rct_signatures.type);
return 0;
}
}
catch (const std::exception &e)
@ -538,10 +538,9 @@ namespace service_nodes
// The signer can try falsify the key image, but the equation used to
// construct the key image is re-derived by the verifier, false key
// images will not match the re-derived key image.
crypto::public_key const *ephemeral_pub_key_ptr = &ephemeral_pub_key;
for (auto proof = key_image_proofs.proofs.begin(); proof != key_image_proofs.proofs.end(); proof++)
{
if (!crypto::check_ring_signature((const crypto::hash &)(proof->key_image), proof->key_image, &ephemeral_pub_key_ptr, 1, &proof->signature))
if (!crypto::check_key_image_signature(proof->key_image, ephemeral_pub_key, proof->signature))
continue;
contribution->locked_contributions.emplace_back(service_node_info::contribution_t::version_t::v0, ephemeral_pub_key, proof->key_image, transferred);
@ -835,8 +834,8 @@ namespace service_nodes
if (cit != contributor.locked_contributions.end())
{
// NOTE(loki): This should be checked in blockchain check_tx_inputs already
crypto::hash const hash = service_nodes::generate_request_stake_unlock_hash(unlock.nonce);
if (crypto::check_signature(hash, cit->key_image_pub_key, unlock.signature))
if (crypto::check_signature(service_nodes::generate_request_stake_unlock_hash(unlock.nonce),
cit->key_image_pub_key, unlock.signature))
{
duplicate_info(it->second).requested_unlock_height = unlock_height;
return true;
@ -3580,60 +3579,70 @@ namespace service_nodes
bool service_node_info::can_be_voted_on(uint64_t height) const
{
// If the SN expired and was reregistered since the height we'll be voting on it prematurely
if (!this->is_fully_funded() || this->registration_height >= height) return false;
if (this->is_decommissioned() && this->last_decommission_height >= height) return false;
if (this->is_active())
{
// NOTE: This cast is safe. The definition of is_active() is that active_since_height >= 0
assert(this->active_since_height >= 0);
if (static_cast<uint64_t>(this->active_since_height) >= height) return false;
if (!is_fully_funded()) {
MDEBUG("SN vote at height " << height << " invalid: not fully funded");
return false;
} else if (height <= registration_height) {
MDEBUG("SN vote at height " << height << " invalid: height <= reg height (" << registration_height << ")");
return false;
} else if (is_decommissioned() && height <= last_decommission_height) {
MDEBUG("SN vote at height " << height << " invalid: height <= last decomm height (" << last_decommission_height << ")");
return false;
} else if (is_active()) {
assert(active_since_height >= 0); // should be satisfied whenever is_active() is true
if (height <= static_cast<uint64_t>(active_since_height)) {
MDEBUG("SN vote at height " << height << " invalid: height <= active-since height (" << active_since_height << ")");
return false;
}
}
MTRACE("SN vote at height " << height << " is valid.");
return true;
}
bool service_node_info::can_transition_to_state(uint8_t hf_version, uint64_t height, new_state proposed_state) const
{
if (hf_version >= cryptonote::network_version_13_enforce_checkpoints)
{
if (!can_be_voted_on(height))
if (hf_version >= cryptonote::network_version_13_enforce_checkpoints) {
if (!can_be_voted_on(height)) {
MDEBUG("SN state transition invalid: " << height << " is not a valid vote height");
return false;
}
if (proposed_state == new_state::deregister)
{
if (height <= this->registration_height)
if (proposed_state == new_state::deregister) {
if (height <= registration_height) {
MDEBUG("SN deregister invalid: vote height (" << height << ") <= registration_height (" << registration_height << ")");
return false;
}
else if (proposed_state == new_state::ip_change_penalty)
{
if (height <= this->last_ip_change_height)
}
} else if (proposed_state == new_state::ip_change_penalty) {
if (height <= last_ip_change_height) {
MDEBUG("SN ip change penality invalid: vote height (" << height << ") <= last_ip_change_height (" << last_ip_change_height << ")");
return false;
}
}
if (this->is_decommissioned())
{
return proposed_state != new_state::decommission && proposed_state != new_state::ip_change_penalty;
}
return (proposed_state != new_state::recommission);
}
else
{
if (proposed_state == new_state::deregister)
{
if (height < this->registration_height) return false;
}
if (this->is_decommissioned())
{
return proposed_state != new_state::decommission && proposed_state != new_state::ip_change_penalty;
}
else
{
return (proposed_state != new_state::recommission);
} else { // pre-HF13
if (proposed_state == new_state::deregister) {
if (height < registration_height) {
MDEBUG("SN deregister invalid: vote height (" << height << ") < registration_height (" << registration_height << ")");
return false;
}
}
}
if (is_decommissioned()) {
if (proposed_state == new_state::decommission) {
MDEBUG("SN decommission invalid: already decommissioned");
return false;
} else if (proposed_state == new_state::ip_change_penalty) {
MDEBUG("SN ip change penalty invalid: currently decommissioned");
return false;
}
return true; // recomm or dereg
} else if (proposed_state == new_state::recommission) {
MDEBUG("SN recommission invalid: not recommissioned");
return false;
}
MTRACE("SN state change is valid");
return true;
}
payout service_node_info_to_payout(crypto::public_key const &key, service_node_info const &info)

View File

@ -1,6 +1,7 @@
#include "cryptonote_config.h"
#include "common/loki.h"
#include "epee/int-util.h"
#include <boost/endian/conversion.hpp>
#include <limits>
#include <vector>
#include <boost/lexical_cast.hpp>
@ -114,17 +115,11 @@ bool check_service_node_portions(uint8_t hf_version, const std::vector<uint64_t>
crypto::hash generate_request_stake_unlock_hash(uint32_t nonce)
{
crypto::hash result = {};
char const *nonce_ptr = (char *)&nonce;
char *hash_ptr = result.data;
static_assert(sizeof(result) % sizeof(nonce) == 0, "The nonce should be evenly divisible into the hash");
for (size_t i = 0; i < sizeof(result) / sizeof(nonce); ++i)
{
memcpy(hash_ptr, nonce_ptr, sizeof(nonce));
hash_ptr += sizeof(nonce);
}
assert(hash_ptr == (char *)result.data + sizeof(result));
static_assert(sizeof(crypto::hash) == 8 * sizeof(uint32_t) && alignof(crypto::hash) >= alignof(uint32_t));
crypto::hash result;
boost::endian::native_to_little_inplace(nonce);
for (size_t i = 0; i < 8; i++)
reinterpret_cast<uint32_t*>(result.data)[i] = nonce;
return result;
}

View File

@ -44,6 +44,12 @@ target_link_libraries(device
version
extra)
option(HWDEVICE_DEBUG "Enable hardware wallet debugging (requires also using a debug build of the Ledger wallet)" OFF)
if(HWDEVICE_DEBUG)
target_compile_definitions(device PUBLIC DEBUG_HWDEVICE IODUMMYCRYPT_HWDEVICE)
endif()
if(BUILD_STATIC_DEPS)
message(STATUS "Using HIDAPI from static build deps")
else()

View File

@ -33,6 +33,8 @@
#include "crypto/chacha.h"
#include "ringct/rctTypes.h"
#include "cryptonote_config.h"
#include "epee/wipeable_string.h"
#include "cryptonote_basic/txtypes.h"
#ifndef USE_DEVICE_LEDGER
@ -118,10 +120,10 @@ namespace hw {
/* ======================================================================= */
/* SETUP/TEARDOWN */
/* ======================================================================= */
virtual bool set_name(const std::string &name) = 0;
virtual const std::string get_name() const = 0;
virtual bool set_name(std::string_view name) = 0;
virtual std::string get_name() const = 0;
virtual bool init(void) = 0;
virtual bool init(void) = 0;
virtual bool release() = 0;
virtual bool connect(void) = 0;
@ -178,6 +180,9 @@ namespace hw {
virtual bool derive_public_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::public_key &pub, crypto::public_key &derived_pub) = 0;
virtual bool secret_key_to_public_key(const crypto::secret_key &sec, crypto::public_key &pub) = 0;
virtual bool generate_key_image(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_image &image) = 0;
virtual bool generate_key_image_signature(const crypto::key_image& image, const crypto::public_key& pub, const crypto::secret_key& sec, crypto::signature& sig) = 0;
virtual bool generate_unlock_signature(const crypto::public_key& pub, const crypto::secret_key& sec, crypto::signature& sig) = 0;
virtual bool generate_lns_signature(std::string_view signature_data, const cryptonote::account_keys& keys, const cryptonote::subaddress_index& index, crypto::signature& sig) = 0;
// alternative prototypes available in libringct
rct::key scalarmultKey(const rct::key &P, const rct::key &a)
@ -202,7 +207,7 @@ namespace hw {
const crypto::public_key &R, const crypto::public_key &A, const std::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
crypto::signature &sig) = 0;
virtual bool open_tx(crypto::secret_key &tx_key) = 0;
virtual bool open_tx(crypto::secret_key &tx_key, cryptonote::txversion txversion, cryptonote::txtype txtype) = 0;
virtual void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) = 0;
@ -218,23 +223,32 @@ namespace hw {
virtual bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec, bool short_amount) = 0;
virtual bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec, bool short_amount) = 0;
virtual bool generate_output_ephemeral_keys(const size_t tx_version, bool &found_change, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key,
const cryptonote::tx_destination_entry &dst_entr, const std::optional<cryptonote::tx_destination_entry> &change_addr, const size_t output_index,
const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys,
std::vector<crypto::public_key> &additional_tx_public_keys,
std::vector<rct::key> &amount_keys,
crypto::public_key &out_eph_public_key) = 0;
virtual bool mlsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) = 0;
virtual bool mlsag_prepare(const rct::key &H, const rct::key &xx, rct::key &a, rct::key &aG, rct::key &aHP, rct::key &rvII) = 0;
virtual bool mlsag_prepare(rct::key &a, rct::key &aG) = 0;
virtual bool mlsag_hash(const rct::keyV &long_message, rct::key &c) = 0;
virtual bool mlsag_sign(const rct::key &c, const rct::keyV &xx, const rct::keyV &alpha, const size_t rows, const size_t dsRows, rct::keyV &ss) = 0;
virtual bool generate_output_ephemeral_keys(
size_t tx_version,
bool& found_change,
const cryptonote::account_keys& sender_account_keys,
const crypto::public_key& txkey_pub,
const crypto::secret_key& tx_key,
const cryptonote::tx_destination_entry& dst_entr,
const std::optional<cryptonote::tx_destination_entry>& change_addr,
size_t output_index,
bool need_additional_txkeys,
const std::vector<crypto::secret_key>& additional_tx_keys,
std::vector<crypto::public_key>& additional_tx_public_keys,
std::vector<rct::key>& amount_keys,
crypto::public_key& out_eph_public_key) = 0;
virtual bool clsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) = 0;
virtual bool clsag_prepare(const rct::key &p, const rct::key &z, rct::key &I, rct::key &D, const rct::key &H, rct::key &a, rct::key &aG, rct::key &aH) = 0;
virtual bool clsag_hash(const rct::keyV &data, rct::key &hash) = 0;
virtual bool clsag_sign(const rct::key &c, const rct::key &a, const rct::key &p, const rct::key &z, const rct::key &mu_P, const rct::key &mu_C, rct::key &s) = 0;
// Retrieves the tx secret key from the device; this should only be called for staking
// transactions. `key` will be whatever we got back from the device, but for hardware
// devices that value may be encrypted or null; this call should update it to the actual
// secret key value, if necessary.
virtual bool update_staking_tx_secret_key(crypto::secret_key& key) = 0;
virtual bool close_tx(void) = 0;
virtual bool has_ki_cold_sync(void) const { return false; }
@ -249,10 +263,10 @@ namespace hw {
device_mode mode;
} ;
struct reset_mode {
struct mode_resetter {
device& hwref;
reset_mode(hw::device& dev) : hwref(dev) { }
~reset_mode() { hwref.set_mode(hw::device::NONE);}
mode_resetter(hw::device& dev) : hwref(dev) { }
~mode_resetter() { hwref.set_mode(hw::device::NONE);}
};
class device_registry {

View File

@ -31,12 +31,14 @@
#include "device_default.hpp"
#include "crypto/crypto.h"
#include "epee/int-util.h"
#include "cryptonote_basic/account.h"
#include "cryptonote_basic/subaddress_index.h"
#include "cryptonote_core/cryptonote_tx_utils.h"
#include "ringct/rctOps.h"
#include "cryptonote_config.h"
#include <sodium/crypto_generichash.h>
namespace hw {
@ -59,12 +61,12 @@ namespace hw {
/* ======================================================================= */
/* SETUP/TEARDOWN */
/* ======================================================================= */
bool device_default::set_name(const std::string &name) {
bool device_default::set_name(std::string_view name) {
this->name = name;
return true;
}
const std::string device_default::get_name() const {
return this->name;
std::string device_default::get_name() const {
return name;
}
bool device_default::init(void) {
@ -262,6 +264,31 @@ namespace hw {
return true;
}
bool device_default::generate_key_image_signature(const crypto::key_image& image, const crypto::public_key& pub, const crypto::secret_key& sec, crypto::signature& sig) {
crypto::generate_key_image_signature(image, pub, sec, sig);
return true;
}
bool device_default::generate_unlock_signature(const crypto::public_key& pub, const crypto::secret_key& sec, crypto::signature& sig) {
crypto::generate_signature(cryptonote::tx_extra_tx_key_image_unlock::HASH, pub, sec, sig);
return true;
}
bool device_default::generate_lns_signature(std::string_view sig_data, const cryptonote::account_keys& keys, const cryptonote::subaddress_index& index, crypto::signature& sig) {
crypto::hash hash;
crypto_generichash(reinterpret_cast<unsigned char*>(hash.data), sizeof(hash), reinterpret_cast<const unsigned char*>(sig_data.data()), sig_data.size(), nullptr, 0);
crypto::secret_key skey = keys.m_spend_secret_key;
if (!index.is_zero())
sc_secret_add(skey, skey, get_subaddress_secret_key(keys.m_view_secret_key, index));
crypto::public_key pkey;
secret_key_to_public_key(skey, pkey);
crypto::generate_signature(hash, pkey, skey, sig);
return true;
}
bool device_default::conceal_derivation(crypto::key_derivation &derivation, const crypto::public_key &tx_pub_key, const std::vector<crypto::public_key> &additional_tx_pub_keys, const crypto::key_derivation &main_derivation, const std::vector<crypto::key_derivation> &additional_derivations){
return true;
}
@ -275,8 +302,8 @@ namespace hw {
crypto::generate_tx_proof(prefix_hash, R, A, B, D, r, sig);
}
bool device_default::open_tx(crypto::secret_key &tx_key) {
cryptonote::keypair txkey = cryptonote::keypair::generate(*this);
bool device_default::open_tx(crypto::secret_key &tx_key, cryptonote::txversion /*version*/, cryptonote::txtype /*type*/) {
cryptonote::keypair txkey{*this};
tx_key = txkey.sec;
return true;
}
@ -285,12 +312,20 @@ namespace hw {
cryptonote::get_transaction_prefix_hash(tx, h);
}
bool device_default::generate_output_ephemeral_keys(const size_t tx_version, bool &found_change,
const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key,
const cryptonote::tx_destination_entry &dst_entr, const std::optional<cryptonote::tx_destination_entry> &change_addr, const size_t output_index,
const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys,
std::vector<crypto::public_key> &additional_tx_public_keys,
std::vector<rct::key> &amount_keys, crypto::public_key &out_eph_public_key) {
bool device_default::generate_output_ephemeral_keys(
const size_t tx_version,
bool& found_change,
const cryptonote::account_keys& sender_account_keys,
const crypto::public_key& txkey_pub,
const crypto::secret_key& tx_key,
const cryptonote::tx_destination_entry& dst_entr,
const std::optional<cryptonote::tx_destination_entry>& change_addr,
const size_t output_index,
const bool need_additional_txkeys,
const std::vector<crypto::secret_key>& additional_tx_keys,
std::vector<crypto::public_key>& additional_tx_public_keys,
std::vector<rct::key>& amount_keys,
crypto::public_key& out_eph_public_key) {
// make additional tx pubkey if necessary
cryptonote::keypair additional_txkey;
@ -369,39 +404,11 @@ namespace hw {
return true;
}
bool device_default::mlsag_prepare(const rct::key &H, const rct::key &xx,
rct::key &a, rct::key &aG, rct::key &aHP, rct::key &II) {
rct::skpkGen(a, aG);
rct::scalarmultKey(aHP, H, a);
rct::scalarmultKey(II, H, xx);
return true;
}
bool device_default::mlsag_prepare(rct::key &a, rct::key &aG) {
rct::skpkGen(a, aG);
return true;
}
bool device_default::mlsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) {
bool device_default::clsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) {
prehash = rct::cn_fast_hash(hashes);
return true;
}
bool device_default::mlsag_hash(const rct::keyV &toHash, rct::key &c_old) {
c_old = rct::hash_to_scalar(toHash);
return true;
}
bool device_default::mlsag_sign(const rct::key &c, const rct::keyV &xx, const rct::keyV &alpha, const size_t rows, const size_t dsRows, rct::keyV &ss ) {
CHECK_AND_ASSERT_THROW_MES(dsRows<=rows, "dsRows greater than rows");
CHECK_AND_ASSERT_THROW_MES(xx.size() == rows, "xx size does not match rows");
CHECK_AND_ASSERT_THROW_MES(alpha.size() == rows, "alpha size does not match rows");
CHECK_AND_ASSERT_THROW_MES(ss.size() == rows, "ss size does not match rows");
for (size_t j = 0; j < rows; j++) {
sc_mulsub(ss[j].bytes, c.bytes, xx[j].bytes, alpha[j].bytes);
}
return true;
}
bool device_default::clsag_prepare(const rct::key &p, const rct::key &z, rct::key &I, rct::key &D, const rct::key &H, rct::key &a, rct::key &aG, rct::key &aH) {
rct::skpkGen(a,aG); // aG = a*G
rct::scalarmultKey(aH,H,a); // aH = a*H
@ -425,6 +432,11 @@ namespace hw {
return true;
}
bool device_default::update_staking_tx_secret_key([[maybe_unused]] crypto::secret_key& key) {
return true;
}
bool device_default::close_tx() {
return true;
}

View File

@ -50,8 +50,8 @@ namespace hw {
/* ======================================================================= */
/* SETUP/TEARDOWN */
/* ======================================================================= */
bool set_name(const std::string &name) override;
const std::string get_name() const override;
bool set_name(std::string_view name) override;
std::string get_name() const override;
bool init(void) override;
bool release() override;
@ -101,6 +101,9 @@ namespace hw {
bool derive_public_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::public_key &pub, crypto::public_key &derived_pub) override;
bool secret_key_to_public_key(const crypto::secret_key &sec, crypto::public_key &pub) override;
bool generate_key_image(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_image &image) override;
bool generate_key_image_signature(const crypto::key_image& image, const crypto::public_key& pub, const crypto::secret_key& sec, crypto::signature& sig) override;
bool generate_unlock_signature(const crypto::public_key& pub, const crypto::secret_key& sec, crypto::signature& sig) override;
bool generate_lns_signature(std::string_view signature_data, const cryptonote::account_keys& keys, const cryptonote::subaddress_index& index, crypto::signature& sig) override;
/* ======================================================================= */
@ -111,7 +114,7 @@ namespace hw {
const crypto::public_key &R, const crypto::public_key &A, const std::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
crypto::signature &sig) override;
bool open_tx(crypto::secret_key &tx_key) override;
bool open_tx(crypto::secret_key &tx_key, cryptonote::txversion txversion, cryptonote::txtype txtype) override;
void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) override;
bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) override;
@ -121,23 +124,28 @@ namespace hw {
bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec, bool short_amount) override;
bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec, bool short_amount) override;
bool generate_output_ephemeral_keys(const size_t tx_version, bool &found_change, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key,
const cryptonote::tx_destination_entry &dst_entr, const std::optional<cryptonote::tx_destination_entry> &change_addr, const size_t output_index,
const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys,
std::vector<crypto::public_key> &additional_tx_public_keys,
std::vector<rct::key> &amount_keys,
crypto::public_key &out_eph_public_key) override;
bool mlsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) override;
bool mlsag_prepare(const rct::key &H, const rct::key &xx, rct::key &a, rct::key &aG, rct::key &aHP, rct::key &rvII) override;
bool mlsag_prepare(rct::key &a, rct::key &aG) override;
bool mlsag_hash(const rct::keyV &long_message, rct::key &c) override;
bool mlsag_sign(const rct::key &c, const rct::keyV &xx, const rct::keyV &alpha, const size_t rows, const size_t dsRows, rct::keyV &ss) override;
bool generate_output_ephemeral_keys(
size_t tx_version,
bool& found_change,
const cryptonote::account_keys& sender_account_keys,
const crypto::public_key& txkey_pub,
const crypto::secret_key& tx_key,
const cryptonote::tx_destination_entry& dst_entr,
const std::optional<cryptonote::tx_destination_entry>& change_addr,
size_t output_index,
bool need_additional_txkeys,
const std::vector<crypto::secret_key>& additional_tx_keys,
std::vector<crypto::public_key>& additional_tx_public_keys,
std::vector<rct::key>& amount_keys,
crypto::public_key& out_eph_public_key) override;
bool clsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) override;
bool clsag_prepare(const rct::key &p, const rct::key &z, rct::key &I, rct::key &D, const rct::key &H, rct::key &a, rct::key &aG, rct::key &aH) override;
bool clsag_hash(const rct::keyV &data, rct::key &hash) override;
bool clsag_sign(const rct::key &c, const rct::key &a, const rct::key &p, const rct::key &z, const rct::key &mu_P, const rct::key &mu_C, rct::key &s) override;
bool update_staking_tx_secret_key(crypto::secret_key& key) override;
bool close_tx(void) override;
};

View File

@ -28,7 +28,7 @@
//
#if defined(HAVE_HIDAPI)
#include <boost/scope_exit.hpp>
#include "common/loki.h"
#include "log.hpp"
#include "device_io_hid.hpp"
@ -76,18 +76,15 @@ namespace hw {
timeout(to),
usb_vid(0),
usb_pid(0),
usb_device(NULL) {
usb_device(nullptr) {
}
device_io_hid::device_io_hid() : device_io_hid(DEFAULT_CHANNEL, DEFAULT_TAG, DEFAULT_PACKET_SIZE, DEFAULT_TIMEOUT) {
}
void device_io_hid::io_hid_log(int read, unsigned char* buffer, int block_len) {
if (hid_verbose) {
char strbuffer[1024];
hw::buffer_to_str(strbuffer, sizeof(strbuffer), (char*)buffer, block_len);
MDEBUG( "HID " << (read?"<":">") <<" : "<<strbuffer);
}
if (hid_verbose)
MDEBUG("HID " << (read ? '<' : '>') << " : " << lokimq::to_hex(buffer, buffer + block_len));
}
void device_io_hid::init() {
@ -99,7 +96,7 @@ namespace hw {
void device_io_hid::connect(void *params) {
hid_conn_params *p = (struct hid_conn_params*)params;
if (!this->connect(p->vid, p->pid, p->interface_number, p->usage_page)) {
ASSERT_X(false, "No device found");
ASSERT_X(false, "No device found. (Is the device running with the wallet app opened?)");
}
}
@ -109,7 +106,7 @@ namespace hw {
return;
}
}
ASSERT_X(false, "No device found");
ASSERT_X(false, "No device found. (Is the device running with the wallet app opened?)");
}
hid_device_info *device_io_hid::find_device(hid_device_info *devices_list, std::optional<int> interface_number, std::optional<unsigned short> usage_page) {
@ -123,14 +120,13 @@ namespace hw {
hid_device_info *result = nullptr;
for (; devices_list != nullptr; devices_list = devices_list->next) {
BOOST_SCOPE_EXIT(&devices_list, &result) {
LOKI_DEFER {
MDEBUG( (result == devices_list ? "SELECTED" : "SKIPPED ") <<
" HID Device" <<
" path " << safe_hid_path(devices_list) <<
" interface_number " << devices_list->interface_number <<
" usage_page " << devices_list->usage_page);
}
BOOST_SCOPE_EXIT_END
};
if (result != nullptr) {
continue;
@ -157,9 +153,9 @@ namespace hw {
hwdev_info_list = hid_enumerate(vid, pid);
if (!hwdev_info_list) {
MDEBUG("Unable to enumerate device "+std::to_string(vid)+":"+std::to_string(vid)+ ": "+ safe_hid_error(this->usb_device));
return NULL;
return nullptr;
}
hwdev = NULL;
hwdev = nullptr;
if (hid_device_info *device = find_device(hwdev_info_list, interface_number, usage_page)) {
hwdev = hid_open_path(device->path);
}
@ -173,7 +169,7 @@ namespace hw {
bool device_io_hid::connected() const {
return this->usb_device != NULL;
return this->usb_device != nullptr;
}
int device_io_hid::exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len, bool user_input) {
@ -235,7 +231,7 @@ namespace hw {
}
this->usb_vid = 0;
this->usb_pid = 0;
this->usb_device = NULL;
this->usb_device = nullptr;
}
void device_io_hid::release() {
@ -304,7 +300,7 @@ namespace hw {
unsigned int val;
//end?
if ((data == NULL) || (data_len < 7 + 5)) {
if ((data == nullptr) || (data_len < 7 + 5)) {
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,7 @@
#pragma once
#include <chrono>
#include <cstddef>
#include <string>
#include "device.hpp"
@ -38,11 +39,16 @@
namespace hw {
using namespace std::literals;
namespace ledger {
// Required coin value as returned by INS_GET_NETWORK during connection
constexpr auto COIN_NETWORK = "LOKI"sv;
/* Minimal supported version */
#define MINIMAL_APP_VERSION_MAJOR 1
#define MINIMAL_APP_VERSION_MINOR 6
#define MINIMAL_APP_VERSION_MAJOR 0
#define MINIMAL_APP_VERSION_MINOR 9
#define MINIMAL_APP_VERSION_MICRO 0
#define VERSION(M,m,u) ((M)<<16|(m)<<8|(u))
@ -56,49 +62,39 @@ namespace hw {
#ifdef WITH_DEVICE_LEDGER
// Origin: https://github.com/LedgerHQ/ledger-app-monero/blob/master/src/monero_types.h
#define SW_BYTES_REMAINING_00 0x6100
#define SW_WARNING_STATE_UNCHANGED 0x6200
#define SW_STATE_TERMINATED 0x6285
#define SW_MORE_DATA_AVAILABLE 0x6310
#define SW_WRONG_LENGTH 0x6700
#define SW_LOGICAL_CHANNEL_NOT_SUPPORTED 0x6881
#define SW_SECURE_MESSAGING_NOT_SUPPORTED 0x6882
#define SW_LAST_COMMAND_EXPECTED 0x6883
#define SW_COMMAND_CHAINING_NOT_SUPPORTED 0x6884
#define SW_SECURITY_LOAD_KEY 0x6900
#define SW_SECURITY_COMMITMENT_CONTROL 0x6911
#define SW_SECURITY_AMOUNT_CHAIN_CONTROL 0x6912
#define SW_SECURITY_COMMITMENT_CHAIN_CONTROL 0x6913
#define SW_SECURITY_OUTKEYS_CHAIN_CONTROL 0x6914
#define SW_SECURITY_MAXOUTPUT_REACHED 0x6915
#define SW_SECURITY_TRUSTED_INPUT 0x6916
#define SW_CLIENT_NOT_SUPPORTED 0x6930
#define SW_SECURITY_STATUS_NOT_SATISFIED 0x6982
#define SW_FILE_INVALID 0x6983
#define SW_PIN_BLOCKED 0x6983
#define SW_DATA_INVALID 0x6984
#define SW_CONDITIONS_NOT_SATISFIED 0x6985
#define SW_COMMAND_NOT_ALLOWED 0x6986
#define SW_APPLET_SELECT_FAILED 0x6999
#define SW_WRONG_DATA 0x6a80
#define SW_FUNC_NOT_SUPPORTED 0x6a81
#define SW_FILE_NOT_FOUND 0x6a82
#define SW_RECORD_NOT_FOUND 0x6a83
#define SW_FILE_FULL 0x6a84
#define SW_INCORRECT_P1P2 0x6a86
#define SW_REFERENCED_DATA_NOT_FOUND 0x6a88
#define SW_WRONG_P1P2 0x6b00
#define SW_CORRECT_LENGTH_00 0x6c00
#define SW_INS_NOT_SUPPORTED 0x6d00
#define SW_CLA_NOT_SUPPORTED 0x6e00
#define SW_UNKNOWN 0x6f00
#define SW_OK 0x9000
#define SW_ALGORITHM_UNSUPPORTED 0x9484
namespace {
bool apdu_verbose =true;
}
#define SW_WRONG_LENGTH 0x6700
#define SW_SECURITY_PIN_LOCKED 0x6910
#define SW_SECURITY_LOAD_KEY 0x6911
#define SW_SECURITY_COMMITMENT_CONTROL 0x6912
#define SW_SECURITY_AMOUNT_CHAIN_CONTROL 0x6913
#define SW_SECURITY_COMMITMENT_CHAIN_CONTROL 0x6914
#define SW_SECURITY_OUTKEYS_CHAIN_CONTROL 0x6915
#define SW_SECURITY_MAXOUTPUT_REACHED 0x6916
#define SW_SECURITY_HMAC 0x6917
#define SW_SECURITY_RANGE_VALUE 0x6918
#define SW_SECURITY_INTERNAL 0x6919
#define SW_SECURITY_MAX_SIGNATURE_REACHED 0x691A
#define SW_SECURITY_PREFIX_HASH 0x691B
#define SW_SECURITY_LOCKED 0x69EE
#define SW_COMMAND_NOT_ALLOWED 0x6980
#define SW_SUBCOMMAND_NOT_ALLOWED 0x6981
#define SW_DENY 0x6982
#define SW_KEY_NOT_SET 0x6983
#define SW_WRONG_DATA 0x6984
#define SW_WRONG_DATA_RANGE 0x6985
#define SW_IO_FULL 0x6986
#define SW_CLIENT_NOT_SUPPORTED 0x6A30
#define SW_WRONG_P1P2 0x6B00
#define SW_INS_NOT_SUPPORTED 0x6D00
#define SW_PROTOCOL_NOT_SUPPORTED 0x6E00
#define SW_UNKNOWN 0x6F00
void set_apdu_verbose(bool verbose);
@ -141,8 +137,8 @@ namespace hw {
public:
std::vector<SecHMAC> hmacs;
void find_mac(const uint8_t sec[32], uint8_t hmac[32]) ;
void add_mac(const uint8_t sec[32], const uint8_t hmac[32]) ;
void find_mac(const unsigned char sec[32], unsigned char hmac[32]) ;
void add_mac(const unsigned char sec[32], const unsigned char hmac[32]) ;
void clear() ;
};
@ -153,37 +149,59 @@ namespace hw {
class device_ledger : public hw::device {
private:
// Locker for concurrent access
mutable std::recursive_mutex device_locker;
mutable std::mutex command_locker;
mutable std::recursive_mutex device_locker;
mutable std::mutex command_locker;
//IO
hw::io::device_io_hid hw_device;
unsigned int length_send;
unsigned int length_send;
unsigned char buffer_send[BUFFER_SEND_SIZE];
unsigned int length_recv;
unsigned int length_recv;
unsigned char buffer_recv[BUFFER_RECV_SIZE];
unsigned int sw;
unsigned int id;
void logCMD(void);
void logRESP(void);
unsigned int exchange(unsigned int ok=SW_OK, unsigned int mask=0xFFFF);
unsigned int exchange_wait_on_input(unsigned int ok=SW_OK, unsigned int mask=0xFFFF);
void reset_buffer(void);
int set_command_header(unsigned char ins, unsigned char p1 = 0x00, unsigned char p2 = 0x00);
int set_command_header_noopt(unsigned char ins, unsigned char p1 = 0x00, unsigned char p2 = 0x00);
unsigned int sw;
unsigned int id;
std::chrono::steady_clock::time_point last_cmd;
void logCMD();
void logRESP();
unsigned int exchange(bool wait_on_input = false);
void reset_buffer();
int set_command_header(unsigned char ins, unsigned char p1 = 0x00, unsigned char p2 = 0x00);
int set_command_header_noopt(unsigned char ins, unsigned char p1 = 0x00, unsigned char p2 = 0x00);
void send_simple(unsigned char ins, unsigned char p1 = 0x00);
void send_bytes(const void* buf, size_t size, int& offset);
void receive_bytes(void* dest, size_t size, int& offset);
void receive_bytes(void* dest, size_t size);
void send_u16(uint16_t x, int& offset);
void send_u32(uint32_t x, int& offset);
uint32_t receive_u32(int& offset);
uint32_t receive_u32();
void send_secret(const unsigned char sec[32], int &offset);
void send_secret(const char sec[32], int &offset) { send_secret(reinterpret_cast<const unsigned char*>(sec), offset); }
void receive_secret(unsigned char sec[32], int &offset);
void receive_secret(char sec[32], int &offset) { receive_secret(reinterpret_cast<unsigned char*>(sec), offset); }
void check_network_type();
void exchange_multipart_data(uint8_t ins, uint8_t p1, std::string_view data, uint8_t chunk_size);
void send_finish(int& offset);
unsigned int finish_and_exchange(int& offset, bool wait_on_input = false);
// hw running mode
device_mode mode;
bool tx_in_progress;
cryptonote::network_type nettype = cryptonote::network_type::UNDEFINED; // Set by the wallet before connecting
// map public destination key to ephemeral destination key
Keymap key_map;
bool add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const bool is_change,
const bool need_additional, const size_t real_output_index,
const rct::key &amount_key, const crypto::public_key &out_eph_public_key);
bool add_output_key_mapping(
const crypto::public_key& Aout,
const crypto::public_key& Bout,
bool is_subaddress,
bool is_change,
bool need_additional,
size_t real_output_index,
const rct::key& amount_key,
const crypto::public_key& out_eph_public_key);
//hmac for some encrypted value
HMACmap hmac_map;
@ -193,7 +211,7 @@ namespace hw {
//extra debug
#ifdef DEBUG_HWDEVICE
device *controle_device;
device *debug_device;
#endif
public:
@ -205,19 +223,19 @@ namespace hw {
explicit operator bool() const override {return this->connected(); }
bool reset(void);
bool reset();
/* ======================================================================= */
/* SETUP/TEARDOWN */
/* ======================================================================= */
bool set_name(const std::string &name) override;
bool set_name(std::string_view name) override;
const std::string get_name() const override;
bool init(void) override;
std::string get_name() const override;
bool init() override;
bool release() override;
bool connect(void) override;
bool connect() override;
bool disconnect() override;
bool connected(void) const;
bool connected() const;
bool set_mode(device_mode mode) override;
@ -227,9 +245,9 @@ namespace hw {
/* ======================================================================= */
/* LOCKER */
/* ======================================================================= */
void lock(void) override;
void unlock(void) override;
bool try_lock(void) override;
void lock() override;
void unlock() override;
bool try_lock() override;
/* ======================================================================= */
/* WALLET & ADDRESS */
@ -238,6 +256,7 @@ namespace hw {
bool get_secret_keys(crypto::secret_key &viewkey , crypto::secret_key &spendkey) override;
bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds) override;
void display_address(const cryptonote::subaddress_index& index, const std::optional<crypto::hash8> &payment_id) override;
void set_network_type(cryptonote::network_type network_type) override;
/* ======================================================================= */
/* SUB ADDRESS */
@ -263,6 +282,9 @@ namespace hw {
bool derive_public_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::public_key &pub, crypto::public_key &derived_pub) override;
bool secret_key_to_public_key(const crypto::secret_key &sec, crypto::public_key &pub) override;
bool generate_key_image(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_image &image) override;
bool generate_key_image_signature(const crypto::key_image& image, const crypto::public_key& pub, const crypto::secret_key& sec, crypto::signature& sig) override;
bool generate_unlock_signature(const crypto::public_key& pub, const crypto::secret_key& sec, crypto::signature& sig) override;
bool generate_lns_signature(std::string_view sig_data, const cryptonote::account_keys& keys, const cryptonote::subaddress_index& index, crypto::signature& sig) override;
/* ======================================================================= */
/* TRANSACTION */
@ -271,7 +293,7 @@ namespace hw {
const crypto::public_key &R, const crypto::public_key &A, const std::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
crypto::signature &sig) override;
bool open_tx(crypto::secret_key &tx_key) override;
bool open_tx(crypto::secret_key &tx_key, cryptonote::txversion tx_version, cryptonote::txtype tx_type) override;
void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) override;
@ -282,34 +304,31 @@ namespace hw {
bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec, bool short_format) override;
bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec, bool short_format) override;
bool generate_output_ephemeral_keys(const size_t tx_version, bool &found_change, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key,
const cryptonote::tx_destination_entry &dst_entr, const std::optional<cryptonote::tx_destination_entry> &change_addr, const size_t output_index,
const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys,
std::vector<crypto::public_key> &additional_tx_public_keys,
std::vector<rct::key> &amount_keys,
crypto::public_key &out_eph_public_key) override;
bool mlsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) override;
bool mlsag_prepare(const rct::key &H, const rct::key &xx, rct::key &a, rct::key &aG, rct::key &aHP, rct::key &rvII) override;
bool mlsag_prepare(rct::key &a, rct::key &aG) override;
bool mlsag_hash(const rct::keyV &long_message, rct::key &c) override;
bool mlsag_sign( const rct::key &c, const rct::keyV &xx, const rct::keyV &alpha, const size_t rows, const size_t dsRows, rct::keyV &ss) override;
bool generate_output_ephemeral_keys(
size_t tx_version,
bool& found_change,
const cryptonote::account_keys& sender_account_keys,
const crypto::public_key& txkey_pub,
const crypto::secret_key& tx_key,
const cryptonote::tx_destination_entry& dst_entr,
const std::optional<cryptonote::tx_destination_entry>& change_addr,
size_t output_index,
bool need_additional_txkeys,
const std::vector<crypto::secret_key>& additional_tx_keys,
std::vector<crypto::public_key>& additional_tx_public_keys,
std::vector<rct::key>& amount_keys,
crypto::public_key& out_eph_public_key) override;
bool clsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) override;
bool clsag_prepare(const rct::key &p, const rct::key &z, rct::key &I, rct::key &D, const rct::key &H, rct::key &a, rct::key &aG, rct::key &aH) override;
bool clsag_hash(const rct::keyV &data, rct::key &hash) override;
bool clsag_sign(const rct::key &c, const rct::key &a, const rct::key &p, const rct::key &z, const rct::key &mu_P, const rct::key &mu_C, rct::key &s) override;
bool update_staking_tx_secret_key(crypto::secret_key& key) override;
bool close_tx(void) override;
bool close_tx() override;
};
#ifdef DEBUG_HWDEVICE
extern crypto::secret_key dbg_viewkey;
extern crypto::secret_key dbg_spendkey;
#endif
#endif //WITH_DEVICE_LEDGER
}

View File

@ -28,6 +28,7 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#include <lokimq/hex.h>
#include "epee/misc_log_ex.h"
#include "log.hpp"
@ -36,20 +37,11 @@ namespace hw {
#undef LOKI_DEFAULT_LOG_CATEGORY
#define LOKI_DEFAULT_LOG_CATEGORY "device"
void buffer_to_str(char *to_buff, size_t to_len, const char *buff, size_t len) {
CHECK_AND_ASSERT_THROW_MES(to_len > (len*2), "destination buffer too short. At least" << (len*2+1) << " bytes required");
for (size_t i=0; i<len; i++) {
sprintf(to_buff+2*i, "%.02x", (unsigned char)buff[i]);
}
void log_hexbuffer(std::string_view msg, const void* buff, size_t len) {
MDEBUG(msg << ": " << lokimq::to_hex(std::string_view{reinterpret_cast<const char*>(buff), len}));
}
void log_hexbuffer(const std::string &msg, const char* buff, size_t len) {
char logstr[1025];
buffer_to_str(logstr, sizeof(logstr), buff, len);
MDEBUG(msg<< ": " << logstr);
}
void log_message(const std::string &msg, const std::string &info ) {
void log_message(std::string_view msg, std::string_view info) {
MDEBUG(msg << ": " << info);
}
@ -62,36 +54,22 @@ namespace hw {
#ifdef DEBUG_HWDEVICE
extern crypto::secret_key dbg_viewkey;
extern crypto::secret_key dbg_spendkey;
void decrypt(char* buf, size_t len) {
#if defined(IODUMMYCRYPT_HWDEVICE) || defined(IONOCRYPT_HWDEVICE)
size_t i;
if (len == 32) {
//view key?
for (i = 0; i<32; i++) {
if (buf[i] != 0) break;
}
if (i == 32) {
memmove(buf, hw::ledger::dbg_viewkey.data, 32);
return;
}
//spend key?
for (i = 0; i<32; i++) {
if (buf[i] != (char)0xff) break;
}
if (i == 32) {
memmove(buf, hw::ledger::dbg_spendkey.data, 32);
return;
}
if (len == 32 && memcmp(dummy_view_key, buf, 32) == 0) {
memmove(buf, hw::ledger::dbg_viewkey.data, 32);
return;
}
if (len == 32 && memcmp(dummy_spend_key, buf, 32) == 0) {
memmove(buf, hw::ledger::dbg_spendkey.data, 32);
return;
}
#if defined(IODUMMYCRYPT_HWDEVICE)
//std decrypt: XOR.55h
for (i = 0; i<len;i++) {
buf[i] ^= 0x55;
}
for (size_t i = 0; i < len; i++)
buf[i] ^= 0x55;
#endif
#endif
}
@ -128,43 +106,29 @@ namespace hw {
return x;
}
rct::keyV decrypt(const rct::keyV &keys) {
rct::keyV x ;
x.reserve(keys.size());
for (unsigned int j = 0; j<keys.size(); j++) {
x.push_back(decrypt(keys[j]));
}
return x;
}
static void check(const std::string &msg, const std::string &info, const char *h, const char *d, size_t len, bool crypted) {
char dd[32];
char logstr[128];
CHECK_AND_ASSERT_THROW_MES(len <= sizeof(dd), "invalid len");
memmove(dd,d,len);
if (crypted) {
CHECK_AND_ASSERT_THROW_MES(len<=32, "encrypted data greater than 32");
decrypt(dd,len);
CHECK_AND_ASSERT_THROW_MES(len <= 32, "encrypted data greater than 32");
decrypt(dd, len);
d = dd;
}
if (memcmp(h,dd,len)) {
log_message("ASSERT EQ FAIL", msg + ": "+ info );
if (memcmp(h, d, len)) {
log_message("ASSERT EQ FAIL", msg + ": " + info);
log_hexbuffer(" host ", h, len);
log_hexbuffer(" device", dd, len);
log_hexbuffer(" device", d, len);
} else {
buffer_to_str(logstr, 128, dd, len);
log_message("ASSERT EQ OK", msg + ": "+ info + ": "+ std::string(logstr) );
log_message("ASSERT EQ OK", msg + ": " + info + ": " + lokimq::to_hex(d, d+len));
}
}
void check32(const std::string &msg, const std::string &info, const char *h, const char *d, bool crypted) {
check(msg, info, h, d, 32, crypted);
void check32(const std::string &msg, const std::string &info, const void *h, const void *d, bool crypted) {
check(msg, info, reinterpret_cast<const char*>(h), reinterpret_cast<const char*>(d), 32, crypted);
}
void check8(const std::string &msg, const std::string &info, const char *h, const char *d, bool crypted) {
check(msg, info, h, d, 8, crypted);
void check8(const std::string &msg, const std::string &info, const void *h, const void *d, bool crypted) {
check(msg, info, reinterpret_cast<const char*>(h), reinterpret_cast<const char*>(d), 8, crypted);
}
#endif

View File

@ -53,28 +53,31 @@ namespace hw {
* - It assumes sensitive data encryption is off on device side.
*/
void buffer_to_str(char *to_buff, size_t to_len, const char *buff, size_t len) ;
void log_hexbuffer(const std::string &msg, const char* buff, size_t len);
void log_message(const std::string &msg, const std::string &info );
void log_hexbuffer(std::string_view msg, const void* buff, size_t len);
void log_message(std::string_view msg, std::string_view info );
#ifdef WITH_DEVICE_LEDGER
namespace ledger {
#ifdef DEBUG_HWDEVICE
#define TRACK printf("file %s:%d\n",__FILE__, __LINE__)
//#define TRACK MCDEBUG("ledger"," At file " << __FILE__ << ":" << __LINE__)
//#define TRACK while(0);
inline constexpr unsigned const char dummy_view_key[32] = {0};
inline constexpr unsigned const char dummy_spend_key[32] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
void decrypt(char* buf, size_t len) ;
#ifdef DEBUG_HWDEVICE
extern crypto::secret_key dbg_viewkey;
extern crypto::secret_key dbg_spendkey;
void decrypt(char* buf, size_t len);
crypto::key_derivation decrypt(const crypto::key_derivation &derivation) ;
cryptonote::account_keys decrypt(const cryptonote::account_keys& keys) ;
crypto::secret_key decrypt(const crypto::secret_key &sec) ;
rct::key decrypt(const rct::key &sec);
rct::key decrypt(const rct::key &sec);
crypto::ec_scalar decrypt(const crypto::ec_scalar &res);
rct::keyV decrypt(const rct::keyV &keys);
void check32(const std::string &msg, const std::string &info, const char *h, const char *d, bool crypted=false);
void check8(const std::string &msg, const std::string &info, const char *h, const char *d, bool crypted=false);
void check32(const std::string &msg, const std::string &info, const void *h, const void *d, bool crypted=false);
void check8(const std::string &msg, const std::string &info, const void *h, const void *d, bool crypted=false);
void set_check_verbose(bool verbose);
#endif

View File

@ -66,23 +66,21 @@ namespace trezor {
return false;
}
bool device_trezor_base::set_name(const std::string & name) {
this->m_full_name = name;
this->name = "";
bool device_trezor_base::set_name(std::string_view name) {
m_full_name = name;
auto delim = name.find(':');
if (delim != std::string::npos && delim + 1 < name.length()) {
if (auto delim = name.find(':'); delim != std::string::npos && delim + 1 < name.length())
this->name = name.substr(delim + 1);
}
else
this->name = "";
return true;
}
const std::string device_trezor_base::get_name() const {
if (this->m_full_name.empty()) {
return std::string("<disconnected:").append(this->name).append(">");
}
return this->m_full_name;
std::string device_trezor_base::get_name() const {
if (m_full_name.empty())
return "<disconnected:" + name + ">";
return m_full_name;
}
bool device_trezor_base::init() {
@ -124,7 +122,7 @@ namespace trezor {
for (auto &cur : trans) {
std::string cur_path = cur->get_path();
if (tools::starts_with(cur_path, this->name)) {
if (tools::starts_with(cur_path, name)) {
MDEBUG("Device Match: " << cur_path);
m_transport = cur;
break;
@ -132,7 +130,7 @@ namespace trezor {
}
if (!m_transport) {
MERROR("No matching Trezor device found. Device specifier: \"" + this->name + "\"");
MERROR("No matching Trezor device found. Device specifier: \"" << name << "\"");
return false;
}
@ -181,28 +179,28 @@ namespace trezor {
//lock the device for a long sequence
void device_trezor_base::lock() {
MTRACE("Ask for LOCKING for device " << this->name << " in thread ");
MTRACE("Ask for LOCKING for device " << name << " in thread ");
device_locker.lock();
MTRACE("Device " << this->name << " LOCKed");
MTRACE("Device " << name << " LOCKed");
}
//lock the device for a long sequence
bool device_trezor_base::try_lock() {
MTRACE("Ask for LOCKING(try) for device " << this->name << " in thread ");
MTRACE("Ask for LOCKING(try) for device " << name << " in thread ");
bool r = device_locker.try_lock();
if (r) {
MTRACE("Device " << this->name << " LOCKed(try)");
MTRACE("Device " << name << " LOCKed(try)");
} else {
MDEBUG("Device " << this->name << " not LOCKed(try)");
MDEBUG("Device " << name << " not LOCKed(try)");
}
return r;
}
//unlock the device
void device_trezor_base::unlock() {
MTRACE("Ask for UNLOCKING for device " << this->name << " in thread ");
MTRACE("Ask for UNLOCKING for device " << name << " in thread ");
device_locker.unlock();
MTRACE("Device " << this->name << " UNLOCKed");
MTRACE("Device " << name << " UNLOCKed");
}
/* ======================================================================= */
@ -238,7 +236,7 @@ namespace trezor {
auto pingMsg = std::make_shared<messages::management::Ping>();
pingMsg->set_message("PING");
auto success = this->client_exchange<messages::common::Success>(pingMsg); // messages::MessageType_Success
auto success = client_exchange<messages::common::Success>(pingMsg); // messages::MessageType_Success
MDEBUG("Ping response " << success->message());
(void)success;
}
@ -247,7 +245,7 @@ namespace trezor {
require_connected();
try {
this->call_ping_unsafe();
call_ping_unsafe();
} catch(exc::TrezorException const& e){
MINFO("Trezor does not respond: " << e.what());
@ -258,7 +256,7 @@ namespace trezor {
void device_trezor_base::write_raw(const google::protobuf::Message * msg){
require_connected();
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
this->get_transport()->write(*msg);
get_transport()->write(*msg);
}
GenericMessage device_trezor_base::read_raw(){
@ -266,7 +264,7 @@ namespace trezor {
std::shared_ptr<google::protobuf::Message> msg_resp;
hw::trezor::messages::MessageType msg_resp_type;
this->get_transport()->read(msg_resp, &msg_resp_type);
get_transport()->read(msg_resp, &msg_resp_type);
return GenericMessage(msg_resp_type, msg_resp);
}
@ -310,7 +308,7 @@ namespace trezor {
}
void device_trezor_base::set_derivation_path(const std::string &deriv_path){
this->m_wallet_deriv_path.clear();
m_wallet_deriv_path.clear();
if (deriv_path.empty() || deriv_path == "-"){
ensure_derivation_path();
@ -322,7 +320,7 @@ namespace trezor {
std::vector<std::string_view> fields = tools::split(deriv_path, "/");
CHECK_AND_ASSERT_THROW_MES(fields.size() <= 10, "Derivation path is too long");
this->m_wallet_deriv_path.reserve(fields.size());
m_wallet_deriv_path.reserve(fields.size());
for (auto& cur : fields) {
// Required pattern: [0-9]+'? but this is simple enough we can avoid using a regex
if (!cur.empty() && cur.back() == '\'') cur.remove_suffix(1);
@ -348,7 +346,7 @@ namespace trezor {
}
try {
this->call_ping_unsafe();
call_ping_unsafe();
return true;
} catch(std::exception const& e) {
@ -365,16 +363,16 @@ namespace trezor {
require_connected();
std::string tmp_session_id;
auto initMsg = std::make_shared<messages::management::Initialize>();
const auto data_cleaner = epee::misc_utils::create_scope_leave_handler([&]() {
LOKI_DEFER {
memwipe(&tmp_session_id[0], tmp_session_id.size());
});
};
if(!m_device_session_id.empty()) {
tmp_session_id.assign(m_device_session_id.data(), m_device_session_id.size());
initMsg->set_allocated_session_id(&tmp_session_id);
}
m_features = this->client_exchange<messages::management::Features>(initMsg);
m_features = client_exchange<messages::management::Features>(initMsg);
if (m_features->has_session_id()){
m_device_session_id = m_features->session_id();
} else {
@ -458,12 +456,12 @@ namespace trezor {
m.set_allocated_pin(&pin_field);
}
const auto data_cleaner = epee::misc_utils::create_scope_leave_handler([&]() {
LOKI_DEFER {
m.release_pin();
if (!pin_field.empty()){
memwipe(&pin_field[0], pin_field.size());
}
});
};
resp = call_raw(&m);
}
@ -515,12 +513,12 @@ namespace trezor {
}
}
const auto data_cleaner = epee::misc_utils::create_scope_leave_handler([&]() {
LOKI_DEFER {
m.release_passphrase();
if (!passphrase_field.empty()){
memwipe(&passphrase_field[0], passphrase_field.size());
}
});
};
resp = call_raw(&m);
}

View File

@ -267,9 +267,9 @@ namespace trezor {
/* ======================================================================= */
/* SETUP/TEARDOWN */
/* ======================================================================= */
bool set_name(const std::string &name) override;
bool set_name(std::string_view name) override;
const std::string get_name() const override;
std::string get_name() const override;
bool init() override;
bool release() override;
bool connect() override;

View File

@ -27,6 +27,7 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#include "ringct/rctTypes.h"
#include "version.h"
#include "protocol.hpp"
#include <unordered_map>
@ -389,7 +390,7 @@ namespace tx {
}
TData::TData() {
rsig_type = 0;
rsig_type = rct::RangeProofType::Borromean;
bp_version = 0;
cur_input_idx = 0;
cur_output_idx = 0;
@ -430,32 +431,32 @@ namespace tx {
}
}
static unsigned get_rsig_type(const rct::RCTConfig &rct_config, size_t num_outputs){
if (rct_config.range_proof_type == rct::RangeProofBorromean){
return rct::RangeProofBorromean;
static rct::RangeProofType get_rsig_type(const rct::RCTConfig &rct_config, size_t num_outputs){
if (rct_config.range_proof_type == rct::RangeProofType::Borromean){
return rct::RangeProofType::Borromean;
} else if (num_outputs > BULLETPROOF_MAX_OUTPUTS){
return rct::RangeProofMultiOutputBulletproof;
return rct::RangeProofType::MultiOutputBulletproof;
} else {
return rct::RangeProofPaddedBulletproof;
return rct::RangeProofType::PaddedBulletproof;
}
}
static void generate_rsig_batch_sizes(std::vector<uint64_t> &batches, unsigned rsig_type, size_t num_outputs){
static void generate_rsig_batch_sizes(std::vector<uint64_t> &batches, rct::RangeProofType rsig_type, size_t num_outputs){
size_t amount_batched = 0;
while(amount_batched < num_outputs){
if (rsig_type == rct::RangeProofBorromean || rsig_type == rct::RangeProofBulletproof) {
if (rsig_type == rct::RangeProofType::Borromean || rsig_type == rct::RangeProofType::Bulletproof) {
batches.push_back(1);
amount_batched += 1;
} else if (rsig_type == rct::RangeProofPaddedBulletproof){
} else if (rsig_type == rct::RangeProofType::PaddedBulletproof){
if (num_outputs > BULLETPROOF_MAX_OUTPUTS){
throw std::invalid_argument("BP padded can support only BULLETPROOF_MAX_OUTPUTS statements");
}
batches.push_back(num_outputs);
amount_batched += num_outputs;
} else if (rsig_type == rct::RangeProofMultiOutputBulletproof){
} else if (rsig_type == rct::RangeProofType::MultiOutputBulletproof){
size_t batch_size = 1;
while (batch_size * 2 + amount_batched <= num_outputs && batch_size * 2 <= BULLETPROOF_MAX_OUTPUTS){
batch_size *= 2;
@ -558,7 +559,7 @@ namespace tx {
tsx_data.set_num_inputs(static_cast<google::protobuf::uint32>(input_size));
tsx_data.set_mixin(static_cast<google::protobuf::uint32>(tx.sources[0].outputs.size() - 1));
tsx_data.set_account(tx.subaddr_account);
tsx_data.set_monero_version(std::string(LOKI_VERSION_STR) + "|" + LOKI_VERSION_TAG);
tsx_data.set_monero_version(std::string{LOKI_VERSION_STR} + "|" + std::string{LOKI_VERSION_TAG});
tsx_data.set_hard_fork(m_aux_data->hard_fork ? *m_aux_data->hard_fork : 0);
if (client_version() <= 1){
@ -568,8 +569,8 @@ namespace tx {
// Rsig decision
auto rsig_data = tsx_data.mutable_rsig_data();
m_ct.rsig_type = get_rsig_type(tx.rct_config, tx.splitted_dsts.size());
rsig_data->set_rsig_type(m_ct.rsig_type);
if (tx.rct_config.range_proof_type != rct::RangeProofBorromean){
rsig_data->set_rsig_type(static_cast<unsigned>(m_ct.rsig_type));
if (tx.rct_config.range_proof_type != rct::RangeProofType::Borromean){
m_ct.bp_version = (m_aux_data->bp_version ? *m_aux_data->bp_version : 1);
rsig_data->set_bp_version((uint32_t) m_ct.bp_version);
}
@ -858,7 +859,7 @@ namespace tx {
void Signer::step_all_outs_set_ack(std::shared_ptr<const messages::monero::MoneroTransactionAllOutSetAck> ack, hw::device &hwdev){
m_ct.rv = std::make_shared<rct::rctSig>();
m_ct.rv->txnFee = ack->rv().txn_fee();
m_ct.rv->type = static_cast<uint8_t>(ack->rv().rv_type());
m_ct.rv->type = static_cast<rct::RCTType>(static_cast<uint8_t>(ack->rv().rv_type()));
string_to_key(m_ct.rv->message, ack->rv().message());
// Extra copy
@ -912,7 +913,7 @@ namespace tx {
}
}
rct::key hash_computed = rct::get_pre_mlsag_hash(*(m_ct.rv), hwdev);
rct::key hash_computed = rct::get_pre_clsag_hash(*(m_ct.rv), hwdev);
auto & hash = ack->full_message_hash();
if (hash.size() != 32){
@ -1014,7 +1015,7 @@ namespace tx {
}
}
if (m_ct.rv->type == rct::RCTTypeCLSAG){
if (m_ct.rv->type == rct::RCTType::CLSAG){
m_ct.rv->p.CLSAGs.reserve(m_ct.signatures.size());
for (size_t i = 0; i < m_ct.signatures.size(); ++i) {
rct::clsag clsag;

View File

@ -30,6 +30,7 @@
#ifndef MONERO_PROTOCOL_H
#define MONERO_PROTOCOL_H
#include "ringct/rctTypes.h"
#include "trezor_defs.hpp"
#include "device/device_cold.hpp"
#include "messages_map.hpp"
@ -170,7 +171,7 @@ namespace tx {
TsxData tsx_data;
wallet::tx_construction_data tx_data;
cryptonote::transaction tx;
unsigned rsig_type;
rct::RangeProofType rsig_type;
int bp_version;
std::vector<uint64_t> grouping_vct;
std::shared_ptr<MoneroRsigData> rsig_param;
@ -289,11 +290,11 @@ namespace tx {
throw std::invalid_argument("RV not initialized");
}
auto tp = m_ct.rv->type;
return tp == rct::RCTTypeSimple;
return tp == rct::RCTType::Simple;
}
bool is_req_bulletproof() const {
return m_ct.tx_data.rct_config.range_proof_type != rct::RangeProofBorromean;
return m_ct.tx_data.rct_config.range_proof_type != rct::RangeProofType::Borromean;
}
bool is_bulletproof() const {

View File

@ -82,6 +82,7 @@ namespace cryptonote
{
rct::key sk = rct::scalarmultKey(rct::pk2rct(k), rct::sk2rct(blinded_skey));
crypto::secret_key msk = get_multisig_blinded_secret_key(rct::rct2sk(sk));
memwipe(&sk, sizeof(sk));
multisig_keys.push_back(msk);
sc_add(spend_skey.bytes, spend_skey.bytes, (const unsigned char*)msk.data);
}
@ -126,10 +127,10 @@ namespace cryptonote
//-----------------------------------------------------------------
crypto::secret_key generate_multisig_view_secret_key(const crypto::secret_key &skey, const std::vector<crypto::secret_key> &skeys)
{
rct::key view_skey = rct::sk2rct(get_multisig_blinded_secret_key(skey));
crypto::secret_key view_skey = get_multisig_blinded_secret_key(skey);
for (const auto &k: skeys)
sc_add(view_skey.bytes, view_skey.bytes, rct::sk2rct(k).bytes);
return rct::rct2sk(view_skey);
sc_add(reinterpret_cast<unsigned char*>(view_skey.data), rct::sk2rct(view_skey).bytes, rct::sk2rct(k).bytes);
return view_skey;
}
//-----------------------------------------------------------------
crypto::public_key generate_multisig_M_N_spend_public_key(const std::vector<crypto::public_key> &pkeys)

View File

@ -251,7 +251,7 @@ namespace rct {
//generates a random scalar which can be used as a secret key or mask
void skGen(key &sk) {
random32_unbiased(sk.bytes);
random_scalar(sk.bytes);
}
//generates a random scalar which can be used as a secret key or mask

View File

@ -104,37 +104,6 @@ namespace rct {
}
//Borromean (c.f. gmax/andytoshi's paper)
boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices) {
key64 L[2], alpha;
key c;
int naught = 0, prime = 0, ii = 0, jj=0;
boroSig bb;
for (ii = 0 ; ii < 64 ; ii++) {
naught = indices[ii]; prime = (indices[ii] + 1) % 2;
skGen(alpha[ii]);
scalarmultBase(L[naught][ii], alpha[ii]);
if (naught == 0) {
skGen(bb.s1[ii]);
c = hash_to_scalar(L[naught][ii]);
addKeys2(L[prime][ii], bb.s1[ii], c, P2[ii]);
}
}
bb.ee = hash_to_scalar(L[1]); //or L[1]..
key LL, cc;
for (jj = 0 ; jj < 64 ; jj++) {
if (!indices[jj]) {
sc_mulsub(bb.s0[jj].bytes, x[jj].bytes, bb.ee.bytes, alpha[jj].bytes);
} else {
skGen(bb.s0[jj]);
addKeys2(LL, bb.s0[jj], bb.ee, P1[jj]); //different L0
cc = hash_to_scalar(LL);
sc_mulsub(bb.s1[jj].bytes, x[jj].bytes, cc.bytes, alpha[jj].bytes);
}
}
return bb;
}
//see above.
bool verifyBorromean(const boroSig &bb, const ge_p3 P1[64], const ge_p3 P2[64]) {
key64 Lv1; key chash, LL;
int ii = 0;
@ -322,103 +291,6 @@ namespace rct {
return CLSAG_Gen(message, P, p, C, z, C_nonzero, C_offset, l, NULL, NULL, NULL, hw::get_device("default"));
}
// MLSAG signatures
// See paper by Noether (https://eprint.iacr.org/2015/1098)
// This generalization allows for some dimensions not to require linkability;
// this is used in practice for commitment data within signatures
// Note that using more than one linkable dimension is not recommended.
mgSig MLSAG_Gen(const key &message, const keyM & pk, const keyV & xx, const multisig_kLRki *kLRki, key *mscout, const unsigned int index, size_t dsRows, hw::device &hwdev) {
mgSig rv;
size_t cols = pk.size();
CHECK_AND_ASSERT_THROW_MES(cols >= 2, "Error! What is c if cols = 1!");
CHECK_AND_ASSERT_THROW_MES(index < cols, "Index out of range");
size_t rows = pk[0].size();
CHECK_AND_ASSERT_THROW_MES(rows >= 1, "Empty pk");
for (size_t i = 1; i < cols; ++i) {
CHECK_AND_ASSERT_THROW_MES(pk[i].size() == rows, "pk is not rectangular");
}
CHECK_AND_ASSERT_THROW_MES(xx.size() == rows, "Bad xx size");
CHECK_AND_ASSERT_THROW_MES(dsRows <= rows, "Bad dsRows size");
CHECK_AND_ASSERT_THROW_MES((kLRki && mscout) || (!kLRki && !mscout), "Only one of kLRki/mscout is present");
CHECK_AND_ASSERT_THROW_MES(!kLRki || dsRows == 1, "Multisig requires exactly 1 dsRows");
size_t i = 0, j = 0, ii = 0;
key c, c_old, L, R, Hi;
ge_p3 Hi_p3;
sc_0(c_old.bytes);
std::vector<geDsmp> Ip(dsRows);
rv.II = keyV(dsRows);
keyV alpha(rows);
keyV aG(rows);
rv.ss = keyM(cols, aG);
keyV aHP(dsRows);
keyV toHash(1 + 3 * dsRows + 2 * (rows - dsRows));
toHash[0] = message;
DP("here1");
for (i = 0; i < dsRows; i++) {
toHash[3 * i + 1] = pk[index][i];
if (kLRki) {
// multisig
alpha[i] = kLRki->k;
toHash[3 * i + 2] = kLRki->L;
toHash[3 * i + 3] = kLRki->R;
rv.II[i] = kLRki->ki;
}
else {
hash_to_p3(Hi_p3, pk[index][i]);
ge_p3_tobytes(Hi.bytes, &Hi_p3);
hwdev.mlsag_prepare(Hi, xx[i], alpha[i] , aG[i] , aHP[i] , rv.II[i]);
toHash[3 * i + 2] = aG[i];
toHash[3 * i + 3] = aHP[i];
}
precomp(Ip[i].k, rv.II[i]);
}
size_t ndsRows = 3 * dsRows; //non Double Spendable Rows (see identity chains paper)
for (i = dsRows, ii = 0 ; i < rows ; i++, ii++) {
skpkGen(alpha[i], aG[i]); //need to save alphas for later..
toHash[ndsRows + 2 * ii + 1] = pk[index][i];
toHash[ndsRows + 2 * ii + 2] = aG[i];
}
hwdev.mlsag_hash(toHash, c_old);
i = (index + 1) % cols;
if (i == 0) {
copy(rv.cc, c_old);
}
while (i != index) {
rv.ss[i] = skvGen(rows);
sc_0(c.bytes);
for (j = 0; j < dsRows; j++) {
addKeys2(L, rv.ss[i][j], c_old, pk[i][j]);
hash_to_p3(Hi_p3, pk[i][j]);
ge_p3_tobytes(Hi.bytes, &Hi_p3);
addKeys3(R, rv.ss[i][j], Hi, c_old, Ip[j].k);
toHash[3 * j + 1] = pk[i][j];
toHash[3 * j + 2] = L;
toHash[3 * j + 3] = R;
}
for (j = dsRows, ii = 0; j < rows; j++, ii++) {
addKeys2(L, rv.ss[i][j], c_old, pk[i][j]);
toHash[ndsRows + 2 * ii + 1] = pk[i][j];
toHash[ndsRows + 2 * ii + 2] = L;
}
hwdev.mlsag_hash(toHash, c);
copy(c_old, c);
i = (i + 1) % cols;
if (i == 0) {
copy(rv.cc, c_old);
}
}
hwdev.mlsag_sign(c, xx, alpha, rows, dsRows, rv.ss[index]);
if (mscout)
*mscout = c;
return rv;
}
// MLSAG signatures
// See paper by Noether (https://eprint.iacr.org/2015/1098)
// This generalization allows for some dimensions not to require linkability;
@ -490,38 +362,6 @@ namespace rct {
//proveRange and verRange
//proveRange gives C, and mask such that \sumCi = C
// c.f. https://eprint.iacr.org/2015/1098 section 5.1
// and Ci is a commitment to either 0 or 2^i, i=0,...,63
// thus this proves that "amount" is in [0, 2^64]
// mask is a such that C = aG + bH, and b = amount
//verRange verifies that \sum Ci = C and that each Ci is a commitment to 0 or 2^i
rangeSig proveRange(key & C, key & mask, const xmr_amount & amount) {
sc_0(mask.bytes);
identity(C);
bits b;
d2b(b, amount);
rangeSig sig;
key64 ai;
key64 CiH;
int i = 0;
for (i = 0; i < ATOMS; i++) {
skGen(ai[i]);
if (b[i] == 0) {
scalarmultBase(sig.Ci[i], ai[i]);
}
if (b[i] == 1) {
addKeys1(sig.Ci[i], ai[i], H2[i]);
}
subKeys(CiH[i], sig.Ci[i], H2[i]);
sc_add(mask.bytes, mask.bytes, ai[i].bytes);
addKeys(C, C, sig.Ci[i]);
}
sig.asig = genBorromean(ai, sig.Ci, CiH, b);
return sig;
}
//proveRange and verRange
//proveRange gives C, and mask such that \sumCi = C
// c.f. https://eprint.iacr.org/2015/1098 section 5.1
@ -564,7 +404,7 @@ namespace rct {
catch (...) { return false; }
}
key get_pre_mlsag_hash(const rctSig &rv, hw::device &hwdev)
key get_pre_clsag_hash(const rctSig &rv, hw::device &hwdev)
{
keyV hashes;
hashes.reserve(3);
@ -622,97 +462,10 @@ namespace rct {
}
}
hashes.push_back(cn_fast_hash(kv));
hwdev.mlsag_prehash(blob, inputs, outputs, hashes, rv.outPk, prehash);
hwdev.clsag_prehash(blob, inputs, outputs, hashes, rv.outPk, prehash);
return prehash;
}
//Ring-ct MG sigs
//Prove:
// c.f. https://eprint.iacr.org/2015/1098 section 4. definition 10.
// This does the MG sig on the "dest" part of the given key matrix, and
// the last row is the sum of input commitments from that column - sum output commitments
// this shows that sum inputs = sum outputs
//Ver:
// verifies the above sig is created corretly
mgSig proveRctMG(const key &message, const ctkeyM & pubs, const ctkeyV & inSk, const ctkeyV &outSk, const ctkeyV & outPk, const multisig_kLRki *kLRki, key *mscout, unsigned int index, const key &txnFeeKey, hw::device &hwdev) {
//setup vars
size_t cols = pubs.size();
CHECK_AND_ASSERT_THROW_MES(cols >= 1, "Empty pubs");
size_t rows = pubs[0].size();
CHECK_AND_ASSERT_THROW_MES(rows >= 1, "Empty pubs");
for (size_t i = 1; i < cols; ++i) {
CHECK_AND_ASSERT_THROW_MES(pubs[i].size() == rows, "pubs is not rectangular");
}
CHECK_AND_ASSERT_THROW_MES(inSk.size() == rows, "Bad inSk size");
CHECK_AND_ASSERT_THROW_MES(outSk.size() == outPk.size(), "Bad outSk/outPk size");
CHECK_AND_ASSERT_THROW_MES((kLRki && mscout) || (!kLRki && !mscout), "Only one of kLRki/mscout is present");
keyV sk(rows + 1);
keyV tmp(rows + 1);
size_t i = 0, j = 0;
for (i = 0; i < rows + 1; i++) {
sc_0(sk[i].bytes);
identity(tmp[i]);
}
keyM M(cols, tmp);
//create the matrix to mg sig
for (i = 0; i < cols; i++) {
M[i][rows] = identity();
for (j = 0; j < rows; j++) {
M[i][j] = pubs[i][j].dest;
addKeys(M[i][rows], M[i][rows], pubs[i][j].mask); //add input commitments in last row
}
}
sc_0(sk[rows].bytes);
for (j = 0; j < rows; j++) {
sk[j] = copy(inSk[j].dest);
sc_add(sk[rows].bytes, sk[rows].bytes, inSk[j].mask.bytes); //add masks in last row
}
for (i = 0; i < cols; i++) {
for (size_t j = 0; j < outPk.size(); j++) {
subKeys(M[i][rows], M[i][rows], outPk[j].mask); //subtract output Ci's in last row
}
//subtract txn fee output in last row
subKeys(M[i][rows], M[i][rows], txnFeeKey);
}
for (size_t j = 0; j < outPk.size(); j++) {
sc_sub(sk[rows].bytes, sk[rows].bytes, outSk[j].mask.bytes); //subtract output masks in last row..
}
mgSig result = MLSAG_Gen(message, M, sk, kLRki, mscout, index, rows, hwdev);
memwipe(sk.data(), sk.size() * sizeof(key));
return result;
}
//Ring-ct MG sigs Simple
// Simple version for when we assume only
// post rct inputs
// here pubs is a vector of (P, C) length mixin
// inSk is x, a_in corresponding to signing index
// a_out, Cout is for the output commitment
// index is the signing index..
mgSig proveRctMGSimple(const key &message, const ctkeyV & pubs, const ctkey & inSk, const key &a , const key &Cout, const multisig_kLRki *kLRki, key *mscout, unsigned int index, hw::device &hwdev) {
//setup vars
size_t rows = 1;
size_t cols = pubs.size();
CHECK_AND_ASSERT_THROW_MES(cols >= 1, "Empty pubs");
CHECK_AND_ASSERT_THROW_MES((kLRki && mscout) || (!kLRki && !mscout), "Only one of kLRki/mscout is present");
keyV tmp(rows + 1);
keyV sk(rows + 1);
size_t i;
keyM M(cols, tmp);
sk[0] = copy(inSk.dest);
sc_sub(sk[1].bytes, inSk.mask.bytes, a.bytes);
for (i = 0; i < cols; i++) {
M[i][0] = pubs[i].dest;
subKeys(M[i][1], pubs[i].mask, Cout);
}
mgSig result = MLSAG_Gen(message, M, sk, kLRki, mscout, index, rows, hwdev);
memwipe(&sk[0], sizeof(key));
return result;
}
clsag proveRctCLSAGSimple(const key &message, const ctkeyV &pubs, const ctkey &inSk, const key &a, const key &Cout, const multisig_kLRki *kLRki, key *mscout, key *mspout, unsigned int index, hw::device &hwdev) {
//setup vars
size_t rows = 1;
@ -986,82 +739,9 @@ namespace rct {
return index;
}
//RingCT protocol
//genRct:
// creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the
// columns that are claimed as inputs, and that the sum of inputs = sum of outputs.
// Also contains masked "amount" and "mask" so the receiver can see how much they received
//verRct:
// verifies that all signatures (rangeProogs, MG sig, sum inputs = outputs) are correct
//decodeRct: (c.f. https://eprint.iacr.org/2015/1098 section 5.1.1)
// uses the attached ecdh info to find the amounts represented by each output commitment
// must know the destination private key to find the correct amount, else will return a random number
// Note: For txn fees, the last index in the amounts vector should contain that
// Thus the amounts vector will be "one" longer than the destinations vectort
rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev) {
CHECK_AND_ASSERT_THROW_MES(amounts.size() == destinations.size() || amounts.size() == destinations.size() + 1, "Different number of amounts/destinations");
CHECK_AND_ASSERT_THROW_MES(amount_keys.size() == destinations.size(), "Different number of amount_keys/destinations");
CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing");
for (size_t n = 0; n < mixRing.size(); ++n) {
CHECK_AND_ASSERT_THROW_MES(mixRing[n].size() == inSk.size(), "Bad mixRing size");
}
CHECK_AND_ASSERT_THROW_MES((kLRki && msout) || (!kLRki && !msout), "Only one of kLRki/msout is present");
CHECK_AND_ASSERT_THROW_MES(inSk.size() < 2, "genRct is not suitable for 2+ rings");
rctSig rv;
rv.type = RCTTypeFull;
rv.message = message;
rv.outPk.resize(destinations.size());
rv.p.rangeSigs.resize(destinations.size());
rv.ecdhInfo.resize(destinations.size());
size_t i = 0;
keyV masks(destinations.size()); //sk mask..
outSk.resize(destinations.size());
for (i = 0; i < destinations.size(); i++) {
//add destination to sig
rv.outPk[i].dest = copy(destinations[i]);
//compute range proof
rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]);
#ifdef DBG
CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof");
#endif
//mask amount and mask
rv.ecdhInfo[i].mask = copy(outSk[i].mask);
rv.ecdhInfo[i].amount = d2h(amounts[i]);
hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG);
}
//set txn fee
if (amounts.size() > destinations.size())
{
rv.txnFee = amounts[destinations.size()];
}
else
{
rv.txnFee = 0;
}
key txnFeeKey = scalarmultH(d2h(rv.txnFee));
rv.mixRing = mixRing;
if (msout)
msout->c.resize(1);
rv.p.MGs.push_back(proveRctMG(get_pre_mlsag_hash(rv, hwdev), rv.mixRing, inSk, outSk, rv.outPk, kLRki, msout ? &msout->c[0] : NULL, index, txnFeeKey,hwdev));
return rv;
}
rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, const int mixin, const RCTConfig &rct_config, hw::device &hwdev) {
unsigned int index;
ctkeyM mixRing;
ctkeyV outSk;
tie(mixRing, index) = populateFromBlockchain(inPk, mixin);
return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, kLRki, msout, index, outSk, rct_config, hwdev);
}
//RCT simple
//for post-rct only
rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> &inamounts, const std::vector<xmr_amount> &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, const std::vector<unsigned int> & index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev) {
const bool bulletproof = rct_config.range_proof_type != RangeProofBorromean;
CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts");
CHECK_AND_ASSERT_THROW_MES(inamounts.size() == inSk.size(), "Different number of inamounts/inSk");
CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations");
@ -1076,107 +756,79 @@ namespace rct {
CHECK_AND_ASSERT_THROW_MES(kLRki->size() == inamounts.size(), "Mismatched kLRki/inamounts sizes");
}
rctSig rv;
if (bulletproof)
{
switch (rct_config.bp_version)
{
case 0:
case 3:
rv.type = RCTTypeCLSAG;
break;
case 2:
rv.type = RCTTypeBulletproof2;
break;
case 1:
rv.type = RCTTypeBulletproof;
break;
default:
ASSERT_MES_AND_THROW("Unsupported BP version: " << rct_config.bp_version);
}
}
else
rv.type = RCTTypeSimple;
CHECK_AND_ASSERT_THROW_MES(
rct_config.range_proof_type != RangeProofType::Borromean &&
(rct_config.bp_version == 3 || rct_config.bp_version == 0), // 0 means latest version
"Unable to generate rct for non-CLSAG-bulletproof");
rctSig rv;
rv.type = RCTType::CLSAG;
rv.message = message;
rv.outPk.resize(destinations.size());
if (!bulletproof)
rv.p.rangeSigs.resize(destinations.size());
rv.ecdhInfo.resize(destinations.size());
size_t i;
keyV masks(destinations.size()); //sk mask..
outSk.resize(destinations.size());
for (i = 0; i < destinations.size(); i++) {
//add destination to sig
rv.outPk[i].dest = copy(destinations[i]);
//compute range proof
if (!bulletproof)
rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, outamounts[i]);
#ifdef DBG
if (!bulletproof)
CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof");
#endif
}
rv.p.bulletproofs.clear();
if (bulletproof)
size_t n_amounts = outamounts.size();
size_t amounts_proved = 0;
if (rct_config.range_proof_type == RangeProofType::PaddedBulletproof)
{
size_t n_amounts = outamounts.size();
size_t amounts_proved = 0;
if (rct_config.range_proof_type == RangeProofPaddedBulletproof)
rct::keyV C, masks;
if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE)
{
rct::keyV C, masks;
if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE)
{
// use a fake bulletproof for speed
rv.p.bulletproofs.push_back(make_dummy_bulletproof(outamounts, C, masks));
}
else
{
const epee::span<const key> keys{&amount_keys[0], amount_keys.size()};
rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts, keys, hwdev));
#ifdef DBG
CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
#endif
}
for (i = 0; i < outamounts.size(); ++i)
{
rv.outPk[i].mask = rct::scalarmult8(C[i]);
outSk[i].mask = masks[i];
}
// use a fake bulletproof for speed
rv.p.bulletproofs.push_back(make_dummy_bulletproof(outamounts, C, masks));
}
else while (amounts_proved < n_amounts)
else
{
size_t batch_size = 1;
if (rct_config.range_proof_type == RangeProofMultiOutputBulletproof)
while (batch_size * 2 + amounts_proved <= n_amounts && batch_size * 2 <= BULLETPROOF_MAX_OUTPUTS)
batch_size *= 2;
rct::keyV C, masks;
std::vector<uint64_t> batch_amounts(batch_size);
for (i = 0; i < batch_size; ++i)
batch_amounts[i] = outamounts[i + amounts_proved];
if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE)
{
// use a fake bulletproof for speed
rv.p.bulletproofs.push_back(make_dummy_bulletproof(batch_amounts, C, masks));
}
else
{
const epee::span<const key> keys{&amount_keys[amounts_proved], batch_size};
rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts, keys, hwdev));
const epee::span<const key> keys{&amount_keys[0], amount_keys.size()};
rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts, keys, hwdev));
#ifdef DBG
CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
#endif
}
for (i = 0; i < batch_size; ++i)
{
rv.outPk[i + amounts_proved].mask = rct::scalarmult8(C[i]);
outSk[i + amounts_proved].mask = masks[i];
}
amounts_proved += batch_size;
}
for (i = 0; i < outamounts.size(); ++i)
{
rv.outPk[i].mask = rct::scalarmult8(C[i]);
outSk[i].mask = masks[i];
}
}
else while (amounts_proved < n_amounts)
{
size_t batch_size = 1;
if (rct_config.range_proof_type == RangeProofType::MultiOutputBulletproof)
while (batch_size * 2 + amounts_proved <= n_amounts && batch_size * 2 <= BULLETPROOF_MAX_OUTPUTS)
batch_size *= 2;
rct::keyV C, masks;
std::vector<uint64_t> batch_amounts(batch_size);
for (i = 0; i < batch_size; ++i)
batch_amounts[i] = outamounts[i + amounts_proved];
if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE)
{
// use a fake bulletproof for speed
rv.p.bulletproofs.push_back(make_dummy_bulletproof(batch_amounts, C, masks));
}
else
{
const epee::span<const key> keys{&amount_keys[amounts_proved], batch_size};
rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts, keys, hwdev));
#ifdef DBG
CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
#endif
}
for (i = 0; i < batch_size; ++i)
{
rv.outPk[i + amounts_proved].mask = rct::scalarmult8(C[i]);
outSk[i + amounts_proved].mask = masks[i];
}
amounts_proved += batch_size;
}
key sumout = zero();
@ -1187,20 +839,14 @@ namespace rct {
//mask amount and mask
rv.ecdhInfo[i].mask = copy(outSk[i].mask);
rv.ecdhInfo[i].amount = d2h(outamounts[i]);
hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG);
hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], true);
}
//set txn fee
rv.txnFee = txnFee;
// TODO: unused ??
// key txnFeeKey = scalarmultH(d2h(rv.txnFee));
rv.mixRing = mixRing;
keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts;
keyV &pseudoOuts = rv.p.pseudoOuts;
pseudoOuts.resize(inamounts.size());
if (rv.type == RCTTypeCLSAG)
rv.p.CLSAGs.resize(inamounts.size());
else
rv.p.MGs.resize(inamounts.size());
rv.p.CLSAGs.resize(inamounts.size());
key sumpouts = zero(); //sum pseudoOut masks
keyV a(inamounts.size());
for (i = 0 ; i < inamounts.size() - 1; i++) {
@ -1212,22 +858,15 @@ namespace rct {
genC(pseudoOuts[i], a[i], inamounts[i]);
DP(pseudoOuts[i]);
key full_message = get_pre_mlsag_hash(rv,hwdev);
key full_message = get_pre_clsag_hash(rv, hwdev);
if (msout)
{
msout->c.resize(inamounts.size());
msout->mu_p.resize(rv.type == RCTTypeCLSAG ? inamounts.size() : 0);
msout->mu_p.resize(inamounts.size());
}
for (i = 0 ; i < inamounts.size(); i++)
{
if (rv.type == RCTTypeCLSAG)
{
rv.p.CLSAGs[i] = proveRctCLSAGSimple(full_message, rv.mixRing[i], inSk[i], a[i], pseudoOuts[i], kLRki ? &(*kLRki)[i]: NULL, msout ? &msout->c[i] : NULL, msout ? &msout->mu_p[i] : NULL, index[i], hwdev);
}
else
{
rv.p.MGs[i] = proveRctMGSimple(full_message, rv.mixRing[i], inSk[i], a[i], pseudoOuts[i], kLRki ? &(*kLRki)[i]: NULL, msout ? &msout->c[i] : NULL, index[i], hwdev);
}
rv.p.CLSAGs[i] = proveRctCLSAGSimple(full_message, rv.mixRing[i], inSk[i], a[i], pseudoOuts[i], kLRki ? &(*kLRki)[i]: NULL, msout ? &msout->c[i] : NULL, msout ? &msout->mu_p[i] : NULL, index[i], hwdev);
}
return rv;
}
@ -1257,7 +896,7 @@ namespace rct {
// must know the destination private key to find the correct amount, else will return a random number
bool verRct(const rctSig & rv, bool semantics) {
PERF_TIMER(verRct);
CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "verRct called on non-full rctSig");
CHECK_AND_ASSERT_MES(rv.type == RCTType::Full, false, "verRct called on non-full rctSig");
if (semantics)
{
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs");
@ -1292,7 +931,7 @@ namespace rct {
if (!semantics) {
//compute txn fee
key txnFeeKey = scalarmultH(d2h(rv.txnFee));
bool mgVerd = verRctMG(rv.p.MGs[0], rv.mixRing, rv.outPk, txnFeeKey, get_pre_mlsag_hash(rv, hw::get_device("default")));
bool mgVerd = verRctMG(rv.p.MGs[0], rv.mixRing, rv.outPk, txnFeeKey, get_pre_clsag_hash(rv, hw::get_device("default")));
DP("mg sig verified?");
DP(mgVerd);
if (!mgVerd) {
@ -1337,7 +976,7 @@ namespace rct {
if (bulletproof)
{
CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_amounts(rv.p.bulletproofs), false, "Mismatched sizes of outPk and bulletproofs");
if (rv.type == RCTTypeCLSAG)
if (rv.type == RCTType::CLSAG)
{
CHECK_AND_ASSERT_MES(rv.p.MGs.empty(), false, "MGs are not empty for CLSAG");
CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.p.CLSAGs.size(), false, "Mismatched sizes of rv.p.pseudoOuts and rv.p.CLSAGs");
@ -1456,16 +1095,14 @@ namespace rct {
const keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts;
const key message = get_pre_mlsag_hash(rv, hw::get_device("default"));
const key message = get_pre_clsag_hash(rv, hw::get_device("default"));
results.clear();
results.resize(rv.mixRing.size());
for (size_t i = 0 ; i < rv.mixRing.size() ; i++) {
tpool.submit(&waiter, [&, i] {
if (rv.type == RCTTypeCLSAG)
{
if (rv.type == RCTType::CLSAG)
results[i] = verRctCLSAGSimple(message, rv.p.CLSAGs[i], rv.mixRing[i], pseudoOuts[i]);
}
else
results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], pseudoOuts[i]);
});
@ -1505,13 +1142,13 @@ namespace rct {
// uses the attached ecdh info to find the amounts represented by each output commitment
// must know the destination private key to find the correct amount, else will return a random number
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev) {
CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "decodeRct called on non-full rctSig");
CHECK_AND_ASSERT_MES(rv.type == RCTType::Full, false, "decodeRct called on non-full rctSig");
CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index");
CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo");
//mask amount and mask
ecdhTuple ecdh_info = rv.ecdhInfo[i];
hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG);
hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTType::Bulletproof2 || rv.type == RCTType::CLSAG);
mask = ecdh_info.mask;
key amount = ecdh_info.amount;
key C = rv.outPk[i].mask;
@ -1541,7 +1178,7 @@ namespace rct {
//mask amount and mask
ecdhTuple ecdh_info = rv.ecdhInfo[i];
hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG);
hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTType::Bulletproof2 || rv.type == RCTType::CLSAG);
mask = ecdh_info.mask;
key amount = ecdh_info.amount;
key C = rv.outPk[i].mask;
@ -1565,13 +1202,13 @@ namespace rct {
}
bool signMultisigMLSAG(rctSig &rv, const std::vector<unsigned int> &indices, const keyV &k, const multisig_out &msout, const key &secret_key) {
CHECK_AND_ASSERT_MES(tools::equals_any(rv.type, RCTTypeFull, RCTTypeSimple, RCTTypeBulletproof, RCTTypeBulletproof2),
CHECK_AND_ASSERT_MES(tools::equals_any(rv.type, RCTType::Full, RCTType::Simple, RCTType::Bulletproof, RCTType::Bulletproof2),
false, "unsupported rct type");
CHECK_AND_ASSERT_MES(indices.size() == k.size(), false, "Mismatched k/indices sizes");
CHECK_AND_ASSERT_MES(k.size() == rv.p.MGs.size(), false, "Mismatched k/MGs size");
CHECK_AND_ASSERT_MES(k.size() == msout.c.size(), false, "Mismatched k/msout.c size");
CHECK_AND_ASSERT_MES(rv.p.CLSAGs.empty(), false, "CLSAGs not empty for MLSAGs");
if (rv.type == RCTTypeFull)
if (rv.type == RCTType::Full)
{
CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "MGs not a single element");
}
@ -1591,7 +1228,7 @@ namespace rct {
}
bool signMultisigCLSAG(rctSig &rv, const std::vector<unsigned int> &indices, const keyV &k, const multisig_out &msout, const key &secret_key) {
CHECK_AND_ASSERT_MES(rv.type == RCTTypeCLSAG, false, "unsupported rct type");
CHECK_AND_ASSERT_MES(rv.type == RCTType::CLSAG, false, "unsupported rct type");
CHECK_AND_ASSERT_MES(indices.size() == k.size(), false, "Mismatched k/indices sizes");
CHECK_AND_ASSERT_MES(k.size() == rv.p.CLSAGs.size(), false, "Mismatched k/CLSAGs size");
CHECK_AND_ASSERT_MES(k.size() == msout.c.size(), false, "Mismatched k/msout.c size");
@ -1613,7 +1250,7 @@ namespace rct {
}
bool signMultisig(rctSig &rv, const std::vector<unsigned int> &indices, const keyV &k, const multisig_out &msout, const key &secret_key) {
if (rv.type == RCTTypeCLSAG)
if (rv.type == RCTType::CLSAG)
return signMultisigCLSAG(rv, indices, k, msout, secret_key);
else
return signMultisigMLSAG(rv, indices, k, msout, secret_key);

View File

@ -62,7 +62,6 @@ namespace hw {
namespace rct {
boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices);
bool verifyBorromean(const boroSig &bb, const key64 P1, const key64 P2);
//Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures)
@ -71,7 +70,6 @@ namespace rct {
// Gen creates a signature which proves that for some column in the keymatrix "pk"
// the signer knows a secret key for each row in that column
// Ver verifies that the MG sig was created correctly
mgSig MLSAG_Gen(const key &message, const keyM & pk, const keyV & xx, const multisig_kLRki *kLRki, key *mscout, const unsigned int index, size_t dsRows, hw::device &hwdev);
bool MLSAG_Ver(const key &message, const keyM &pk, const mgSig &sig, size_t dsRows);
clsag CLSAG_Gen(const key &message, const keyV & P, const key & p, const keyV & C, const key & z, const keyV & C_nonzero, const key & C_offset, const unsigned int l, const multisig_kLRki *kLRki, key *mscout, key *mspout, hw::device &hwdev);
@ -86,7 +84,6 @@ namespace rct {
// thus this proves that "amount" is in [0, 2^64]
// mask is a such that C = aG + bH, and b = amount
//verRange verifies that \sum Ci = C and that each Ci is a commitment to 0 or 2^i
rangeSig proveRange(key & C, key & mask, const xmr_amount & amount);
bool verRange(const key & C, const rangeSig & as);
//Ring-ct MG sigs
@ -97,8 +94,6 @@ namespace rct {
// this shows that sum inputs = sum outputs
//Ver:
// verifies the above sig is created corretly
mgSig proveRctMG(const ctkeyM & pubs, const ctkeyV & inSk, const keyV &outMasks, const ctkeyV & outPk, const multisig_kLRki *kLRki, key *mscout, unsigned int index, const key &txnFee, const key &message, hw::device &hwdev);
mgSig proveRctMGSimple(const key & message, const ctkeyV & pubs, const ctkey & inSk, const key &a , const key &Cout, const multisig_kLRki *kLRki, key *mscout, unsigned int index, hw::device &hwdev);
bool verRctMG(const mgSig &mg, const ctkeyM & pubs, const ctkeyV & outPk, const key &txnFee, const key &message);
bool verRctMGSimple(const key &message, const mgSig &mg, const ctkeyV & pubs, const key & C);
@ -120,20 +115,18 @@ namespace rct {
//decodeRct: (c.f. https://eprint.iacr.org/2015/1098 section 5.1.1)
// uses the attached ecdh info to find the amounts represented by each output commitment
// must know the destination private key to find the correct amount, else will return a random number
rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev);
rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, const int mixin, const RCTConfig &rct_config, hw::device &hwdev);
rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, xmr_amount txnFee, unsigned int mixin, const RCTConfig &rct_config, hw::device &hwdev);
rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, const std::vector<unsigned int> & index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev);
bool verRct(const rctSig & rv, bool semantics);
static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); }
inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); }
bool verRctSemanticsSimple(const rctSig & rv);
bool verRctSemanticsSimple(const std::vector<const rctSig*> & rv);
bool verRctNonSemanticsSimple(const rctSig & rv);
static inline bool verRctSimple(const rctSig & rv) { return verRctSemanticsSimple(rv) && verRctNonSemanticsSimple(rv); }
inline bool verRctSimple(const rctSig & rv) { return verRctSemanticsSimple(rv) && verRctNonSemanticsSimple(rv); }
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev);
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, hw::device &hwdev);
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev);
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, hw::device &hwdev);
key get_pre_mlsag_hash(const rctSig &rv, hw::device &hwdev);
key get_pre_clsag_hash(const rctSig &rv, hw::device &hwdev);
bool signMultisig(rctSig &rv, const std::vector<unsigned int> &indices, const keyV &k, const multisig_out &msout, const key &secret_key);
}

View File

@ -32,8 +32,7 @@
#include <cstddef>
#include <vector>
#include <iostream>
#include <cinttypes>
#include <cstdint>
#include <sodium/crypto_verify_32.h>
extern "C" {
@ -44,8 +43,7 @@ extern "C" {
#include "crypto/generic-ops.h"
#include "crypto/crypto.h"
#include "epee/hex.h"
#include "epee/span.h"
#include "common/hex.h"
#include "serialization/variant.h"
#include "common/util.h"
@ -105,6 +103,8 @@ namespace rct {
key L;
key R;
key ki;
~multisig_kLRki() { memwipe(&k, sizeof(k)); }
};
struct multisig_out {
@ -245,7 +245,7 @@ namespace rct {
if (Archive::is_deserializer)
v.resize(size);
else if (v.size() != size)
throw std::invalid_argument{"invalid "s + std::string{tag} + " size"s};
throw std::invalid_argument{"invalid " + std::string{tag} + " size: " + std::to_string(size) + " (given size) != " + std::to_string(v.size()) + " (# elements)"};
return ar.begin_array();
}
@ -256,26 +256,26 @@ namespace rct {
// ecdhInfo holds an encoded mask / amount to be passed to each receiver
// outPk contains public keypairs which are destinations (P, C),
// P = address, C = commitment to amount
enum {
RCTTypeNull = 0,
RCTTypeFull = 1,
RCTTypeSimple = 2,
RCTTypeBulletproof = 3,
RCTTypeBulletproof2 = 4,
RCTTypeCLSAG = 5,
enum class RCTType : uint8_t {
Null = 0,
Full = 1,
Simple = 2,
Bulletproof = 3,
Bulletproof2 = 4,
CLSAG = 5,
};
inline bool is_rct_simple(int type) { return tools::equals_any(type, RCTTypeSimple, RCTTypeBulletproof, RCTTypeBulletproof2, RCTTypeCLSAG); }
inline bool is_rct_bulletproof(int type) { return tools::equals_any(type, RCTTypeBulletproof, RCTTypeBulletproof2, RCTTypeCLSAG); }
inline bool is_rct_borromean(int type) { return tools::equals_any(type, RCTTypeSimple, RCTTypeFull); }
inline bool is_rct_simple(RCTType type) { return tools::equals_any(type, RCTType::Simple, RCTType::Bulletproof, RCTType::Bulletproof2, RCTType::CLSAG); }
inline bool is_rct_bulletproof(RCTType type) { return tools::equals_any(type, RCTType::Bulletproof, RCTType::Bulletproof2, RCTType::CLSAG); }
inline bool is_rct_borromean(RCTType type) { return tools::equals_any(type, RCTType::Simple, RCTType::Full); }
enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof, RangeProofPaddedBulletproof };
enum class RangeProofType : uint8_t { Borromean = 0, Bulletproof = 1, MultiOutputBulletproof = 2, PaddedBulletproof = 3 };
struct RCTConfig {
RangeProofType range_proof_type;
int bp_version;
};
struct rctSigBase {
uint8_t type;
RCTType type;
key message;
ctkeyM mixRing; //the set of all pubkeys / copy
//pairs that you mix with
@ -287,10 +287,10 @@ namespace rct {
template <typename Archive>
void serialize_rctsig_base(Archive &ar, size_t inputs, size_t outputs)
{
field(ar, "type", type);
if (type == RCTTypeNull)
field_varint(ar, "type", type, [](const RCTType& x) { return x <= RCTType::CLSAG; });
if (type == RCTType::Null)
return;
if (!tools::equals_any(type, RCTTypeFull, RCTTypeSimple, RCTTypeBulletproof, RCTTypeBulletproof2, RCTTypeCLSAG))
if (!tools::equals_any(type, RCTType::Full, RCTType::Simple, RCTType::Bulletproof, RCTType::Bulletproof2, RCTType::CLSAG))
throw std::invalid_argument{"invalid ringct type"};
field_varint(ar, "txnFee", txnFee);
@ -298,7 +298,7 @@ namespace rct {
// inputs/outputs not saved, only here for serialization help
// FIELD(message) - not serialized, it can be reconstructed
// FIELD(mixRing) - not serialized, it can be reconstructed
if (type == RCTTypeSimple) // moved to prunable with bulletproofs
if (type == RCTType::Simple) // moved to prunable with bulletproofs
{
auto arr = start_array(ar, "pseudoOuts", pseudoOuts, inputs);
for (auto& e : pseudoOuts)
@ -307,7 +307,7 @@ namespace rct {
{
auto arr = start_array(ar, "ecdhInfo", ecdhInfo, outputs);
if (tools::equals_any(type, RCTTypeBulletproof2, RCTTypeCLSAG))
if (tools::equals_any(type, RCTType::Bulletproof2, RCTType::CLSAG))
{
for (auto& e : ecdhInfo) {
auto obj = arr.element().begin_object();
@ -337,16 +337,16 @@ namespace rct {
// when changing this function, update cryptonote::get_pruned_transaction_weight
template<typename Archive>
void serialize_rctsig_prunable(Archive &ar, uint8_t type, size_t inputs, size_t outputs, size_t mixin)
void serialize_rctsig_prunable(Archive &ar, RCTType type, size_t inputs, size_t outputs, size_t mixin)
{
if (type == RCTTypeNull)
if (type == RCTType::Null)
return;
if (!tools::equals_any(type, RCTTypeFull, RCTTypeSimple, RCTTypeBulletproof, RCTTypeBulletproof2, RCTTypeCLSAG))
if (!tools::equals_any(type, RCTType::Full, RCTType::Simple, RCTType::Bulletproof, RCTType::Bulletproof2, RCTType::CLSAG))
throw std::invalid_argument{"invalid ringct type"};
if (rct::is_rct_bulletproof(type))
{
uint32_t nbp = bulletproofs.size();
if (tools::equals_any(type, RCTTypeBulletproof2, RCTTypeCLSAG))
if (tools::equals_any(type, RCTType::Bulletproof2, RCTType::CLSAG))
field_varint(ar, "nbp", nbp);
else
field(ar, "nbp", nbp);
@ -367,7 +367,7 @@ namespace rct {
value(arr.element(), s);
}
if (type == RCTTypeCLSAG)
if (type == RCTType::CLSAG)
{
auto arr = start_array(ar, "CLSAGs", CLSAGs, inputs);
@ -392,7 +392,7 @@ namespace rct {
// a simple or full rct signature, and it's starting to annoy the hell out of me
size_t mg_elements = 1, mg_ss2_elements = inputs + 1;
if (tools::equals_any(type, RCTTypeSimple, RCTTypeBulletproof, RCTTypeBulletproof2)) {
if (tools::equals_any(type, RCTType::Simple, RCTType::Bulletproof, RCTType::Bulletproof2)) {
mg_elements = inputs;
mg_ss2_elements = 2;
}
@ -425,7 +425,7 @@ namespace rct {
}
}
}
if (tools::equals_any(type, RCTTypeBulletproof, RCTTypeBulletproof2, RCTTypeCLSAG))
if (tools::equals_any(type, RCTType::Bulletproof, RCTType::Bulletproof2, RCTType::CLSAG))
{
auto arr = start_array(ar, "pseudoOuts", pseudoOuts, inputs);
for (auto& o : pseudoOuts)
@ -572,7 +572,7 @@ namespace cryptonote {
namespace rct {
inline std::ostream &operator <<(std::ostream &o, const rct::key &v) {
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
return o << '<' << tools::type_to_hex(v) << '>';
}
}

View File

@ -73,7 +73,7 @@ public:
{
if (!base_url_.empty())
set_base_url(std::move(base_url_));
session.SetUserAgent("loki rpc client v"s + LOKI_VERSION_STR);
session.SetUserAgent("loki rpc client v" + std::string{LOKI_VERSION_STR});
}
/// Sets the base_url to the given one. Will have / appended if it doesn't already end in /. The

View File

@ -581,7 +581,7 @@ namespace cryptonote::rpc {
throw std::logic_error{"Cannot call http_server::start() more than once"};
auto net = m_server.nettype();
m_server_header = "lokid/"s + (m_restricted ? std::to_string(LOKI_VERSION[0]) : LOKI_VERSION_FULL)
m_server_header = "lokid/"s + (m_restricted ? std::to_string(LOKI_VERSION[0]) : std::string{LOKI_VERSION_FULL})
+ (net == MAINNET ? " mainnet" : net == TESTNET ? " testnet" : net == DEVNET ? " devnet" : net == FAKECHAIN ? " fakenet" : " unknown net");
m_startup_promise.set_value(true);

View File

@ -78,7 +78,7 @@ namespace cryptonote::rpc {
// An optional required login for this HTTP RPC interface
std::optional<tools::login> m_login;
// Cached string we send for the Server header
std::string m_server_header = "Loki RPC HTTP/"s + LOKI_VERSION_STR;
std::string m_server_header = "Loki RPC HTTP/" + std::string{LOKI_VERSION_STR};
// Access-Control-Allow-Origin header values; if one of these match the incoming Origin header
// we return it in the ACAO header; otherwise (or if this is empty) we omit the header entirely.
std::unordered_set<std::string> m_cors;

View File

@ -31,6 +31,7 @@
#pragma once
#include <sstream>
#include <vector>
#include "binary_archive.h"
#include <streambuf>

View File

@ -106,7 +106,7 @@
*
* public:
* template <class Archive>
* void serialize_value(Archive& a) {
* void serialize_value(Archive& ar) {
* serialization::value(ar, v1);
* serialization::value(ar, v2);
* }

View File

@ -144,6 +144,7 @@ namespace
const command_line::arg_descriptor<std::string> arg_restore_date = {"restore-date", sw::tr("Restore from estimated blockchain height on specified date"), ""};
const command_line::arg_descriptor<bool> arg_do_not_relay = {"do-not-relay", sw::tr("The newly created transaction will not be relayed to the loki network"), false};
const command_line::arg_descriptor<bool> arg_create_address_file = {"create-address-file", sw::tr("Create an address file for new wallets"), false};
const command_line::arg_descriptor<std::string> arg_create_hwdev_txt = {"create-hwdev-txt", sw::tr("Create a .hwdev.txt file for new hardware-backed wallets containing the given comment")};
const command_line::arg_descriptor<std::string> arg_subaddress_lookahead = {"subaddress-lookahead", tools::wallet2::tr("Set subaddress lookahead sizes to <major>:<minor>"), ""};
const command_line::arg_descriptor<bool> arg_use_english_language_names = {"use-english-language-names", sw::tr("Display English language names"), false};
@ -3497,6 +3498,8 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
if(!ask_wallet_create_if_needed()) return false;
}
std::string default_restore_value = "0";
if (!m_generate_new.empty() || m_restoring)
{
if (!m_subaddress_lookahead.empty() && !parse_subaddress_lookahead(m_subaddress_lookahead))
@ -3901,26 +3904,11 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
m_wallet_file = m_generate_from_device;
// create wallet
auto r = new_wallet(vm);
auto r = new_device_wallet(vm);
CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
password = *r;
welcome = true;
// if no block_height is specified, assume its a new account and start it "now"
if(m_wallet->get_refresh_from_block_height() == 0) {
{
tools::scoped_message_writer wrt = tools::msg_writer();
wrt << tr("No restore height is specified.") << " ";
wrt << tr("Assumed you are creating a new account, restore will be done from current estimated blockchain height.") << " ";
wrt << tr("Use --restore-height or --restore-date if you want to restore an already setup account from a specific height.");
}
std::string confirm = input_line(tr("Is this okay?"), true);
if (std::cin.eof() || !command_line::is_yes(confirm))
CHECK_AND_ASSERT_MES(false, false, tr("account creation aborted"));
m_wallet->set_refresh_from_block_height(m_wallet->estimate_blockchain_height() > 0 ? m_wallet->estimate_blockchain_height() - 1 : 0);
m_wallet->explicit_refresh_from_block_height(true);
m_restore_height = m_wallet->get_refresh_from_block_height();
}
default_restore_value = "curr";
}
else
{
@ -3939,7 +3927,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
welcome = true;
}
if (m_restoring && m_generate_from_json.empty() && m_generate_from_device.empty())
if (m_restoring && m_generate_from_json.empty())
{
m_wallet->explicit_refresh_from_block_height(!(command_line::is_arg_defaulted(vm, arg_restore_height) &&
command_line::is_arg_defaulted(vm, arg_restore_date)));
@ -3968,18 +3956,24 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
bool connected = try_connect_to_daemon(false, &version);
while (true)
{
std::string heightstr;
if (!connected || version < rpc::version_t{1, 6})
heightstr = input_line("Restore from specific blockchain height (optional, default 0)");
else
heightstr = input_line("Restore from specific blockchain height (optional, default 0),\nor alternatively from specific date (YYYY-MM-DD)");
std::string prompt =
"\nEnter wallet restore blockchain height (e.g. 123456) or restore date\n"
"(e.g. 2020-07-21). Enter \"curr\" to use the current blockchain height.\n"
"NOTE: transactions before the restore height will not be detected.\n"
"[";
prompt += default_restore_value;
prompt += "]";
std::string heightstr = input_line(prompt);
if (std::cin.eof())
return false;
if (heightstr.empty())
{
m_restore_height = 0;
heightstr = default_restore_value;
if (heightstr == "curr") {
m_restore_height = m_wallet->estimate_blockchain_height();
if (m_restore_height) --m_restore_height;
break;
}
try
{
m_restore_height = boost::lexical_cast<uint64_t>(heightstr);
@ -4367,7 +4361,7 @@ std::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::prog
}
//----------------------------------------------------------------------------------------------------
std::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm)
std::optional<epee::wipeable_string> simple_wallet::new_device_wallet(const boost::program_options::variables_map& vm)
{
std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> rc;
try { rc = tools::wallet2::make_new(vm, false, password_prompter); }
@ -4395,10 +4389,18 @@ std::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::prog
try
{
bool create_address_file = command_line::get_arg(vm, arg_create_address_file);
std::optional<std::string> create_hwdev_txt;
if (!command_line::is_arg_defaulted(vm, arg_create_hwdev_txt))
create_hwdev_txt = command_line::get_arg(vm, arg_create_hwdev_txt);
m_wallet->device_derivation_path(device_derivation_path);
m_wallet->restore(m_wallet_file, std::move(rc.second).password(), device_desc.empty() ? "Ledger" : device_desc, create_address_file);
message_writer(epee::console_color_white, true) << tr("Generated new wallet on hw device: ")
<< m_wallet->get_account().get_public_address_str(m_wallet->nettype());
message_writer(epee::console_color_white, true) << tr("Connecting to hardware device");
message_writer() << tr("Your hardware device will ask for permission to export your wallet view key.\n"
"This is optional, but will significantly improve wallet syncing speed. Your\n"
"spend key (needed to spend funds) does not leave the device.");
m_wallet->restore_from_device(
m_wallet_file, std::move(rc.second).password(), device_desc.empty() ? "Ledger" : device_desc, create_address_file,
std::move(create_hwdev_txt), [](const std::string& msg) { message_writer(epee::console_color_green, true) << msg; });
message_writer(epee::console_color_white, true) << tr("Finished setting up wallet from hw device");
}
catch (const std::exception& e)
{
@ -4504,7 +4506,7 @@ std::optional<epee::wipeable_string> simple_wallet::open_wallet(const boost::pro
prefix = (boost::format(tr("Opened %u/%u multisig wallet%s")) % threshold % total % (ready ? "" : " (not yet finalized)")).str();
else
prefix = tr("Opened wallet");
message_writer(epee::console_color_white, true) <<
message_writer(epee::console_color_green, true) <<
prefix << ": " << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
if (m_wallet->get_account().get_device()) {
message_writer(epee::console_color_white, true) << "Wallet is on device: " << m_wallet->get_account().get_device().get_name();
@ -8810,7 +8812,7 @@ bool simple_wallet::rescan_blockchain(const std::vector<std::string> &args_)
}
m_in_manual_refresh.store(true, std::memory_order_relaxed);
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
LOKI_DEFER { m_in_manual_refresh.store(false, std::memory_order_relaxed); };
return refresh_main(start_height, reset_type, true);
}
//----------------------------------------------------------------------------------------------------
@ -10240,6 +10242,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_restore_date);
command_line::add_arg(desc_params, arg_do_not_relay);
command_line::add_arg(desc_params, arg_create_address_file);
command_line::add_arg(desc_params, arg_create_hwdev_txt);
command_line::add_arg(desc_params, arg_subaddress_lookahead);
command_line::add_arg(desc_params, arg_use_english_language_names);

View File

@ -110,7 +110,7 @@ namespace cryptonote
const std::optional<crypto::secret_key>& spendkey, const crypto::secret_key& viewkey);
std::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm,
const epee::wipeable_string &multisig_keys, const std::string &old_language);
std::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm);
std::optional<epee::wipeable_string> new_device_wallet(const boost::program_options::variables_map& vm);
std::optional<epee::wipeable_string> open_wallet(const boost::program_options::variables_map& vm);
bool close_wallet();

View File

@ -1,8 +1,10 @@
#include "version.h"
using namespace std::literals;
const std::array<uint16_t, 3> LOKI_VERSION = {@PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@};
const char* const LOKI_VERSION_TAG = "@VERSIONTAG@";
const char* const LOKI_VERSION_STR = "@PROJECT_VERSION@";
const char* const LOKI_RELEASE_NAME = "@LOKI_RELEASE_CODENAME@";
const char* const LOKI_VERSION_FULL = "@PROJECT_VERSION@-@VERSIONTAG@@LOKI_RELEASE_SUFFIX@";
const std::string_view LOKI_VERSION_TAG = "@VERSIONTAG@"sv;
const std::string_view LOKI_VERSION_STR = "@PROJECT_VERSION@"sv;
const std::string_view LOKI_RELEASE_NAME = "@LOKI_RELEASE_CODENAME@"sv;
const std::string_view LOKI_VERSION_FULL = "@PROJECT_VERSION@-@VERSIONTAG@@LOKI_RELEASE_SUFFIX@"sv;

View File

@ -1,10 +1,11 @@
#pragma once
#include <cstdint>
#include <array>
#include <string_view>
extern const std::array<uint16_t, 3> LOKI_VERSION;
extern const char* const LOKI_VERSION_TAG;
extern const char* const LOKI_VERSION_STR;
extern const char* const LOKI_RELEASE_NAME;
extern const char* const LOKI_VERSION_FULL;
extern const std::string_view LOKI_VERSION_TAG;
extern const std::string_view LOKI_VERSION_STR;
extern const std::string_view LOKI_RELEASE_NAME;
extern const std::string_view LOKI_VERSION_FULL;

View File

@ -681,7 +681,7 @@ bool WalletImpl::recoverFromDevice(std::string_view path_, const std::string &pa
m_recoveringFromDevice = true;
try
{
m_wallet->restore(path, password, device_name);
m_wallet->restore_from_device(path, password, device_name);
LOG_PRINT_L1("Generated new wallet from device: " + device_name);
}
catch (const std::exception& e) {

View File

@ -102,7 +102,7 @@ void serialize(Archive &a, wallet::tx_construction_data &x, const unsigned int v
a & x.subaddr_indices;
if (!typename Archive::is_saving())
{
x.rct_config = { rct::RangeProofBorromean, 0 };
x.rct_config = { rct::RangeProofType::Borromean, 0 };
if (ver < 6)
{
x.tx_type = cryptonote::txtype::standard;
@ -117,10 +117,10 @@ void serialize(Archive &a, wallet::tx_construction_data &x, const unsigned int v
return;
if (ver < 5)
{
bool use_bulletproofs = x.rct_config.range_proof_type != rct::RangeProofBorromean;
bool use_bulletproofs = x.rct_config.range_proof_type != rct::RangeProofType::Borromean;
a & use_bulletproofs;
if (!typename Archive::is_saving())
x.rct_config = { use_bulletproofs ? rct::RangeProofBulletproof : rct::RangeProofBorromean, 0 };
x.rct_config = { use_bulletproofs ? rct::RangeProofType::Bulletproof : rct::RangeProofType::Borromean, 0 };
return;
}
a & x.rct_config;

View File

@ -42,6 +42,7 @@
#include <lokimq/base64.h>
#include "common/password.h"
#include "common/string_util.h"
#include "cryptonote_basic/tx_extra.h"
#include "cryptonote_core/loki_name_system.h"
#include "common/rules.h"
#include "cryptonote_config.h"
@ -1684,15 +1685,15 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &
{
switch (rv.type)
{
case rct::RCTTypeSimple:
case rct::RCTTypeBulletproof:
case rct::RCTTypeBulletproof2:
case rct::RCTTypeCLSAG:
case rct::RCTType::Simple:
case rct::RCTType::Bulletproof:
case rct::RCTType::Bulletproof2:
case rct::RCTType::CLSAG:
return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev);
case rct::RCTTypeFull:
case rct::RCTType::Full:
return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev);
default:
LOG_ERROR(__func__ << ": Unsupported rct type: " << rv.type);
LOG_ERROR(__func__ << ": Unsupported rct type: " << (int)rv.type);
return 0;
}
}
@ -1907,7 +1908,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
{
hw::device &hwdev = m_account.get_device();
std::unique_lock hwdev_lock{hwdev};
hw::reset_mode rst(hwdev);
hw::mode_resetter rst{hwdev};
hwdev.set_mode(hw::device::TRANSACTION_PARSE);
if (!hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation))
@ -2776,7 +2777,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
waiter.wait(&tpool);
hw::device &hwdev = m_account.get_device();
hw::reset_mode rst(hwdev);
hw::mode_resetter rst{hwdev};
hwdev.set_mode(hw::device::TRANSACTION_PARSE);
const cryptonote::account_keys &keys = m_account.get_keys();
@ -4455,10 +4456,11 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
account_public_address device_account_public_address;
THROW_WALLET_EXCEPTION_IF(!hwdev.get_public_address(device_account_public_address), error::wallet_internal_error, "Cannot get a device address");
THROW_WALLET_EXCEPTION_IF(device_account_public_address != m_account.get_keys().m_account_address, error::wallet_internal_error, "Device wallet does not match wallet address. "
"Device address: " + cryptonote::get_account_address_as_str(m_nettype, false, device_account_public_address) +
", wallet address: " + m_account.get_public_address_str(m_nettype));
LOG_PRINT_L0("Device inited...");
THROW_WALLET_EXCEPTION_IF(device_account_public_address != m_account.get_keys().m_account_address, error::wallet_internal_error,
"Device wallet does not match wallet address. "
"Device address: " + cryptonote::get_account_address_as_str(m_nettype, false, device_account_public_address) +
", wallet address: " + m_account.get_public_address_str(m_nettype));
LOG_PRINT_L0("Device initialized...");
} else if (key_on_device()) {
THROW_WALLET_EXCEPTION(error::wallet_internal_error, "hardware device not supported");
}
@ -4935,13 +4937,8 @@ void wallet2::generate(const fs::path& wallet_, const epee::wipeable_string& pas
store();
}
/*!
* \brief Creates a wallet from a device
* \param wallet_ Name of wallet file
* \param password Password of wallet file
* \param device_name device string address
*/
void wallet2::restore(const fs::path& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file)
void wallet2::restore_from_device(const fs::path& wallet_, const epee::wipeable_string& password, const std::string &device_name,
bool create_address_file, std::optional<std::string> hwdev_label, std::function<void(std::string msg)> progress_callback)
{
clear();
prepare_file_names(wallet_);
@ -4961,6 +4958,8 @@ void wallet2::restore(const fs::path& wallet_, const epee::wipeable_string& pass
m_account.create_from_device(hwdev);
init_type(m_account.get_device().get_type());
setup_keys(password);
if (progress_callback)
progress_callback(tr("Retrieved wallet address from device: ") + m_account.get_public_address_str(m_nettype));
m_device_name = device_name;
create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
@ -4970,6 +4969,14 @@ void wallet2::restore(const fs::path& wallet_, const epee::wipeable_string& pass
m_subaddress_lookahead_major = 5;
m_subaddress_lookahead_minor = 20;
}
if (hwdev_label) {
fs::path hwdev_txt = m_wallet_file;
hwdev_txt += ".hwdev.txt";
if (!save_to_file(hwdev_txt, *hwdev_label, true))
MERROR("failed to write .hwdev.txt comment file");
}
if (progress_callback)
progress_callback(tr("Setting up account and subaddresses"));
setup_new_blockchain();
if (!wallet_.empty()) {
store();
@ -4989,6 +4996,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
std::vector<crypto::secret_key> multisig_keys;
rct::key spend_pkey = rct::identity();
rct::key spend_skey;
LOKI_DEFER { memwipe(&spend_skey, sizeof(spend_skey)); };
std::vector<crypto::public_key> multisig_signers;
// decrypt keys
@ -6964,7 +6972,7 @@ void wallet2::commit_tx(pending_tx& ptx, bool blink)
// tx generated, get rid of used k values
for (size_t idx: ptx.selected_transfers)
m_transfers[idx].m_multisig_k.clear();
memwipe(m_transfers[idx].m_multisig_k.data(), m_transfers[idx].m_multisig_k.size() * sizeof(m_transfers[idx].m_multisig_k[0]));
//fee includes dust if dust policy specified it.
LOG_PRINT_L1("Transaction successfully " << (blink ? "blinked. " : "sent. ") << txid
@ -7405,13 +7413,13 @@ std::string wallet2::save_multisig_tx(multisig_tx_set txs)
// txes generated, get rid of used k values
for (size_t n = 0; n < txs.m_ptx.size(); ++n)
for (size_t idx: txs.m_ptx[n].construction_data.selected_transfers)
m_transfers[idx].m_multisig_k.clear();
memwipe(m_transfers[idx].m_multisig_k.data(), m_transfers[idx].m_multisig_k.size() * sizeof(m_transfers[idx].m_multisig_k[0]));
// zero out some data we don't want to share
for (auto &ptx: txs.m_ptx)
{
for (auto &e: ptx.construction_data.sources)
e.multisig_kLRki.k = rct::zero();
memwipe(&e.multisig_kLRki.k, sizeof(e.multisig_kLRki.k));
}
for (auto &ptx: txs.m_ptx)
@ -7621,10 +7629,16 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto
ptx.tx.rct_signatures = sig.sigs;
rct::keyV k;
rct::key skey = rct::zero();
LOKI_DEFER {
memwipe(k.data(), k.size() * sizeof(rct::key));
memwipe(&skey, sizeof(skey));
};
k.reserve(sd.selected_transfers.size());
for (size_t idx: sd.selected_transfers)
k.push_back(get_multisig_k(idx, sig.used_L));
rct::key skey = rct::zero();
for (const auto &msk: get_account().get_multisig_keys())
{
crypto::public_key pmsk = get_multisig_signing_public_key(msk);
@ -7672,7 +7686,7 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto
// txes generated, get rid of used k values
for (size_t n = 0; n < exported_txs.m_ptx.size(); ++n)
for (size_t idx: exported_txs.m_ptx[n].construction_data.selected_transfers)
m_transfers[idx].m_multisig_k.clear();
memwipe(m_transfers[idx].m_multisig_k.data(), m_transfers[idx].m_multisig_k.size() * sizeof(m_transfers[idx].m_multisig_k[0]));
exported_txs.m_signers.insert(get_multisig_signer_public_key());
@ -8602,9 +8616,21 @@ wallet2::request_stake_unlock_result wallet2::can_request_stake_unlock(const cry
return result;
}
if (!generate_signature_for_request_stake_unlock(unlock.key_image, unlock.signature, unlock.nonce))
{
result.msg = tr("Failed to generate signature to sign request. The key image: ") + contribution.key_image + (" doesn't belong to this wallet");
// We used to use a 32-byte time value concatenated to itself 8 times as a message hash, but
// that accomplishes nothing (there is no point in using a nonce in the message itself: a
// signature already has its own nonce), so now we just sign with a null hash and always send
// out a nonce value of 0. The nonce value, unfortunately, can't be easily removed from the
// key image unlock tx_extra data without versioning/replacing it, so we still send this 0,
// but this will hopefully make it easier in the future to eliminate from the tx extra.
unlock.nonce = tx_extra_tx_key_image_unlock::FAKE_NONCE;
try {
if (!generate_signature_for_request_stake_unlock(unlock.key_image, unlock.signature))
{
result.msg = tr("Failed to generate signature to sign request. The key image: ") + contribution.key_image + tr(" doesn't belong to this wallet");
return result;
}
} catch (const std::exception& e) {
result.msg = tr("Failed to generate unlock signature: ") + std::string(e.what());
return result;
}
}
@ -8638,24 +8664,19 @@ static bool try_generate_lns_signature(wallet2 const &wallet, std::string const
std::optional<cryptonote::subaddress_index> index = wallet.get_subaddress_index(curr_owner_parsed.address);
if (!index) return false;
// TODO(doyle): Taken from wallet2.cpp::get_reserve_proof
crypto::secret_key skey = wallet.get_account().get_keys().m_spend_secret_key;
if (!index->is_zero())
{
crypto::secret_key m = wallet.get_account().get_device().get_subaddress_secret_key(wallet.get_account().get_keys().m_view_secret_key, *index);
crypto::secret_key tmp = skey;
sc_add((unsigned char*)&skey, (unsigned char*)&m, (unsigned char*)&tmp);
}
auto sig_data = lns::tx_extra_signature(
result.encrypted_value.to_view(),
new_owner ? &result.owner : nullptr,
new_backup_owner ? &result.backup_owner : nullptr,
result.prev_txid);
if (sig_data.empty()) return false;
crypto::public_key pkey;
crypto::secret_key_to_public_key(skey, pkey);
auto& account = wallet.get_account();
auto& hwdev = account.get_device();
hw::mode_resetter rst{hwdev};
hwdev.generate_lns_signature(sig_data, account.get_keys(), *index, result.signature.monero);
result.signature.type = lns::generic_owner_sig_type::monero;
crypto::hash hash = lns::tx_extra_signature_hash(result.encrypted_value.to_view(),
new_owner ? &result.owner : nullptr,
new_backup_owner ? &result.backup_owner : nullptr,
result.prev_txid);
if (!hash) return false;
result.signature = lns::make_monero_signature(hash, pkey, skey);
return true;
}
@ -10026,7 +10047,20 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
rct::multisig_out msout;
LOG_PRINT_L2("constructing tx");
auto sources_copy = sources;
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts, extra, tx, unlock_time, tx_key, additional_tx_keys, rct_config, m_multisig ? &msout : NULL, tx_params);
bool r = cryptonote::construct_tx_and_get_tx_key(
m_account.get_keys(),
m_subaddresses,
sources,
splitted_dsts,
change_dts,
extra,
tx,
unlock_time,
tx_key,
additional_tx_keys,
rct_config,
m_multisig ? &msout : nullptr,
tx_params);
LOG_PRINT_L2("constructed tx, r="<<r);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_nettype);
@ -10132,7 +10166,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
ptx.construction_data.tx_type = tx_params.tx_type;
ptx.construction_data.hf_version = tx_params.hf_version;
ptx.construction_data.rct_config = {
tx.rct_signatures.p.bulletproofs.empty() ? rct::RangeProofBorromean : rct::RangeProofPaddedBulletproof,
tx.rct_signatures.p.bulletproofs.empty() ? rct::RangeProofType::Borromean : rct::RangeProofType::PaddedBulletproof,
use_fork_rules(HF_VERSION_CLSAG, 0) ? 3 : 2
};
ptx.construction_data.dests = dsts;
@ -10765,7 +10799,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
//ensure device is let in NONE mode in any case
hw::device &hwdev = m_account.get_device();
std::unique_lock hwdev_lock{hwdev};
hw::reset_mode rst(hwdev);
hw::mode_resetter rst{hwdev};
bool const is_lns_tx = (tx_params.tx_type == txtype::loki_name_system);
auto original_dsts = dsts;
@ -10826,7 +10860,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
uint64_t needed_fee, available_for_fee = 0;
uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
const bool clsag = use_fork_rules(HF_VERSION_CLSAG, 0);
const rct::RCTConfig rct_config{rct::RangeProofPaddedBulletproof, clsag ? 3 : 2};
const rct::RCTConfig rct_config{rct::RangeProofType::PaddedBulletproof, clsag ? 3 : 2};
const auto base_fee = get_base_fees();
const uint64_t fee_percent = get_fee_percent(priority, tx_params.tx_type);
@ -11469,7 +11503,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
//ensure device is let in NONE mode in any case
hw::device &hwdev = m_account.get_device();
std::unique_lock hwdev_lock{hwdev};
hw::reset_mode rst(hwdev);
hw::mode_resetter rst{hwdev};
uint64_t accumulated_fee, accumulated_outputs, accumulated_change;
struct TX {
@ -11489,7 +11523,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
std::vector<std::vector<get_outs_entry>> outs;
const bool clsag = use_fork_rules(HF_VERSION_CLSAG, 0);
const rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, clsag ? 3 : 2 };
const rct::RCTConfig rct_config { rct::RangeProofType::PaddedBulletproof, clsag ? 3 : 2 };
const auto base_fee = get_base_fees();
const uint64_t fee_percent = get_fee_percent(priority, tx_type);
const uint64_t fee_quantization_mask = get_fee_quantization_mask();
@ -12142,8 +12176,9 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, std::string_view
std::to_string(res.outs.size()) + ", expected " + std::to_string(ring_size));
// copy pubkey pointers
std::vector<const crypto::public_key *> p_output_keys;
for (const auto& out : res.outs)
std::vector<const crypto::public_key*> p_output_keys;
p_output_keys.reserve(res.outs.size());
for (auto& out : res.outs)
p_output_keys.push_back(&out.key);
// figure out real output index and secret key
@ -12159,9 +12194,7 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, std::string_view
THROW_WALLET_EXCEPTION_IF(sec_index >= ring_size, error::wallet_internal_error, "secret index not found");
// generate ring sig for this input
signatures.push_back(std::vector<crypto::signature>());
std::vector<crypto::signature>& sigs = signatures.back();
sigs.resize(in_key->key_offsets.size());
auto& sigs = signatures.emplace_back(in_key->key_offsets.size());
crypto::generate_ring_signature(sig_prefix_hash, in_key->k_image, p_output_keys, in_ephemeral.sec, sec_index, sigs.data());
}
@ -12305,7 +12338,7 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt
if (found)
{
uint64_t amount;
if (tx.version == txversion::v1 || tx.rct_signatures.type == rct::RCTTypeNull)
if (tx.version == txversion::v1 || tx.rct_signatures.type == rct::RCTType::Null)
{
amount = tx.vout[n].amount;
}
@ -12314,7 +12347,7 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt
crypto::secret_key scalar1;
crypto::derivation_to_scalar(found_derivation, n, scalar1);
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tools::equals_any(tx.rct_signatures.type, rct::RCTTypeBulletproof2, rct::RCTTypeCLSAG));
rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tools::equals_any(tx.rct_signatures.type, rct::RCTType::Bulletproof2, rct::RCTType::CLSAG));
const rct::key C = tx.rct_signatures.outPk[n].mask;
rct::key Ctmp;
THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask");
@ -12706,8 +12739,7 @@ std::string wallet2::get_reserve_proof(const std::optional<std::pair<uint32_t, u
THROW_WALLET_EXCEPTION_IF(ephemeral.pub != td.get_public_key(), error::wallet_internal_error, "Derived public key doesn't agree with the stored one");
// generate signature for key image
const std::vector<const crypto::public_key*> pubs = { &ephemeral.pub };
crypto::generate_ring_signature(prefix_hash, td.m_key_image, &pubs[0], 1, ephemeral.sec, 0, &proof.key_image_sig);
crypto::generate_key_image_signature(td.m_key_image, ephemeral.pub, ephemeral.sec, proof.key_image_sig);
}
// collect all subaddress spend keys that received those outputs and generate their signatures
@ -12850,8 +12882,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
return false;
// check signature for key image
const std::vector<const crypto::public_key*> pubs = { &out_key->key };
ok = crypto::check_ring_signature(prefix_hash, proof.key_image, &pubs[0], 1, &proof.key_image_sig);
ok = crypto::check_key_image_signature(proof.key_image, out_key->key, proof.key_image_sig);
if (!ok)
return false;
@ -12871,7 +12902,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
crypto::secret_key shared_secret;
crypto::derivation_to_scalar(derivation, proof.index_in_tx, shared_secret);
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[proof.index_in_tx];
rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), tools::equals_any(tx.rct_signatures.type, rct::RCTTypeBulletproof2, rct::RCTTypeCLSAG));
rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), tools::equals_any(tx.rct_signatures.type, rct::RCTType::Bulletproof2, rct::RCTType::CLSAG));
amount = rct::h2d(ecdh_info.amount);
}
total += amount;
@ -13310,13 +13341,9 @@ std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>>
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
// sign the key image with the output secret key
crypto::signature signature;
std::vector<const crypto::public_key*> key_ptrs;
key_ptrs.push_back(&pkey);
crypto::generate_ring_signature((const crypto::hash&)td.m_key_image, td.m_key_image, key_ptrs, in_ephemeral.sec, 0, &signature);
ski.push_back(std::make_pair(td.m_key_image, signature));
auto& ki_s = ski.emplace_back();
ki_s.first = td.m_key_image;
crypto::generate_key_image_signature(ki_s.first, pkey, in_ephemeral.sec, ki_s.second);
}
return std::make_pair(offset, ski);
}
@ -13414,13 +13441,11 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
const cryptonote::tx_out &out = td.m_tx.vout[td.m_internal_output_index];
THROW_WALLET_EXCEPTION_IF(!std::holds_alternative<txout_to_key>(out.target), error::wallet_internal_error,
"Non txout_to_key output found");
const auto pkey = var::get<cryptonote::txout_to_key>(out.target).key;
const auto& pkey = var::get<cryptonote::txout_to_key>(out.target).key;
std::string const key_image_str = tools::type_to_hex(key_image);
if (!td.m_key_image_known || !(key_image == td.m_key_image))
{
std::vector<const crypto::public_key*> pkeys;
pkeys.push_back(&pkey);
THROW_WALLET_EXCEPTION_IF(!(rct::scalarmultKey(rct::ki2rct(key_image), rct::curveOrder()) == rct::identity()),
error::wallet_internal_error, "Key image out of validity domain: input " + std::to_string(n + offset) + "/"
+ std::to_string(signed_key_images.size()) + ", key image " + key_image_str);
@ -13434,10 +13459,10 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
// for that block, export_key_images will fail temporarily until the
// block is commited and the wallets sorts its transfers into a finalized
// canonical ordering.
THROW_WALLET_EXCEPTION_IF(!crypto::check_ring_signature((const crypto::hash&)key_image, key_image, pkeys, &signature),
THROW_WALLET_EXCEPTION_IF(!crypto::check_key_image_signature(key_image, pkey, signature),
error::signature_check_failed, std::to_string(n + offset) + "/"
+ std::to_string(signed_key_images.size()) + ", key image " + key_image_str
+ ", signature " + tools::type_to_hex(signature) + ", pubkey " + tools::type_to_hex(*pkeys[0]));
+ ", signature " + tools::type_to_hex(signature) + ", pubkey " + tools::type_to_hex(pkey));
}
req.key_images.push_back(key_image_str);
}
@ -14050,7 +14075,7 @@ cryptonote::blobdata wallet2::export_multisig()
{
transfer_details &td = m_transfers[n];
crypto::key_image ki;
td.m_multisig_k.clear();
memwipe(td.m_multisig_k.data(), td.m_multisig_k.size() * sizeof(td.m_multisig_k[0]));
info[n].m_LR.clear();
info[n].m_partial_key_images.clear();
@ -14160,6 +14185,10 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
CHECK_AND_ASSERT_THROW_MES(info.size() + 1 <= m_multisig_signers.size() && info.size() + 1 >= m_multisig_threshold, "Wrong number of multisig sources");
std::vector<std::vector<rct::key>> k;
LOKI_DEFER {
for (auto& kv : k)
memwipe(kv.data(), kv.size() * sizeof(rct::key));
};
k.reserve(m_transfers.size());
for (const auto &td: m_transfers)
k.push_back(td.m_multisig_k);
@ -14563,44 +14592,50 @@ bool wallet2::contains_key_image(const crypto::key_image& key_image) const {
return result;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::generate_signature_for_request_stake_unlock(crypto::key_image const &key_image, crypto::signature &signature, uint32_t &nonce) const
bool wallet2::generate_signature_for_request_stake_unlock(const crypto::key_image& key_image, crypto::signature& signature) const
{
const auto &key_image_it = m_key_images.find(key_image);
auto key_image_it = m_key_images.find(key_image);
if (key_image_it == m_key_images.end())
return false;
size_t transfer_details_index = key_image_it->second;
transfer_details const &td = m_transfers[transfer_details_index];
cryptonote::keypair in_ephemeral;
const auto& td = m_transfers[key_image_it->second];
// get ephemeral public key
const auto& target = td.m_tx.vout[td.m_internal_output_index].target;
THROW_WALLET_EXCEPTION_IF(!std::holds_alternative<txout_to_key>(target), error::wallet_internal_error, "Output is not txout_to_key");
const auto& pkey = var::get<cryptonote::txout_to_key>(target).key;
crypto::public_key tx_pub_key;
if (!try_get_tx_pub_key_using_td(td, tx_pub_key))
{
// get ephemeral public key
const cryptonote::tx_out &out = td.m_tx.vout[td.m_internal_output_index];
THROW_WALLET_EXCEPTION_IF(!std::holds_alternative<txout_to_key>(out.target), error::wallet_internal_error, "Output is not txout_to_key");
const cryptonote::txout_to_key &o = var::get<cryptonote::txout_to_key>(out.target);
const crypto::public_key pkey = o.key;
crypto::public_key tx_pub_key;
if (!try_get_tx_pub_key_using_td(td, tx_pub_key))
{
// TODO(doyle): TODO(loki): Fallback to old get tx pub key method for
// incase for now. But we need to go find out why we can't just use
// td.m_pk_index for everything? If we were able to decode the output
// using that, why not use it for everthing?
tx_pub_key = get_tx_pub_key_from_received_outs(td);
}
const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx);
// generate ephemeral secret key
crypto::key_image ki;
bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, pkey, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, ki, m_account.get_device());
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
THROW_WALLET_EXCEPTION_IF(td.m_key_image_known && !td.m_key_image_partial && ki != td.m_key_image, error::wallet_internal_error, "key_image generated not matched with cached key image");
THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != pkey, error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
// TODO(doyle): TODO(loki): Fallback to old get tx pub key method for
// incase for now. But we need to go find out why we can't just use
// td.m_pk_index for everything? If we were able to decode the output
// using that, why not use it for everthing?
tx_pub_key = get_tx_pub_key_from_received_outs(td);
}
nonce = static_cast<uint32_t>(time(nullptr));
crypto::hash hash = service_nodes::generate_request_stake_unlock_hash(nonce);
crypto::generate_signature(hash, in_ephemeral.pub, in_ephemeral.sec, signature);
// generate ephemeral secret key
auto& hwdev = m_account.get_device();
cryptonote::keypair in_ephemeral;
crypto::key_image ki;
bool r = cryptonote::generate_key_image_helper(
m_account.get_keys(),
m_subaddresses,
pkey,
tx_pub_key,
get_additional_tx_pub_keys_from_extra(td.m_tx),
td.m_internal_output_index,
in_ephemeral,
ki,
hwdev);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
THROW_WALLET_EXCEPTION_IF(td.m_key_image_known && !td.m_key_image_partial && ki != td.m_key_image, error::wallet_internal_error, "key_image generated not matched with cached key image");
THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != pkey, error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
THROW_WALLET_EXCEPTION_IF(
!hwdev.generate_unlock_signature(in_ephemeral.pub, in_ephemeral.sec, signature),
error::wallet_internal_error, "Hardware device failed to sign the unlock request");
return true;
}
//----------------------------------------------------------------------------------------------------

View File

@ -519,13 +519,18 @@ private:
const cryptonote::account_public_address &account_public_address,
const crypto::secret_key& viewkey = crypto::secret_key(), bool create_address_file = true);
/*!
* \brief Restore a wallet hold by an HW.
* \brief Restore a wallet from a hardware device
* \param wallet_ Name of wallet file
* \param password Password of wallet file
* \param device_name name of HW to use
* \param create_address_file Whether to create an address file
* \param hwdev_label if non-nullopt, create a [wallet].hwdev.txt containing the
* specified string content (which can be empty). Used to identify
* a hardware-backed wallet file with an optional comment.
* \param status_callback callback to invoke with progress messages to display to the user
*/
void restore(const fs::path& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file = true);
void restore_from_device(const fs::path& wallet_, const epee::wipeable_string& password, const std::string &device_name,
bool create_address_file = false, std::optional<std::string> hwdev_label = std::nullopt, std::function<void(std::string msg)> status_callback = {});
/*!
* \brief Creates a multisig wallet
@ -695,7 +700,7 @@ private:
std::pair<size_t, size_t> get_subaddress_lookahead() const { return {m_subaddress_lookahead_major, m_subaddress_lookahead_minor}; }
bool contains_address(const cryptonote::account_public_address& address) const;
bool contains_key_image(const crypto::key_image& key_image) const;
bool generate_signature_for_request_stake_unlock(crypto::key_image const &key_image, crypto::signature &signature, uint32_t &nonce) const;
bool generate_signature_for_request_stake_unlock(crypto::key_image const &key_image, crypto::signature &signature) const;
/*!
* \brief Tells if the wallet file is deprecated.
*/

View File

@ -497,7 +497,7 @@ namespace tools
m_restricted = command_line::get_arg(m_vm, arg_restricted);
m_server_header = "loki-wallet-rpc/"s + (m_restricted ? std::to_string(LOKI_VERSION[0]) : LOKI_VERSION_STR);
m_server_header = "loki-wallet-rpc/"s + (m_restricted ? std::to_string(LOKI_VERSION[0]) : std::string{LOKI_VERSION_STR});
m_cors = {rpc_config.access_control_origins.begin(), rpc_config.access_control_origins.end()};
@ -2365,6 +2365,7 @@ namespace {
if (ptr)
throw wallet_rpc_error{error_code::UNKNOWN_ERROR, "Invalid filename"};
fs::path wallet_file = req.filename.empty() ? fs::path{} : m_wallet_dir / fs::u8path(req.filename);
if (!req.hardware_wallet)
{
std::vector<std::string> languages;
crypto::ElectrumWords::get_language_list(languages, false);
@ -2383,15 +2384,21 @@ namespace {
std::unique_ptr<tools::wallet2> wal = tools::wallet2::make_new(vm2, true, nullptr).first;
if (!wal)
throw wallet_rpc_error{error_code::UNKNOWN_ERROR, "Failed to create wallet"};
wal->set_seed_language(req.language);
if (!req.hardware_wallet)
wal->set_seed_language(req.language);
rpc::GET_HEIGHT::request hreq{};
rpc::GET_HEIGHT::response hres{};
hres.height = 0;
bool r = wal->invoke_http<rpc::GET_HEIGHT>(hreq, hres);
if (r)
wal->set_refresh_from_block_height(hres.height);
crypto::secret_key dummy_key;
wal->generate(wallet_file, req.password, dummy_key, false, false);
if (req.hardware_wallet)
wal->restore_from_device(wallet_file, req.password, req.device_name.empty() ? "Ledger" : req.device_name);
else
wal->generate(wallet_file, req.password);
if (m_wallet)
m_wallet->store();

View File

@ -857,6 +857,9 @@ KV_SERIALIZE_MAP_CODE_BEGIN(CREATE_WALLET::request)
KV_SERIALIZE(filename)
KV_SERIALIZE(password)
KV_SERIALIZE(language)
KV_SERIALIZE(hardware_wallet)
KV_SERIALIZE(device_name)
KV_SERIALIZE(device_label)
KV_SERIALIZE_MAP_CODE_END()

View File

@ -1652,7 +1652,7 @@ namespace tools::wallet_rpc {
};
LOKI_RPC_DOC_INTROSPECT
// Create a new wallet. You need to have set the argument "'wallet-dir" when launching loki-wallet-rpc to make this work.
// Create a new wallet. You need to have set the argument "'--wallet-dir" when launching loki-wallet-rpc to make this work.
struct CREATE_WALLET : RPC_COMMAND
{
static constexpr auto names() { return NAMES("create_wallet"); }
@ -1662,6 +1662,9 @@ namespace tools::wallet_rpc {
std::string filename; // Set the wallet file name.
std::string password; // (Optional) Set the password to protect the wallet.
std::string language; // Language for your wallets' seed.
bool hardware_wallet; // Create this wallet from a connected hardware wallet. (`language` will be ignored).
std::string device_name; // When `hardware` is true, this specifies the hardware wallet device type (currently supported: "Ledger"). If omitted "Ledger" is used.
std::optional<std::string> device_label; // Custom label to write to a `wallet.hwdev.txt`. Can be empty; omit the parameter entirely to not write a .hwdev.txt file at all.
KV_MAP_SERIALIZABLE
};
@ -1670,7 +1673,7 @@ namespace tools::wallet_rpc {
};
LOKI_RPC_DOC_INTROSPECT
// Open a wallet. You need to have set the argument "-wallet-dir" when launching loki-wallet-rpc to make this work.
// Open a wallet. You need to have set the argument "--wallet-dir" when launching loki-wallet-rpc to make this work.
// The wallet rpc executable may only open wallet files within the same directory as wallet-dir, otherwise use the
// "--wallet-file" flag to open specific wallets.
struct OPEN_WALLET : RPC_COMMAND
@ -1679,7 +1682,7 @@ namespace tools::wallet_rpc {
struct request
{
std::string filename; // Wallet name stored in "-wallet-dir".
std::string filename; // Wallet name stored in "--wallet-dir".
std::string password; // The wallet password, set as "" if there's no password
bool autosave_current; // (Optional: Default true): If a pre-existing wallet is open, save to disk before opening the new wallet.

View File

@ -425,7 +425,7 @@ static bool construct_miner_tx_with_extra_output(cryptonote::transaction& tx,
uint64_t already_generated_coins,
const cryptonote::account_public_address& extra_address)
{
keypair txkey = keypair::generate(hw::get_device("default"));
keypair txkey{hw::get_device("default")};
add_tx_extra<tx_extra_pub_key>(tx, txkey.pub);
keypair gov_key = get_deterministic_keypair_from_height(height);
@ -570,7 +570,7 @@ bool gen_block_is_too_big::generate(std::vector<test_event_entry>& events) const
bool gen_block_invalid_binary_format::generate(std::vector<test_event_entry>& events) const
{
#if 1
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);

View File

@ -61,9 +61,7 @@ bool gen_bp_tx_validation_base::generate_with(std::vector<test_event_entry>& eve
}
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = {
std::make_pair(7, 0),
std::make_pair(8, 1),
std::make_pair(target_hf, NUM_UNLOCKED_BLOCKS + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW + 1),
{7,0}, {8,1}, {target_hf, NUM_UNLOCKED_BLOCKS + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW + 1},
};
event_replay_settings settings = {};
settings.hard_forks = hard_forks;
@ -272,11 +270,12 @@ bool gen_bp_tx_validation_base::generate_with(std::vector<test_event_entry>& eve
rct::key rct_tx_mask;
uint64_t amount = 0;
const uint8_t type = rct_txes.back().rct_signatures.type;
const auto& sigs = rct_txes.back().rct_signatures;
const auto type = sigs.type;
if (rct::is_rct_simple(type))
amount = rct::decodeRctSimple(rct_txes.back().rct_signatures, rct::sk2rct(amount_key), o, rct_tx_mask, hw::get_device("default"));
amount = rct::decodeRctSimple(sigs, rct::sk2rct(amount_key), o, rct_tx_mask, hw::get_device("default"));
else
amount = rct::decodeRct(rct_txes.back().rct_signatures, rct::sk2rct(amount_key), o, rct_tx_mask, hw::get_device("default"));
amount = rct::decodeRct(sigs, rct::sk2rct(amount_key), o, rct_tx_mask, hw::get_device("default"));
total_amount_encoded += amount;
}
@ -345,7 +344,7 @@ bool gen_bp_tx_valid_1_old::generate(std::vector<test_event_entry>& events) cons
// wallet never produces such a thing):
const uint64_t amounts_paid[] = {MK_COINS(120), (uint64_t)-1};
const size_t bp_sizes[] = {1, (size_t)-1};
const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 2 } };
const rct::RCTConfig rct_config[] = { { rct::RangeProofType::PaddedBulletproof, 0 } };
return generate_with(events, 1, amounts_paid, true, rct_config, HF_VERSION_MIN_2_OUTPUTS-1, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_1_before_clsag"); });
}
@ -355,22 +354,15 @@ bool gen_bp_tx_invalid_1_new::generate(std::vector<test_event_entry>& events) co
// that is tested elsewhere).
const uint64_t amounts_paid[] = {10000, (uint64_t)-1};
const size_t bp_sizes[] = {1, (size_t)-1};
const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 2 } };
const rct::RCTConfig rct_config[] = { { rct::RangeProofType::PaddedBulletproof, 0 } };
return generate_with(events, 1, amounts_paid, false, rct_config, HF_VERSION_MIN_2_OUTPUTS, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_invalid_1_from_clsag"); });
}
bool gen_bp_tx_invalid_1_1::generate(std::vector<test_event_entry>& events) const
{
const uint64_t amounts_paid[] = {5, 5, (uint64_t)-1};
const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof , 0 } };
return generate_with(events, 1, amounts_paid, false, rct_config, 0, NULL, NULL);
}
bool gen_bp_tx_valid_2::generate(std::vector<test_event_entry>& events) const
{
const uint64_t amounts_paid[] = {MK_COINS(60), MK_COINS(60), (uint64_t)-1};
const size_t bp_sizes[] = {2, (size_t)-1};
const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 0 } };
const rct::RCTConfig rct_config[] = { { rct::RangeProofType::PaddedBulletproof, 0 } };
return generate_with(events, 1, amounts_paid, true, rct_config, 0, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_2"); });
}
@ -379,7 +371,7 @@ bool gen_bp_tx_valid_3::generate(std::vector<test_event_entry>& events) const
// const uint64_t amounts_paid[] = {50, 50, 50, (uint64_t)-1};
const uint64_t amounts_paid[] = {MK_COINS(40), MK_COINS(40), MK_COINS(40), (uint64_t)-1};
const size_t bp_sizes[] = {4, (size_t)-1};
const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof , 0 } };
const rct::RCTConfig rct_config[] = { { rct::RangeProofType::PaddedBulletproof , 0 } };
return generate_with(events, 1, amounts_paid, true, rct_config, 0, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_3"); });
}
@ -388,95 +380,35 @@ bool gen_bp_tx_valid_16::generate(std::vector<test_event_entry>& events) const
// const uint64_t amounts_paid[] = {5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, (uint64_t)-1};
const uint64_t amounts_paid[] = {MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), (uint64_t)-1};
const size_t bp_sizes[] = {16, (size_t)-1};
const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof , 0 } };
const rct::RCTConfig rct_config[] = { { rct::RangeProofType::PaddedBulletproof , 0 } };
return generate_with(events, 1, amounts_paid, true, rct_config, 0, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_16"); });
}
bool gen_bp_tx_invalid_4_2_1::generate(std::vector<test_event_entry>& events) const
{
const uint64_t amounts_paid[] = {1, 1, 1, 1, 1, 1, 1, (uint64_t)-1};
const rct::RCTConfig rct_config[] = { { rct::RangeProofMultiOutputBulletproof , 0 } };
return generate_with(events, 1, amounts_paid, false, rct_config, 0, NULL, NULL);
}
bool gen_bp_tx_invalid_16_16::generate(std::vector<test_event_entry>& events) const
{
const uint64_t amounts_paid[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, (uint64_t)-1};
const rct::RCTConfig rct_config[] = { { rct::RangeProofMultiOutputBulletproof , 0 } };
return generate_with(events, 1, amounts_paid, false, rct_config, 0, NULL, NULL);
}
bool gen_bp_txs_valid_2_and_2::generate(std::vector<test_event_entry>& events) const
{
//const uint64_t amounts_paid[] = {1000, 1000, (size_t)-1, 1000, 1000, (uint64_t)-1};
const uint64_t amounts_paid[] = {MK_COINS(60), MK_COINS(60), (size_t)-1, MK_COINS(60), MK_COINS(60), (uint64_t)-1};
const size_t bp_sizes[] = {2, (size_t)-1, 2, (size_t)-1};
const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 0 }, {rct::RangeProofPaddedBulletproof, 0 } };
const rct::RCTConfig rct_config[] = { { rct::RangeProofType::PaddedBulletproof, 0 }, {rct::RangeProofType::PaddedBulletproof, 0 } };
return generate_with(events, 2, amounts_paid, true, rct_config, 0, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_txs_valid_2_and_2"); });
}
bool gen_bp_txs_invalid_2_and_8_2_and_16_16_1::generate(std::vector<test_event_entry>& events) const
{
const uint64_t amounts_paid[] = {1, 1, (uint64_t)-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, (uint64_t)-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, (uint64_t)-1};
const rct::RCTConfig rct_config[] = {{rct::RangeProofMultiOutputBulletproof, 0}, {rct::RangeProofMultiOutputBulletproof, 0}, {rct::RangeProofMultiOutputBulletproof, 0}};
return generate_with(events, 3, amounts_paid, false, rct_config, 0, NULL, NULL);
}
bool gen_bp_txs_valid_2_and_3_and_2_and_4::generate(std::vector<test_event_entry>& events) const
{
// const uint64_t amounts_paid[] = {11111115000, 11111115000, (uint64_t)-1, 11111115000, 11111115000, 11111115001, (uint64_t)-1, 11111115000, 11111115002, (uint64_t)-1, 11111115000, 11111115000, 11111115000, 11111115003, (uint64_t)-1};
const uint64_t amounts_paid[] = {MK_COINS(60), MK_COINS(60), (uint64_t)-1, MK_COINS(40), MK_COINS(40), MK_COINS(40), (uint64_t)-1, MK_COINS(60), MK_COINS(60), (uint64_t)-1, MK_COINS(30), MK_COINS(30), MK_COINS(30), MK_COINS(30), (uint64_t)-1};
const rct::RCTConfig rct_config[] = {{rct::RangeProofPaddedBulletproof, 0}, {rct::RangeProofPaddedBulletproof, 0}, {rct::RangeProofPaddedBulletproof, 0}, {rct::RangeProofPaddedBulletproof, 0}};
const rct::RCTConfig rct_config[] = {{rct::RangeProofType::PaddedBulletproof, 0}, {rct::RangeProofType::PaddedBulletproof, 0}, {rct::RangeProofType::PaddedBulletproof, 0}, {rct::RangeProofType::PaddedBulletproof, 0}};
const size_t bp_sizes[] = {2, (size_t)-1, 4, (size_t)-1, 2, (size_t)-1, 4, (size_t)-1};
return generate_with(events, 4, amounts_paid, true, rct_config, 0, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx) { return check_bp(tx, tx_idx, bp_sizes, "gen_bp_txs_valid_2_and_3_and_2_and_4"); });
}
bool gen_bp_tx_invalid_not_enough_proofs::generate(std::vector<test_event_entry>& events) const
{
DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_not_enough_proofs");
const uint64_t amounts_paid[] = {5, 5, (uint64_t)-1};
const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof, 0 } };
return generate_with(events, 1, amounts_paid, false, rct_config, 0, NULL, [&](cryptonote::transaction &tx, size_t idx){
CHECK_TEST_CONDITION(rct::is_rct_bulletproof(tx.rct_signatures.type));
CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty());
tx.rct_signatures.p.bulletproofs.pop_back();
CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty());
return true;
});
}
bool gen_bp_tx_invalid_empty_proofs::generate(std::vector<test_event_entry>& events) const
{
DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_empty_proofs");
const uint64_t amounts_paid[] = {50, 50, (uint64_t)-1};
const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof, 0 } };
return generate_with(events, 1, amounts_paid, false, rct_config, 0, NULL, [&](cryptonote::transaction &tx, size_t idx){
CHECK_TEST_CONDITION(rct::is_rct_bulletproof(tx.rct_signatures.type));
tx.rct_signatures.p.bulletproofs.clear();
return true;
});
}
bool gen_bp_tx_invalid_too_many_proofs::generate(std::vector<test_event_entry>& events) const
{
DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_too_many_proofs");
const uint64_t amounts_paid[] = {10000, (uint64_t)-1};
const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof, 0 } };
return generate_with(events, 1, amounts_paid, false, rct_config, 0, NULL, [&](cryptonote::transaction &tx, size_t idx){
CHECK_TEST_CONDITION(rct::is_rct_bulletproof(tx.rct_signatures.type));
CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty());
tx.rct_signatures.p.bulletproofs.push_back(tx.rct_signatures.p.bulletproofs.back());
return true;
});
}
bool gen_bp_tx_invalid_wrong_amount::generate(std::vector<test_event_entry>& events) const
{
DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_wrong_amount");
const uint64_t amounts_paid[] = {10, (uint64_t)-1};
const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof, 0 } };
const rct::RCTConfig rct_config[] = { { rct::RangeProofType::PaddedBulletproof, 0 } };
return generate_with(events, 1, amounts_paid, false, rct_config, 0, NULL, [&](cryptonote::transaction &tx, size_t idx){
CHECK_TEST_CONDITION(rct::is_rct_bulletproof(tx.rct_signatures.type));
CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty());
@ -485,34 +417,14 @@ bool gen_bp_tx_invalid_wrong_amount::generate(std::vector<test_event_entry>& eve
});
}
bool gen_bp_tx_invalid_borromean_type::generate(std::vector<test_event_entry>& events) const
{
DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_borromean_type");
const uint64_t amounts_paid[] = {5, 5, (uint64_t)-1};
const rct::RCTConfig rct_config[] = { { rct::RangeProofBorromean, 0 } };
return generate_with(events, 1, amounts_paid, false, rct_config, HF_VERSION_CLSAG-1, NULL, [&](cryptonote::transaction &tx, size_t tx_idx){
return true;
});
}
bool gen_bp_tx_invalid_bulletproof2_type::generate(std::vector<test_event_entry>& events) const
{
DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_bulletproof2_type");
const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 2 } };
return generate_with(events, 1, amounts_paid, false, rct_config, 0, NULL, [&](cryptonote::transaction &tx, size_t tx_idx){
return true;
}, 10 /* extra blocks before MLSAGs aren't accepted */);
}
bool gen_rct2_tx_clsag_malleability::generate(std::vector<test_event_entry>& events) const
{
DEFINE_TESTS_ERROR_CONTEXT("gen_rct_tx_clsag_malleability");
const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 3 } };
const rct::RCTConfig rct_config[] = { { rct::RangeProofType::PaddedBulletproof, 3 } };
return generate_with(events, 1, amounts_paid, false, rct_config, 0, NULL, [&](cryptonote::transaction &tx, size_t tx_idx) {
CHECK_TEST_CONDITION(tx.version == cryptonote::txversion::v4_tx_types);
CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeCLSAG);
CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTType::CLSAG);
CHECK_TEST_CONDITION(!tx.rct_signatures.p.CLSAGs.empty());
rct::key x;
CHECK_TEST_CONDITION(tools::hex_to_type("c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", x));

View File

@ -104,11 +104,6 @@ struct gen_bp_tx_invalid_1_new : public gen_bp_tx_validation_base
bool generate(std::vector<test_event_entry>& events) const;
};
struct gen_bp_tx_invalid_1_1 : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
struct gen_bp_tx_valid_2 : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
@ -124,61 +119,21 @@ struct gen_bp_tx_valid_16 : public gen_bp_tx_validation_base
bool generate(std::vector<test_event_entry>& events) const;
};
struct gen_bp_tx_invalid_4_2_1 : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
struct gen_bp_tx_invalid_16_16 : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
struct gen_bp_txs_valid_2_and_2 : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
struct gen_bp_txs_invalid_2_and_8_2_and_16_16_1 : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
struct gen_bp_txs_valid_2_and_3_and_2_and_4 : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
struct gen_bp_tx_invalid_not_enough_proofs : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
struct gen_bp_tx_invalid_empty_proofs : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
struct gen_bp_tx_invalid_too_many_proofs : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
struct gen_bp_tx_invalid_wrong_amount : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
struct gen_bp_tx_invalid_borromean_type : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
struct gen_bp_tx_invalid_bulletproof2_type : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
struct gen_rct2_tx_clsag_malleability : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;

View File

@ -28,6 +28,7 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include <iterator>
#include <limits>
#include <vector>
#include <iostream>
@ -39,6 +40,7 @@
#include <fstream>
#include "common/string_util.h"
#include "common/varint.h"
#include "epee/console_handler.h"
#include "common/rules.h"
@ -69,24 +71,20 @@ void loki_register_callback(std::vector<test_event_entry> &events,
}
std::vector<std::pair<uint8_t, uint64_t>>
loki_generate_sequential_hard_fork_table(uint8_t max_hf_version, uint64_t pos_delay)
loki_generate_hard_fork_table(uint8_t hf_version, uint64_t pos_delay)
{
assert(max_hf_version < cryptonote::network_version_count);
std::vector<std::pair<uint8_t, uint64_t>> result = {};
uint64_t version_height = 0;
// HF15 reduces and HF16 eliminates miner block rewards, so we need to ensure we have enough
// pre-HF15 blocks to generate enough LOKI for tests:
bool delayed = false;
for (uint8_t version = cryptonote::network_version_7; version <= max_hf_version; version++)
{
if (version >= cryptonote::network_version_15_lns && !delayed)
{
assert(hf_version < cryptonote::network_version_count);
// We always need block 0 == v7 for the genesis block:
std::vector<std::pair<uint8_t, uint64_t>> result{{cryptonote::network_version_7, 0}};
uint64_t version_height = 1;
// HF15 reduces and HF16+ eliminates miner block rewards, so we need to ensure we have enough
// HF14 blocks to generate enough LOKI for tests:
if (hf_version > cryptonote::network_version_14_blink) {
result.emplace_back(cryptonote::network_version_14_blink, version_height);
version_height += pos_delay;
delayed = true;
}
result.emplace_back(version, version_height++);
}
result.emplace_back(hf_version, version_height);
return result;
}
@ -325,9 +323,7 @@ void loki_chain_generator::add_transfer_unlock_blocks()
void loki_chain_generator::add_tx(cryptonote::transaction const &tx, bool can_be_added_to_blockchain, std::string const &fail_msg, bool kept_by_block)
{
loki_transaction tx_entry = {tx, kept_by_block};
loki_blockchain_addable<loki_transaction> entry = {std::move(tx_entry), can_be_added_to_blockchain, fail_msg};
events_.push_back(entry);
events_.emplace_back(loki_blockchain_addable<loki_transaction>{{tx, kept_by_block}, can_be_added_to_blockchain, fail_msg});
}
cryptonote::transaction
@ -386,6 +382,20 @@ cryptonote::transaction loki_chain_generator::create_and_add_tx(const cryptonote
return t;
}
cryptonote::transaction loki_chain_generator::create_and_add_big_tx(
const cryptonote::account_base &src,
const cryptonote::account_public_address &dest,
uint64_t junk_size,
uint64_t amount,
uint64_t fee,
bool kept_by_block)
{
cryptonote::transaction t = create_tx(src, dest, amount, fee);
loki_tx_builder(events_, t, db_.blocks.back().block, src, dest, amount, hf_version_).with_fee(fee).with_junk(junk_size).build();
add_tx(t, true /*can_be_added_to_blockchain*/, ""/*fail_msg*/, kept_by_block);
return t;
}
cryptonote::transaction loki_chain_generator::create_and_add_state_change_tx(service_nodes::new_state state, const crypto::public_key &pub_key, uint64_t height, const std::vector<uint64_t> &voters, uint64_t fee, bool kept_by_block)
{
cryptonote::transaction result = create_state_change_tx(state, pub_key, height, voters, fee);
@ -664,8 +674,12 @@ cryptonote::transaction loki_chain_generator::create_loki_name_system_tx_update(
if (!signature)
{
signature = &signature_;
crypto::hash hash = lns::tx_extra_signature_hash(encrypted_value.to_view(), owner, backup_owner, prev_txid);
*signature = lns::make_monero_signature(hash, src.get_keys().m_account_address.m_spend_public_key, src.get_keys().m_spend_secret_key);
auto data = lns::tx_extra_signature(encrypted_value.to_view(), owner, backup_owner, prev_txid);
crypto::hash hash{};
if (!data.empty())
crypto_generichash(reinterpret_cast<unsigned char*>(hash.data), sizeof(hash), reinterpret_cast<const unsigned char*>(data.data()), data.size(), nullptr, 0);
generate_signature(hash, src.get_keys().m_account_address.m_spend_public_key, src.get_keys().m_spend_secret_key, signature->monero);
signature->type = lns::generic_owner_sig_type::monero;
}
std::vector<uint8_t> extra;
@ -1485,12 +1499,12 @@ uint64_t get_amount(const cryptonote::account_base& account, const cryptonote::t
{
if (rct::is_rct_simple(tx.rct_signatures.type))
money_transferred = rct::decodeRctSimple(tx.rct_signatures, rct::sk2rct(scalar1), i, mask, hwdev);
else if (tx.rct_signatures.type == rct::RCTTypeFull)
else if (tx.rct_signatures.type == rct::RCTType::Full)
money_transferred = rct::decodeRct(tx.rct_signatures, rct::sk2rct(scalar1), i, hwdev);
else if (tx.rct_signatures.type == rct::RCTTypeNull)
else if (tx.rct_signatures.type == rct::RCTType::Null)
money_transferred = tx.vout[i].amount;
else {
LOG_PRINT_L0(__func__ << ": Unsupported rct type: " << +tx.rct_signatures.type);
LOG_PRINT_L0(__func__ << ": Unsupported rct type: " << (int)tx.rct_signatures.type);
return 0;
}
}
@ -1715,7 +1729,7 @@ bool fill_tx_sources(std::vector<cryptonote::tx_source_entry>& sources, const st
{
rct::decodeRctSimple(tx.rct_signatures, rct::sk2rct(amount_key), oi.out_no, ts.mask, hw::get_device("default"));
}
else if (tx.rct_signatures.type == rct::RCTTypeFull)
else if (tx.rct_signatures.type == rct::RCTType::Full)
{
rct::decodeRct(tx.rct_signatures, rct::sk2rct(amount_key), oi.out_no, ts.mask, hw::get_device("default"));
}

View File

@ -50,6 +50,7 @@
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_config.h"
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_basic/cryptonote_boost_serialization.h"
#include "cryptonote_protocol/quorumnet.h"
@ -507,15 +508,15 @@ uint64_t sum_amount(const std::vector<cryptonote::tx_source_entry>& sources);
bool construct_tx_to_key(const std::vector<test_event_entry>& events, cryptonote::transaction& tx,
const cryptonote::block& blk_head, const cryptonote::account_base& from, const var_addr_t& to, uint64_t amount,
uint64_t fee, size_t nmix, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0);
uint64_t fee, size_t nmix, rct::RangeProofType range_proof_type=rct::RangeProofType::Borromean, int bp_version = 0);
bool construct_tx_to_key(const std::vector<test_event_entry>& events, cryptonote::transaction& tx, const cryptonote::block& blk_head,
const cryptonote::account_base& from, std::vector<cryptonote::tx_destination_entry> destinations,
uint64_t fee, size_t nmix, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0);
uint64_t fee, size_t nmix, rct::RangeProofType range_proof_type=rct::RangeProofType::Borromean, int bp_version = 0);
bool construct_tx_to_key(cryptonote::transaction& tx, const cryptonote::account_base& from, const var_addr_t& to, uint64_t amount,
std::vector<cryptonote::tx_source_entry> &sources,
uint64_t fee, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0);
uint64_t fee, rct::RangeProofType range_proof_type=rct::RangeProofType::Borromean, int bp_version = 0);
bool construct_tx_to_key(cryptonote::transaction& tx, const cryptonote::account_base& from, const std::vector<cryptonote::tx_destination_entry>& destinations,
std::vector<cryptonote::tx_source_entry> &sources,
@ -530,7 +531,7 @@ bool construct_tx_rct(const cryptonote::account_keys& sender_account_keys,
const std::vector<cryptonote::tx_destination_entry>& destinations,
const std::optional<cryptonote::tx_destination_entry>& change_addr,
std::vector<uint8_t> extra, cryptonote::transaction& tx, uint64_t unlock_time,
rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0);
rct::RangeProofType range_proof_type=rct::RangeProofType::Borromean, int bp_version = 0);
uint64_t num_blocks(const std::vector<test_event_entry>& events);
@ -842,13 +843,15 @@ public:
{
log_event("loki_blockchain_addable<loki_transaction>");
cryptonote::tx_verification_context tvc = {};
size_t pool_size = m_c.get_pool().get_transactions_count();
size_t pool_size = m_c.get_pool().get_transactions_count();
cryptonote::tx_pool_options opts;
opts.kept_by_block = entry.data.kept_by_block;
m_c.handle_incoming_tx(t_serializable_object_to_blob(entry.data.tx), tvc, opts);
bool added = (pool_size + 1) == m_c.get_pool().get_transactions_count();
CHECK_AND_NO_ASSERT_MES(added == entry.can_be_added_to_blockchain, false, (entry.fail_msg.size() ? entry.fail_msg : "Failed to add transaction (no reason given)"));
CHECK_AND_NO_ASSERT_MES(added == entry.can_be_added_to_blockchain, false, (entry.fail_msg.size() ? entry.fail_msg :
entry.can_be_added_to_blockchain ? "Failed to add transaction that should have been accepted" : "TX adding should have failed, but didn't"));
return true;
}
@ -1090,7 +1093,7 @@ inline bool do_replay_file(const std::string& filename)
#define MAKE_TX_MIX_RCT(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \
cryptonote::transaction TX_NAME; \
construct_tx_to_key(VEC_EVENTS, TX_NAME, HEAD, FROM, TO, AMOUNT, TESTS_DEFAULT_FEE, NMIX, rct::RangeProofPaddedBulletproof); \
construct_tx_to_key(VEC_EVENTS, TX_NAME, HEAD, FROM, TO, AMOUNT, TESTS_DEFAULT_FEE, NMIX, rct::RangeProofType::PaddedBulletproof); \
VEC_EVENTS.push_back(TX_NAME);
#define MAKE_TX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, HEAD) MAKE_TX_MIX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, 9, HEAD)
@ -1105,7 +1108,7 @@ inline bool do_replay_file(const std::string& filename)
#define MAKE_TX_MIX_LIST_RCT(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \
MAKE_TX_MIX_LIST_RCT_EX(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD, rct::RangeProofPaddedBulletproof, 1)
MAKE_TX_MIX_LIST_RCT_EX(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD, rct::RangeProofType::PaddedBulletproof, 1)
#define MAKE_TX_MIX_LIST_RCT_EX(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD, RCT_TYPE, BP_VER) \
{ \
cryptonote::transaction t; \
@ -1115,7 +1118,7 @@ inline bool do_replay_file(const std::string& filename)
}
#define MAKE_TX_MIX_DEST_LIST_RCT(VEC_EVENTS, SET_NAME, FROM, TO, NMIX, HEAD) \
MAKE_TX_MIX_DEST_LIST_RCT_EX(VEC_EVENTS, SET_NAME, FROM, TO, NMIX, HEAD, rct::RangeProofPaddedBulletproof, 1)
MAKE_TX_MIX_DEST_LIST_RCT_EX(VEC_EVENTS, SET_NAME, FROM, TO, NMIX, HEAD, rct::RangeProofType::PaddedBulletproof, 1)
#define MAKE_TX_MIX_DEST_LIST_RCT_EX(VEC_EVENTS, SET_NAME, FROM, TO, NMIX, HEAD, RCT_TYPE, BP_VER) \
{ \
cryptonote::transaction t; \
@ -1260,6 +1263,16 @@ inline bool do_replay_file(const std::string& filename)
#define CHECK_NOT_EQ(v1, v2) CHECK_AND_ASSERT_MES(!(v1 == v2), false, "[" << perr_context << "] failed: \"" << QUOTEME(v1) << " != " << QUOTEME(v2) << "\", " << v1 << " == " << v2)
#define MK_COINS(amount) (UINT64_C(amount) * COIN)
static std::string make_junk() {
std::string junk;
junk.reserve(1024);
for (size_t i = 0; i < 256; i++)
junk += (char) i;
junk += junk;
junk += junk;
return junk;
}
//
// NOTE: Loki
//
@ -1275,6 +1288,7 @@ class loki_tx_builder {
uint64_t m_amount;
uint64_t m_fee;
uint64_t m_unlock_time;
uint64_t m_junk_size = 0;
std::vector<uint8_t> m_extra;
cryptonote::loki_construct_tx_params m_tx_params;
@ -1321,6 +1335,11 @@ public:
return std::move(*this);
}
loki_tx_builder&& with_junk(size_t size) {
m_junk_size = size;
return std::move(*this);
}
~loki_tx_builder() {
if (!m_finished) {
std::cerr << "Tx building not finished\n";
@ -1328,6 +1347,8 @@ public:
}
}
inline static const std::string junk1k = make_junk();
bool build()
{
m_finished = true;
@ -1349,6 +1370,20 @@ public:
m_events, m_head, m_from, m_to, m_amount, m_fee, nmix, sources, destinations, &change_amount);
}
if (m_junk_size > 0) {
std::string junk;
junk.reserve(m_junk_size + 10);
tools::write_varint(std::back_inserter(junk), m_junk_size);
m_junk_size += junk.size(); // we just added some bytes for the varint
std::string_view junk_piece{junk1k};
while (junk.size() < m_junk_size) {
if (junk.size() + junk_piece.size() > m_junk_size)
junk_piece = junk_piece.substr(0, m_junk_size - junk.size());
junk += junk_piece;
}
cryptonote::add_tagged_data_to_tx_extra(m_extra, cryptonote::TX_EXTRA_MYSTERIOUS_MINERGATE_TAG, junk);
}
cryptonote::tx_destination_entry change_addr{ change_amount, m_from.get_keys().m_account_address, false /*is_subaddr*/ };
bool result = cryptonote::construct_tx(
m_from.get_keys(), sources, destinations, change_addr, m_extra, m_tx, m_unlock_time, m_tx_params);
@ -1356,9 +1391,9 @@ public:
}
};
void fill_nonce_with_loki_generator (struct loki_chain_generator const *generator, cryptonote::block& blk, const cryptonote::difficulty_type& diffic, uint64_t height);
void loki_register_callback (std::vector<test_event_entry> &events, std::string const &callback_name, loki_callback callback);
std::vector<std::pair<uint8_t, uint64_t>> loki_generate_sequential_hard_fork_table(uint8_t max_hf_version = cryptonote::network_version_count - 1, uint64_t pos_delay = 60);
void fill_nonce_with_loki_generator(struct loki_chain_generator const *generator, cryptonote::block& blk, const cryptonote::difficulty_type& diffic, uint64_t height);
void loki_register_callback(std::vector<test_event_entry> &events, std::string const &callback_name, loki_callback callback);
std::vector<std::pair<uint8_t, uint64_t>> loki_generate_hard_fork_table(uint8_t hf_version = cryptonote::network_version_count - 1, uint64_t pos_delay = 60);
struct loki_blockchain_entry
{
@ -1464,14 +1499,16 @@ struct loki_chain_generator
cryptonote::transaction create_and_add_loki_name_system_tx_renew(cryptonote::account_base const &src, uint8_t hf_version, lns::mapping_type type, std::string const &name, bool kept_by_block = false);
cryptonote::transaction create_and_add_tx (const cryptonote::account_base& src, const cryptonote::account_public_address& dest, uint64_t amount, uint64_t fee = TESTS_DEFAULT_FEE, bool kept_by_block = false);
cryptonote::transaction create_and_add_state_change_tx(service_nodes::new_state state, const crypto::public_key& pub_key, uint64_t height = -1, const std::vector<uint64_t>& voters = {}, uint64_t fee = 0, bool kept_by_block = false);
cryptonote::transaction create_and_add_registration_tx(const cryptonote::account_base& src, const cryptonote::keypair& sn_keys = cryptonote::keypair::generate(hw::get_device("default")), bool kept_by_block = false);
cryptonote::transaction create_and_add_registration_tx(const cryptonote::account_base& src, const cryptonote::keypair& sn_keys = cryptonote::keypair{hw::get_device("default")}, bool kept_by_block = false);
cryptonote::transaction create_and_add_staking_tx (const crypto::public_key &pub_key, const cryptonote::account_base &src, uint64_t amount, bool kept_by_block = false);
loki_blockchain_entry &create_and_add_next_block (const std::vector<cryptonote::transaction>& txs = {}, cryptonote::checkpoint_t const *checkpoint = nullptr, bool can_be_added_to_blockchain = true, std::string const &fail_msg = {});
// Same as create_and_add_tx, but also adds 95kB of junk into tx_extra to bloat up the tx size.
cryptonote::transaction create_and_add_big_tx(const cryptonote::account_base& src, const cryptonote::account_public_address& dest, uint64_t amount, uint64_t junk_size = 95000, uint64_t fee = TESTS_DEFAULT_FEE, bool kept_by_block = false);
// NOTE: Create transactions but don't add to events_
cryptonote::transaction create_tx(const cryptonote::account_base &src, const cryptonote::account_public_address &dest, uint64_t amount, uint64_t fee) const;
cryptonote::transaction create_registration_tx(const cryptonote::account_base &src,
const cryptonote::keypair &service_node_keys = cryptonote::keypair::generate(hw::get_device("default")),
const cryptonote::keypair &service_node_keys = cryptonote::keypair{hw::get_device("default")},
uint64_t src_portions = STAKING_PORTIONS,
uint64_t src_operator_cut = 0,
std::array<loki_service_node_contribution, 3> const &contributors = {},

View File

@ -54,6 +54,9 @@ int main(int argc, char* argv[])
tools::on_startup();
epee::string_tools::set_module_name_and_folder(argv[0]);
// Bypass tx version checks for core tests:
cryptonote::hack::test_suite_permissive_txes = true;
//set up logging options
mlog_configure(mlog_get_default_log_path("core_tests.log"), true);
@ -207,7 +210,6 @@ int main(int argc, char* argv[])
// as well because they special case and run under very different code
// paths from the regular tx path
// Transaction verification tests
GENERATE_AND_PLAY(gen_tx_big_version);
GENERATE_AND_PLAY(gen_tx_unlock_time);
GENERATE_AND_PLAY(gen_tx_input_is_not_txin_to_key);
GENERATE_AND_PLAY(gen_tx_no_inputs_no_outputs);
@ -243,21 +245,12 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(gen_bp_tx_valid_1_old);
GENERATE_AND_PLAY(gen_bp_tx_invalid_1_new);
GENERATE_AND_PLAY(gen_bp_tx_invalid_1_1);
GENERATE_AND_PLAY(gen_bp_tx_valid_2);
GENERATE_AND_PLAY(gen_bp_tx_valid_3);
GENERATE_AND_PLAY(gen_bp_tx_valid_16);
GENERATE_AND_PLAY(gen_bp_tx_invalid_4_2_1);
GENERATE_AND_PLAY(gen_bp_tx_invalid_16_16);
GENERATE_AND_PLAY(gen_bp_txs_valid_2_and_2);
GENERATE_AND_PLAY(gen_bp_txs_invalid_2_and_8_2_and_16_16_1);
GENERATE_AND_PLAY(gen_bp_txs_valid_2_and_3_and_2_and_4);
GENERATE_AND_PLAY(gen_bp_tx_invalid_not_enough_proofs);
GENERATE_AND_PLAY(gen_bp_tx_invalid_empty_proofs);
GENERATE_AND_PLAY(gen_bp_tx_invalid_too_many_proofs);
GENERATE_AND_PLAY(gen_bp_tx_invalid_wrong_amount);
GENERATE_AND_PLAY(gen_bp_tx_invalid_borromean_type);
GENERATE_AND_PLAY(gen_bp_tx_invalid_bulletproof2_type);
GENERATE_AND_PLAY(gen_rct2_tx_clsag_malleability);

View File

@ -38,7 +38,7 @@ using namespace cryptonote;
bool gen_double_spend_in_tx::generate(std::vector<test_event_entry>& events) const
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
gen.add_n_blocks(20);
@ -113,7 +113,7 @@ bool gen_double_spend_in_tx::generate(std::vector<test_event_entry>& events) con
bool gen_double_spend_in_the_same_block::generate(std::vector<test_event_entry>& events) const
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
gen.add_n_blocks(10);
@ -156,7 +156,7 @@ bool gen_double_spend_in_the_same_block::generate(std::vector<test_event_entry>&
bool gen_double_spend_in_different_blocks::generate(std::vector<test_event_entry>& events) const
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
gen.add_n_blocks(10);
@ -199,7 +199,7 @@ bool gen_double_spend_in_different_blocks::generate(std::vector<test_event_entry
bool gen_double_spend_in_alt_chain_in_the_same_block::generate(std::vector<test_event_entry>& events) const
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
gen.add_n_blocks(10);
@ -243,7 +243,7 @@ bool gen_double_spend_in_alt_chain_in_the_same_block::generate(std::vector<test_
bool gen_double_spend_in_alt_chain_in_different_blocks::generate(std::vector<test_event_entry>& events) const
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
gen.add_n_blocks(10);
@ -284,7 +284,7 @@ bool gen_double_spend_in_alt_chain_in_different_blocks::generate(std::vector<tes
bool gen_double_spend_in_different_chains::generate(std::vector<test_event_entry>& events) const
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table(cryptonote::network_version_15_lns);
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
gen.add_n_blocks(10);
@ -293,19 +293,33 @@ bool gen_double_spend_in_different_chains::generate(std::vector<test_event_entry
uint64_t amount = MK_COINS(10);
cryptonote::account_base const &miner = gen.first_miner_;
cryptonote::account_base bob = gen.add_account();
// These two transactions are meant to conflict:
cryptonote::transaction tx_1 = gen.create_tx(miner, bob.get_keys().m_account_address, amount, TESTS_DEFAULT_FEE);
cryptonote::transaction tx_2 = gen.create_tx(miner, bob.get_keys().m_account_address, amount, TESTS_DEFAULT_FEE);
// Create a fork:
auto fork = gen;
// Add one tx to chain 1:
fork.add_event_msg("Add original tx to main chain");
gen.add_tx(tx_1, true /*can_be_added_to_blockchain*/, "", true /*kept_by_block*/);
fork.add_tx(tx_2, true /*can_be_added_to_blockchain*/, "", true /*kept_by_block*/);
gen.create_and_add_next_block({tx_1});
auto tx1_hash = get_transaction_hash(tx_1);
// Add the other to chain 2, and then put another block so that it is longer and we reorg:
fork.add_event_msg("Add double spend tx to alt chain");
fork.add_tx(tx_2, true /*can_be_added_to_blockchain*/, "", true /*kept_by_block*/);
fork.create_and_add_next_block({tx_2});
fork.add_event_msg("Add new block to fork to cause a reorg to the alt chain with a double spending transaction");
fork.create_and_add_next_block();
// Add some more just to make sure the double-spend tx doesn't get mined
fork.create_and_add_next_block();
fork.create_and_add_next_block();
crypto::hash block_hash = cryptonote::get_block_hash(fork.top().block);
loki_register_callback(events, "check_top_block", [block_hash](cryptonote::core &c, size_t ev_index)
loki_register_callback(events, "check_top_block", [block_hash, tx1_hash](cryptonote::core &c, size_t ev_index)
{
DEFINE_TESTS_ERROR_CONTEXT("check_txpool");
uint64_t top_height;
@ -313,9 +327,10 @@ bool gen_double_spend_in_different_chains::generate(std::vector<test_event_entry
c.get_blockchain_top(top_height, top_hash);
CHECK_EQ(top_hash, block_hash);
// TODO(loki): This is questionable behaviour, currently we keep alt chains even after switching over
CHECK_EQ(c.get_pool().get_transactions_count(), 1);
CHECK_EQ(c.get_alternative_blocks_count(), 1);
std::vector<transaction> mempool;
c.get_pool().get_transactions(mempool);
CHECK_EQ(mempool.size(), 1);
CHECK_EQ(get_transaction_hash(mempool[0]), tx1_hash);
return true;
});
return true;

View File

@ -97,7 +97,7 @@ bool gen_uint_overflow_base::mark_last_valid_block(cryptonote::core& c, size_t e
bool gen_uint_overflow_1::generate(std::vector<test_event_entry>& events) const
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);

View File

@ -31,6 +31,7 @@
#include "loki_tests.h"
#include "common/string_util.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_basic/tx_extra.h"
#include "cryptonote_config.h"
#include "cryptonote_core/loki_name_system.h"
#include "cryptonote_core/service_node_list.h"
@ -67,7 +68,7 @@ static void add_service_nodes(loki_chain_generator &gen, size_t count)
// code path" again
bool loki_checkpointing_alt_chain_handle_alt_blocks_at_tip::generate(std::vector<test_event_entry>& events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
@ -126,7 +127,7 @@ bool loki_checkpointing_alt_chain_handle_alt_blocks_at_tip::generate(std::vector
// NOTE: - Checks that a chain with a checkpoint but less PoW is preferred over a chain that is longer with more PoW but no checkpoints
bool loki_checkpointing_alt_chain_more_service_node_checkpoints_less_pow_overtakes::generate(std::vector<test_event_entry>& events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
@ -163,7 +164,7 @@ bool loki_checkpointing_alt_chain_more_service_node_checkpoints_less_pow_overtak
// NOTE: - A chain that receives checkpointing votes sufficient to form a checkpoint should reorg back accordingly
bool loki_checkpointing_alt_chain_receive_checkpoint_votes_should_reorg_back::generate(std::vector<test_event_entry>& events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
@ -230,7 +231,7 @@ bool loki_checkpointing_alt_chain_receive_checkpoint_votes_should_reorg_back::ge
bool loki_checkpointing_alt_chain_too_old_should_be_dropped::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
gen.add_mined_money_unlock_blocks();
@ -265,7 +266,7 @@ bool loki_checkpointing_alt_chain_too_old_should_be_dropped::generate(std::vecto
// available checkpoint heights whilst maintaining equal heights with the main chain
bool loki_checkpointing_alt_chain_with_increasing_service_node_checkpoints::generate(std::vector<test_event_entry>& events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
@ -325,7 +326,7 @@ bool loki_checkpointing_alt_chain_with_increasing_service_node_checkpoints::gene
// - Checks invalid vote (signature or key) is not accepted due to not being part of the quorum
bool loki_checkpointing_service_node_checkpoint_from_votes::generate(std::vector<test_event_entry>& events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
@ -346,7 +347,7 @@ bool loki_checkpointing_service_node_checkpoint_from_votes::generate(std::vector
// NOTE: Submit invalid vote using service node keys not in the quorum
{
const cryptonote::keypair invalid_kp = cryptonote::keypair::generate(hw::get_device("default"));
const cryptonote::keypair invalid_kp{hw::get_device("default")};
service_nodes::service_node_keys invalid_keys;
invalid_keys.pub = invalid_kp.pub;
invalid_keys.key = invalid_kp.sec;
@ -389,7 +390,7 @@ bool loki_checkpointing_service_node_checkpoint_from_votes::generate(std::vector
// - Checks you can add a block after the 1st checkpoint out of 2 checkpoints.
bool loki_checkpointing_service_node_checkpoints_check_reorg_windows::generate(std::vector<test_event_entry>& events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
@ -436,7 +437,7 @@ bool loki_checkpointing_service_node_checkpoints_check_reorg_windows::generate(s
bool loki_core_block_reward_unpenalized_pre_pulse::generate(std::vector<test_event_entry>& events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table(cryptonote::network_version_16_pulse - 1);
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table(cryptonote::network_version_16_pulse - 1);
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
@ -446,10 +447,11 @@ bool loki_core_block_reward_unpenalized_pre_pulse::generate(std::vector<test_eve
gen.add_mined_money_unlock_blocks();
cryptonote::account_base dummy = gen.add_account();
int constexpr NUM_TXS = 60;
std::vector<cryptonote::transaction> txs(NUM_TXS);
for (int i = 0; i < NUM_TXS; i++)
txs[i] = gen.create_and_add_tx(gen.first_miner_, dummy.get_keys().m_account_address, MK_COINS(5));
int constexpr NUM_TXS = 4;
std::vector<cryptonote::transaction> txs;
txs.reserve(NUM_TXS);
while (txs.size() < NUM_TXS)
txs.push_back(gen.create_and_add_big_tx(gen.first_miner_, dummy.get_keys().m_account_address, 95000, MK_COINS(5)));
gen.create_and_add_next_block(txs);
uint64_t unpenalized_block_reward = cryptonote::block_reward_unpenalized_formula_v8(gen.height());
@ -475,7 +477,7 @@ bool loki_core_block_reward_unpenalized_pre_pulse::generate(std::vector<test_eve
bool loki_core_block_reward_unpenalized_post_pulse::generate(std::vector<test_event_entry>& events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table(cryptonote::network_version_count -1, 150 /*Proof Of Stake Delay*/);
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table(cryptonote::network_version_count -1, 150 /*Proof Of Stake Delay*/);
loki_chain_generator gen(events, hard_forks);
uint8_t const newest_hf = hard_forks.back().first;
@ -487,24 +489,10 @@ bool loki_core_block_reward_unpenalized_post_pulse::generate(std::vector<test_ev
// Make big chunky TX's to trigger the block size penalty
cryptonote::account_base dummy = gen.add_account();
uint64_t tx_fee = 0;
std::vector<cryptonote::transaction> txs(150);
std::vector<cryptonote::transaction> txs(4);
for (size_t i = 0; i < txs.size(); i++)
{
std::array<loki_service_node_contribution, 3> contributions = {};
for (size_t i = 0; i < contributions.size(); i++)
{
loki_service_node_contribution &entry = contributions[i];
entry.contributor = gen.add_account().get_keys().m_account_address;
entry.portions = STAKING_PORTIONS / 4;
}
txs[i] = gen.create_registration_tx(gen.first_miner(),
cryptonote::keypair::generate(hw::get_device("default")),
STAKING_PORTIONS / 4, /*operator portions*/
0, /*operator cut*/
contributions,
3);
gen.add_tx(txs[i], true /*can_be_added_to_blockchain*/, ""/*fail_msg*/, true /*kept_by_block*/);
txs[i] = gen.create_and_add_big_tx(gen.first_miner_, dummy.get_keys().m_account_address, 95000, MK_COINS(5), TESTS_DEFAULT_FEE * 5);
tx_fee += txs[i].rct_signatures.txnFee;
}
gen.create_and_add_next_block(txs);
@ -538,7 +526,7 @@ bool loki_core_block_reward_unpenalized_post_pulse::generate(std::vector<test_ev
bool loki_core_fee_burning::generate(std::vector<test_event_entry>& events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
@ -620,7 +608,7 @@ bool loki_core_fee_burning::generate(std::vector<test_event_entry>& events)
bool loki_core_governance_batched_reward::generate(std::vector<test_event_entry>& events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table(cryptonote::network_version_10_bulletproofs);
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table(cryptonote::network_version_10_bulletproofs);
uint64_t hf10_height = 0;
for (std::pair<uint8_t, uint64_t> hf_pair : hard_forks)
@ -696,7 +684,7 @@ bool loki_core_governance_batched_reward::generate(std::vector<test_event_entry>
bool loki_core_block_rewards_lrc6::generate(std::vector<test_event_entry>& events)
{
constexpr auto& network = cryptonote::get_config(cryptonote::FAKECHAIN);
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table(cryptonote::network_version_15_lns);
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table(cryptonote::network_version_15_lns);
hard_forks.emplace_back(cryptonote::network_version_16_pulse, hard_forks.back().second + network.GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS + 10);
hard_forks.emplace_back(cryptonote::network_version_17, hard_forks.back().second + network.GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS);
loki_chain_generator batched_governance_generator(events, hard_forks);
@ -779,19 +767,24 @@ bool loki_core_block_rewards_lrc6::generate(std::vector<test_event_entry>& event
bool loki_core_test_deregister_preferred::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table(cryptonote::network_version_9_service_nodes);
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
const auto miner = gen.first_miner();
const auto alice = gen.add_account();
gen.add_blocks_until_version(hard_forks.back().first);
gen.add_n_blocks(60); /// give miner some outputs to spend and unlock them
gen.add_mined_money_unlock_blocks();
gen.add_n_blocks(10); /// give miner some outputs to spend and unlock them
add_service_nodes(gen, 12);
gen.add_mined_money_unlock_blocks();
/// generate transactions to fill up txpool entirely
for (auto i = 0u; i < 45; ++i) {
gen.create_and_add_tx(miner, alice.get_keys().m_account_address, MK_COINS(1), TESTS_DEFAULT_FEE * 100);
/// generate high fee transactions with huge fees to fill up txpool entirely. This pushes all the
/// way into the penalty buffer (i.e. produces a 600kB tx). The junk data size here is quite
/// sensitive to tx changes: we need a value that makes the transaction *just* big enough so that
/// 6 transactions fit if no deregs are added, but when we add the deregs we can only fit 5.
/// Expect this test to break (and need some tweaking to the junk size here) on tx structure
/// changes.
for (size_t i = 0; i < 6; ++i) {
gen.create_and_add_big_tx(miner, alice.get_keys().m_account_address, 98450, MK_COINS(1), TESTS_DEFAULT_FEE * 100);
}
/// generate two deregisters
@ -824,7 +817,8 @@ bool loki_core_test_deregister_preferred::generate(std::vector<test_event_entry>
return mtx[tx_hash]->type == cryptonote::txtype::state_change;
});
CHECK_TEST_CONDITION(tx_count > full_blk.tx_hashes.size()); /// test that there are more transactions in tx pool
CHECK_TEST_CONDITION(tx_count == 8);
CHECK_EQ(full_blk.tx_hashes.size(), 7);
CHECK_EQ(deregister_count, 2);
return true;
});
@ -832,18 +826,18 @@ bool loki_core_test_deregister_preferred::generate(std::vector<test_event_entry>
}
// Test if a person registers onto the network and they get included in the nodes to test (i.e. heights 0, 5, 10). If
// they get dereigstered in the nodes to test, height 5, and rejoin the network before height 10 (and are in the nodes
// they get deregistered in the nodes to test, height 5, and rejoin the network before height 10 (and are in the nodes
// to test), they don't get deregistered.
bool loki_core_test_deregister_safety_buffer::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table(cryptonote::network_version_9_service_nodes);
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
const auto miner = gen.first_miner();
gen.add_blocks_until_version(hard_forks.back().first);
gen.add_n_blocks(40); /// give miner some outputs to spend and unlock them
gen.add_mined_money_unlock_blocks();
add_service_nodes(gen, service_nodes::STATE_CHANGE_QUORUM_SIZE * 2 + 1);
gen.add_n_blocks(1);
const auto height_a = gen.height();
std::vector<crypto::public_key> quorum_a = gen.quorum(height_a).obligations->workers;
@ -885,7 +879,7 @@ bool loki_core_test_deregister_safety_buffer::generate(std::vector<test_event_en
// Daemon A accepts the block without X. Now X is too old and should not be added in future blocks.
bool loki_core_test_deregister_too_old::generate(std::vector<test_event_entry>& events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table(cryptonote::network_version_9_service_nodes);
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
@ -893,6 +887,7 @@ bool loki_core_test_deregister_too_old::generate(std::vector<test_event_entry>&
gen.add_n_blocks(20);
gen.add_mined_money_unlock_blocks();
add_service_nodes(gen, 11);
gen.add_n_blocks(1);
const auto pk = gen.top_quorum().obligations->workers[0];
const auto dereg_tx = gen.create_and_add_state_change_tx(service_nodes::new_state::deregister, pk);
@ -910,7 +905,7 @@ bool loki_core_test_deregister_too_old::generate(std::vector<test_event_entry>&
bool loki_core_test_deregister_zero_fee::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
@ -935,7 +930,7 @@ bool loki_core_test_deregister_zero_fee::generate(std::vector<test_event_entry>
// those sitting on Chain 1 should not have problems switching over.
bool loki_core_test_deregister_on_split::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
@ -995,7 +990,7 @@ bool loki_core_test_deregister_on_split::generate(std::vector<test_event_entry>
bool loki_core_test_state_change_ip_penalty_disallow_dupes::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
@ -1057,7 +1052,7 @@ static bool verify_lns_mapping_record(char const *perr_context,
bool loki_name_system_disallow_reserved_type::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
cryptonote::account_base miner = gen.first_miner_;
@ -1109,7 +1104,7 @@ uint64_t lokinet_expiry(lns::mapping_type type) {
bool loki_name_system_expiration::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
cryptonote::account_base miner = gen.first_miner_;
@ -1181,7 +1176,7 @@ bool loki_name_system_expiration::generate(std::vector<test_event_entry> &events
bool loki_name_system_get_mappings_by_owner::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
cryptonote::account_base miner = gen.first_miner_;
@ -1298,7 +1293,7 @@ bool loki_name_system_get_mappings_by_owner::generate(std::vector<test_event_ent
bool loki_name_system_get_mappings_by_owners::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
cryptonote::account_base miner = gen.first_miner_;
@ -1368,7 +1363,7 @@ bool loki_name_system_get_mappings_by_owners::generate(std::vector<test_event_en
bool loki_name_system_get_mappings::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
cryptonote::account_base miner = gen.first_miner_;
@ -1410,7 +1405,7 @@ bool loki_name_system_get_mappings::generate(std::vector<test_event_entry> &even
bool loki_name_system_handles_duplicate_in_lns_db::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
cryptonote::account_base miner = gen.first_miner_;
@ -1487,7 +1482,7 @@ bool loki_name_system_handles_duplicate_in_lns_db::generate(std::vector<test_eve
bool loki_name_system_handles_duplicate_in_tx_pool::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
cryptonote::account_base miner = gen.first_miner_;
@ -1521,7 +1516,7 @@ bool loki_name_system_handles_duplicate_in_tx_pool::generate(std::vector<test_ev
bool loki_name_system_invalid_tx_extra_params::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
cryptonote::account_base miner = gen.first_miner_;
@ -1652,7 +1647,7 @@ bool loki_name_system_invalid_tx_extra_params::generate(std::vector<test_event_e
bool loki_name_system_large_reorg::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
cryptonote::account_base const miner = gen.first_miner_;
@ -1862,7 +1857,7 @@ bool loki_name_system_large_reorg::generate(std::vector<test_event_entry> &event
bool loki_name_system_name_renewal::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
cryptonote::account_base miner = gen.first_miner_;
@ -1945,7 +1940,7 @@ bool loki_name_system_name_renewal::generate(std::vector<test_event_entry> &even
bool loki_name_system_name_value_max_lengths::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
cryptonote::account_base miner = gen.first_miner_;
@ -2015,7 +2010,7 @@ bool loki_name_system_name_value_max_lengths::generate(std::vector<test_event_en
bool loki_name_system_update_mapping_after_expiry_fails::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
cryptonote::account_base miner = gen.first_miner_;
@ -2069,7 +2064,7 @@ uint8_t loki_name_system_update_mapping::hf() { return cryptonote::network_versi
uint8_t loki_name_system_update_mapping_argon2::hf() { return cryptonote::network_version_15_lns; }
bool loki_name_system_update_mapping::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table(hf());
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table(hf());
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
gen.add_mined_money_unlock_blocks();
@ -2130,9 +2125,25 @@ bool loki_name_system_update_mapping::generate(std::vector<test_event_entry> &ev
return true;
}
template <typename... Args>
static crypto::hash lns_signature_hash(Args&&... args) {
crypto::hash hash{};
auto data = lns::tx_extra_signature(std::forward<Args>(args)...);
if (!data.empty())
crypto_generichash(reinterpret_cast<unsigned char*>(hash.data), sizeof(hash), reinterpret_cast<const unsigned char*>(data.data()), data.size(), nullptr, 0);
return hash;
}
lns::generic_signature lns_monero_signature(const crypto::hash& h, const crypto::public_key& pkey, const crypto::secret_key& skey) {
lns::generic_signature result{};
result.type = lns::generic_owner_sig_type::monero;
generate_signature(h, pkey, skey, result.monero);
return result;
}
bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_event_entry>& events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
gen.add_n_blocks(10); /// generate some outputs and unlock them
@ -2173,7 +2184,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
{
lns_keys_t temp_keys = make_lns_keys(gen.add_account());
lns::mapping_value encrypted_value = temp_keys.session_value.make_encrypted(name);
crypto::hash hash = lns::tx_extra_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
crypto::hash hash = lns_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
auto signature = lns::make_ed25519_signature(hash, owner1_key);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::session, name, &encrypted_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
@ -2194,7 +2205,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
{
lns_keys_t temp_keys = make_lns_keys(gen.add_account());
lns::mapping_value encrypted_value = temp_keys.session_value.make_encrypted(name);
crypto::hash hash = lns::tx_extra_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
crypto::hash hash = lns_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
auto signature = lns::make_ed25519_signature(hash, owner2_key);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::session, name, &encrypted_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
@ -2230,8 +2241,8 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
{
lns_keys_t temp_keys = make_lns_keys(gen.add_account());
lns::mapping_value encrypted_value = temp_keys.session_value.make_encrypted(name);
crypto::hash hash = lns::tx_extra_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
auto signature = lns::make_monero_signature(hash, owner1.wallet.address.m_spend_public_key, account1.get_keys().m_spend_secret_key);
crypto::hash hash = lns_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
auto signature = lns_monero_signature(hash, owner1.wallet.address.m_spend_public_key, account1.get_keys().m_spend_secret_key);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::session, name, &encrypted_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
gen.create_and_add_next_block({tx2});
@ -2251,8 +2262,8 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
{
lns_keys_t temp_keys = make_lns_keys(gen.add_account());
lns::mapping_value encrypted_value = temp_keys.session_value.make_encrypted(name);
crypto::hash hash = lns::tx_extra_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
auto signature = lns::make_monero_signature(hash, owner2.wallet.address.m_spend_public_key, account2.get_keys().m_spend_secret_key);
crypto::hash hash = lns_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
auto signature = lns_monero_signature(hash, owner2.wallet.address.m_spend_public_key, account2.get_keys().m_spend_secret_key);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::session, name, &encrypted_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
gen.create_and_add_next_block({tx2});
@ -2291,7 +2302,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
{
lns_keys_t temp_keys = make_lns_keys(gen.add_account());
lns::mapping_value encrypted_value = temp_keys.session_value.make_encrypted(name);
crypto::hash hash = lns::tx_extra_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
crypto::hash hash = lns_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
auto signature = lns::make_ed25519_signature(hash, owner1_key);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::session, name, &encrypted_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
@ -2312,8 +2323,8 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
{
lns_keys_t temp_keys = make_lns_keys(gen.add_account());
lns::mapping_value encrypted_value = temp_keys.session_value.make_encrypted(name);
crypto::hash hash = lns::tx_extra_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
auto signature = lns::make_monero_signature(hash, owner2.wallet.address.m_spend_public_key, account2.get_keys().m_spend_secret_key);
crypto::hash hash = lns_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
auto signature = lns_monero_signature(hash, owner2.wallet.address.m_spend_public_key, account2.get_keys().m_spend_secret_key);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::session, name, &encrypted_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
gen.create_and_add_next_block({tx2});
@ -2352,8 +2363,8 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
lns_keys_t temp_keys = make_lns_keys(gen.add_account());
lns::mapping_value encrypted_value = temp_keys.session_value.make_encrypted(name);
crypto::hash hash = lns::tx_extra_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
auto signature = lns::make_monero_signature(hash, owner1.wallet.address.m_spend_public_key, account1.get_keys().m_spend_secret_key);
crypto::hash hash = lns_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
auto signature = lns_monero_signature(hash, owner1.wallet.address.m_spend_public_key, account1.get_keys().m_spend_secret_key);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::session, name, &encrypted_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
gen.create_and_add_next_block({tx2});
@ -2374,7 +2385,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
lns_keys_t temp_keys = make_lns_keys(gen.add_account());
lns::mapping_value encrypted_value = temp_keys.session_value.make_encrypted(name);
crypto::hash hash = lns::tx_extra_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
crypto::hash hash = lns_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
auto signature = lns::make_ed25519_signature(hash, owner2_key);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::session, name, &encrypted_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
@ -2396,7 +2407,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
bool loki_name_system_update_mapping_non_existent_name_fails::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
gen.add_mined_money_unlock_blocks();
@ -2411,7 +2422,7 @@ bool loki_name_system_update_mapping_non_existent_name_fails::generate(std::vect
bool loki_name_system_update_mapping_invalid_signature::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
gen.add_mined_money_unlock_blocks();
@ -2433,7 +2444,7 @@ bool loki_name_system_update_mapping_invalid_signature::generate(std::vector<tes
bool loki_name_system_update_mapping_replay::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
gen.add_mined_money_unlock_blocks();
@ -2485,7 +2496,7 @@ bool loki_name_system_update_mapping_replay::generate(std::vector<test_event_ent
bool loki_name_system_wrong_burn::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
cryptonote::account_base miner = gen.first_miner_;
gen.add_blocks_until_version(hard_forks.back().first);
@ -2541,7 +2552,7 @@ bool loki_name_system_wrong_burn::generate(std::vector<test_event_entry> &events
bool loki_name_system_wrong_version::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
cryptonote::account_base miner = gen.first_miner_;
@ -2579,7 +2590,7 @@ bool loki_name_system_wrong_version::generate(std::vector<test_event_entry> &eve
// NOTE: Generate forked block, check that alternative quorums are generated and accessible
bool loki_service_nodes_alt_quorums::generate(std::vector<test_event_entry>& events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
@ -2627,7 +2638,7 @@ bool loki_service_nodes_alt_quorums::generate(std::vector<test_event_entry>& eve
bool loki_service_nodes_checkpoint_quorum_size::generate(std::vector<test_event_entry>& events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
@ -2667,7 +2678,7 @@ bool loki_service_nodes_checkpoint_quorum_size::generate(std::vector<test_event_
bool loki_service_nodes_gen_nodes::generate(std::vector<test_event_entry> &events)
{
const std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table(cryptonote::network_version_9_service_nodes);
const std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table(cryptonote::network_version_9_service_nodes);
loki_chain_generator gen(events, hard_forks);
const auto miner = gen.first_miner();
const auto alice = gen.add_account();
@ -2679,7 +2690,7 @@ bool loki_service_nodes_gen_nodes::generate(std::vector<test_event_entry> &event
const auto tx0 = gen.create_and_add_tx(miner, alice.get_keys().m_account_address, MK_COINS(101));
gen.create_and_add_next_block({tx0});
gen.add_mined_money_unlock_blocks();
gen.add_transfer_unlock_blocks();
const auto reg_tx = gen.create_and_add_registration_tx(alice);
gen.create_and_add_next_block({reg_tx});
@ -2688,8 +2699,7 @@ bool loki_service_nodes_gen_nodes::generate(std::vector<test_event_entry> &event
{
DEFINE_TESTS_ERROR_CONTEXT("gen_service_nodes::check_registered");
std::vector<cryptonote::block> blocks;
size_t count = 15 + (2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW);
bool r = c.get_blocks((uint64_t)0, count, blocks);
bool r = c.get_blocks((uint64_t)0, (uint64_t)-1, blocks);
CHECK_TEST_CONDITION(r);
std::vector<cryptonote::block> chain;
map_hash2tx_t mtx;
@ -2705,7 +2715,7 @@ bool loki_service_nodes_gen_nodes::generate(std::vector<test_event_entry> &event
/// check that alice is registered
const auto info_v = c.get_service_node_list_state({});
CHECK_EQ(info_v.empty(), false);
CHECK_EQ(info_v.size(), 1);
return true;
});
@ -2747,11 +2757,9 @@ static bool contains(const std::vector<sn_info_t>& infos, const crypto::public_k
bool loki_service_nodes_test_rollback::generate(std::vector<test_event_entry>& events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table(cryptonote::network_version_9_service_nodes);
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
gen.add_n_blocks(20); /// generate some outputs and unlock them
gen.add_mined_money_unlock_blocks();
add_service_nodes(gen, 11);
gen.add_n_blocks(5); /// create a few blocks with active service nodes
@ -2763,15 +2771,17 @@ bool loki_service_nodes_test_rollback::generate(std::vector<test_event_entry>& e
size_t deregister_index = gen.event_index();
gen.create_and_add_next_block({dereg_tx});
size_t reg_evnt_idx;
/// create a new service node (B) in the next block
{
const auto tx = gen.create_and_add_registration_tx(gen.first_miner());
reg_evnt_idx = gen.event_index();
gen.create_and_add_next_block({tx});
}
fork.add_n_blocks(3); /// create blocks on the alt chain and trigger chain switch
fork.add_n_blocks(15); // create a few more blocks to test winner selection
loki_register_callback(events, "test_registrations", [&events, deregister_index](cryptonote::core &c, size_t ev_index)
loki_register_callback(events, "test_registrations", [&events, deregister_index, reg_evnt_idx](cryptonote::core &c, size_t ev_index)
{
DEFINE_TESTS_ERROR_CONTEXT("test_registrations");
const auto sn_list = c.get_service_node_list_state({});
@ -2799,7 +2809,6 @@ bool loki_service_nodes_test_rollback::generate(std::vector<test_event_entry>& e
/// Test that node B is not registered
{
/// obtain public key of node B
constexpr size_t reg_evnt_idx = 73;
const auto event_b = events.at(reg_evnt_idx);
CHECK_TEST_CONDITION(std::holds_alternative<loki_blockchain_addable<loki_transaction>>(event_b));
const auto reg_tx = var::get<loki_blockchain_addable<loki_transaction>>(event_b);
@ -2953,16 +2962,16 @@ bool loki_service_nodes_test_swarms_basic::generate(std::vector<test_event_entry
bool loki_service_nodes_insufficient_contribution::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
gen.add_mined_money_unlock_blocks();
uint64_t operator_portions = STAKING_PORTIONS / 2;
uint64_t remaining_portions = STAKING_PORTIONS - operator_portions;
cryptonote::keypair sn_keys = cryptonote::keypair::generate(hw::get_device("default"));
cryptonote::transaction register_tx = gen.create_registration_tx(gen.first_miner_, sn_keys, operator_portions);
uint64_t operator_portions = STAKING_PORTIONS / 2;
uint64_t remaining_portions = STAKING_PORTIONS - operator_portions;
cryptonote::keypair sn_keys{hw::get_device("default")};
cryptonote::transaction register_tx = gen.create_registration_tx(gen.first_miner_, sn_keys, operator_portions);
gen.add_tx(register_tx);
gen.create_and_add_next_block({register_tx});
@ -2985,7 +2994,7 @@ bool loki_service_nodes_insufficient_contribution::generate(std::vector<test_eve
static loki_chain_generator setup_pulse_tests(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator result(events, hard_forks);
result.add_blocks_until_version(hard_forks.back().first);
@ -3157,7 +3166,7 @@ bool loki_pulse_reject_miner_block::generate(std::vector<test_event_entry> &even
bool loki_pulse_generate_blocks::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
@ -3181,7 +3190,7 @@ bool loki_pulse_generate_blocks::generate(std::vector<test_event_entry> &events)
bool loki_pulse_fallback_to_pow_and_back::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
@ -3229,7 +3238,7 @@ bool loki_pulse_fallback_to_pow_and_back::generate(std::vector<test_event_entry>
bool loki_pulse_chain_split::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
@ -3274,7 +3283,7 @@ bool loki_pulse_chain_split::generate(std::vector<test_event_entry> &events)
// Pulse chain weight to switch over.
bool loki_pulse_chain_split_with_no_checkpoints::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);

View File

@ -368,7 +368,7 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector<test_event_entry
auto sources_copy = sources;
loki_construct_tx_params tx_params;
tx_params.hf_version = cryptonote::network_version_8;
r = construct_tx_and_get_tx_key(miner_account[creator].get_keys(), subaddresses, sources, destinations, std::nullopt, std::vector<uint8_t>(), tx, 0, tx_key, additional_tx_secret_keys, { rct::RangeProofPaddedBulletproof, 2 }, msoutp, tx_params);
r = construct_tx_and_get_tx_key(miner_account[creator].get_keys(), subaddresses, sources, destinations, std::nullopt, std::vector<uint8_t>(), tx, 0, tx_key, additional_tx_secret_keys, { rct::RangeProofType::PaddedBulletproof, 3 }, msoutp, tx_params);
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
#ifndef NO_MULTISIG
@ -458,7 +458,7 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector<test_event_entry
crypto::secret_key scalar1;
crypto::derivation_to_scalar(derivation, n, scalar1);
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG);
rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTType::Bulletproof2 || tx.rct_signatures.type == rct::RCTType::CLSAG);
rct::key C = tx.rct_signatures.outPk[n].mask;
rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H);
CHECK_AND_ASSERT_MES(rct::equalKeys(C, Ctmp), false, "Failed to decode amount");

View File

@ -134,8 +134,8 @@ bool gen_rct_tx_validation_base::generate_with_full(std::vector<test_event_entry
CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation");
crypto::secret_key amount_key;
crypto::derivation_to_scalar(derivation, o, amount_key);
const uint8_t type = rct_txes[n].rct_signatures.type;
if (type == rct::RCTTypeSimple || type == rct::RCTTypeBulletproof || type == rct::RCTTypeBulletproof2 || type == rct::RCTTypeCLSAG)
const auto type = rct_txes[n].rct_signatures.type;
if (type == rct::RCTType::Simple || type == rct::RCTType::Bulletproof || type == rct::RCTType::Bulletproof2 || type == rct::RCTType::CLSAG)
rct::decodeRctSimple(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4], hw::get_device("default"));
else
rct::decodeRct(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4], hw::get_device("default"));
@ -247,7 +247,7 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
const std::function<void(std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations)> &pre_tx,
const std::function<void(transaction &tx)> &post_tx) const
{
const rct::RCTConfig rct_config { rct::RangeProofBorromean, 0 };
const rct::RCTConfig rct_config { rct::RangeProofType::Borromean, 0 };
return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, 4, rct_config, valid, pre_tx, post_tx);
}
@ -526,6 +526,6 @@ bool gen_rct_tx_uses_output_too_early::generate(std::vector<test_event_entry>& e
const int mixin = 10;
const int out_idx[] = {1, -1};
const uint64_t amount_paid = 10000;
const rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, 2 };
const rct::RCTConfig rct_config { rct::RangeProofType::PaddedBulletproof, 2 };
return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE-3, HF_VERSION_ENFORCE_MIN_AGE, rct_config, false, NULL, NULL);
}

View File

@ -48,7 +48,7 @@ namespace
m_tx.version = version;
m_tx.unlock_time = unlock_time;
m_tx_key = keypair::generate(hw::get_device("default"));
m_tx_key = keypair{hw::get_device("default")};
add_tx_extra<tx_extra_pub_key>(m_tx, m_tx_key.pub);
}
@ -180,27 +180,6 @@ namespace
//----------------------------------------------------------------------------------------------------------------------
// Tests
bool gen_tx_big_version::generate(std::vector<test_event_entry>& events) const
{
uint64_t ts_start = 1338224400;
GENERATE_ACCOUNT(miner_account);
MAKE_GENESIS_BLOCK(events, blk_tail, miner_account, ts_start);
REWIND_BLOCKS_N(events, blk_money_unlocked, blk_tail, miner_account, 30);
REWIND_BLOCKS(events, blk_head, blk_money_unlocked, miner_account);
std::vector<tx_source_entry> sources;
std::vector<tx_destination_entry> destinations;
fill_tx_sources_and_destinations(events, blk_money_unlocked, miner_account, get_address(miner_account), MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations);
transaction tx = {};
loki_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), -1).build();
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(tx);
return true;
}
bool gen_tx_unlock_time::generate(std::vector<test_event_entry>& events) const
{
uint64_t ts_start = 1338224400;
@ -304,7 +283,7 @@ bool gen_tx_no_inputs_no_outputs::generate(std::vector<test_event_entry>& events
transaction tx = {};
tx.version = cryptonote::txversion::v2_ringct;
add_tx_extra<tx_extra_pub_key>(tx, keypair::generate(hw::get_device("default")).pub);
add_tx_extra<tx_extra_pub_key>(tx, keypair{hw::get_device("default")}.pub);
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(tx);
@ -535,7 +514,7 @@ bool gen_tx_key_image_not_derive_from_tx_key::generate(std::vector<test_event_en
txin_to_key& in_to_key = var::get<txin_to_key>(tx.vin.front());
// Use fake key image
keypair keys = keypair::generate(hw::get_device("default"));
keypair keys{hw::get_device("default")};
key_image fake_key_image;
crypto::generate_key_image(keys.pub, keys.sec, fake_key_image);
in_to_key.k_image = fake_key_image;

View File

@ -74,11 +74,6 @@ private:
size_t m_invalid_block_index;
};
struct gen_tx_big_version : public get_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
struct gen_tx_unlock_time : public get_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;

View File

@ -72,7 +72,7 @@ cryptonote::account_public_address get_address(const tools::wallet2*);
bool construct_tx_to_key(cryptonote::transaction& tx, tools::wallet2 * from_wallet, const var_addr_t& to, uint64_t amount,
std::vector<cryptonote::tx_source_entry> &sources,
uint64_t fee, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0);
uint64_t fee, rct::RangeProofType range_proof_type=rct::RangeProofType::Borromean, int bp_version = 0);
bool construct_tx_to_key(cryptonote::transaction& tx, tools::wallet2 * sender_wallet, const std::vector<cryptonote::tx_destination_entry>& destinations,
std::vector<cryptonote::tx_source_entry> &sources,
@ -83,4 +83,4 @@ bool construct_tx_rct(tools::wallet2 * sender_wallet,
const std::vector<cryptonote::tx_destination_entry>& destinations,
const std::optional<cryptonote::tx_destination_entry>& change_addr,
std::vector<uint8_t> extra, cryptonote::transaction& tx, uint64_t unlock_time,
rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0);
rct::RangeProofType range_proof_type=rct::RangeProofType::Borromean, int bp_version = 0);

View File

@ -42,8 +42,6 @@ void setup_random(void);
}
bool check_scalar(const crypto::ec_scalar &scalar);
void random_scalar(crypto::ec_scalar &res);
void hash_to_scalar(const void *data, std::size_t length, crypto::ec_scalar &res);
void hash_to_point(const crypto::hash &h, crypto::ec_point &res);
void hash_to_ec(const crypto::public_key &key, crypto::ec_point &res);
#endif

View File

@ -36,14 +36,6 @@ bool check_scalar(const crypto::ec_scalar &scalar) {
return crypto::sc_check(crypto::operator &(scalar)) == 0;
}
void random_scalar(crypto::ec_scalar &res) {
crypto::random_scalar(res);
}
void hash_to_scalar(const void *data, std::size_t length, crypto::ec_scalar &res) {
crypto::hash_to_scalar(data, length, res);
}
void hash_to_point(const crypto::hash &h, crypto::ec_point &res) {
crypto::ge_p2 point;
crypto::ge_fromfe_frombytes_vartime(&point, reinterpret_cast<const unsigned char *>(&h));

View File

@ -31,16 +31,21 @@
#include <cstddef>
#include <cstring>
#include <fstream>
#include <initializer_list>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "common/hex.h"
#include "common/string_util.h"
#include "epee/warnings.h"
#include "epee/misc_log_ex.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
#include "crypto-tests.h"
#include "../io.h"
using namespace std::literals;
using namespace crypto;
typedef crypto::hash chash;
@ -58,214 +63,285 @@ bool operator !=(const key_derivation &a, const key_derivation &b) {
DISABLE_GCC_WARNING(maybe-uninitialized)
size_t lineno;
template <typename T>
T extract_single(std::string_view val) {
if constexpr (std::is_same_v<T, bool>) {
if (val == "true") return true;
if (val == "false") return false;
throw std::runtime_error{"Invalid value, expected {true|false}, got " + std::string{val} + " on line " + std::to_string(lineno)};
} else if constexpr (std::is_integral_v<T>) {
T v;
if (!tools::parse_int(val, v))
throw std::runtime_error{"Invalid value, expected integer, got " + std::string{val} + " on line " + std::to_string(lineno)};
return v;
} else if constexpr (std::is_same_v<T, std::string_view>) {
return val;
} else {
T v;
if (!tools::hex_to_type(val, v))
throw std::runtime_error("Invalid hex [" + std::string{val} + ", size=" + std::to_string(val.size()/2) + "B], could not extract type (T size=" + std::to_string(sizeof(T)) + ") on line " + std::to_string(lineno));
return v;
}
}
template <typename T>
std::string make_single(const T& val) {
if constexpr (std::is_same_v<T, bool>)
return val ? "true" : "false";
else if constexpr (std::is_integral_v<T>)
return std::to_string(val);
else if constexpr (std::is_same_v<T, std::string_view>)
return std::string{val};
else
return tools::type_to_hex(val);
}
template <typename... T, typename It, size_t... S>
std::tuple<T...> extract(It it, It end, std::index_sequence<S...>) {
return {extract_single<T>(it[S])...};
}
template <typename... T>
std::tuple<T...> extract(const std::vector<std::string_view>& line, size_t skip = 0) {
if (sizeof...(T) + skip + 1 > line.size())
throw std::runtime_error("Invalid data: too few elements on line " + std::to_string(lineno) + ": expected >= " + std::to_string(sizeof...(T) + skip + 1) + ", have " + std::to_string(line.size()));
return extract<T...>(line.begin() + skip + 1, line.end(), std::index_sequence_for<T...>{});
}
template <typename... T>
std::string make(const T&... val) {
return tools::join(" ", std::initializer_list<std::string>{make_single(val)...});
}
int main(int argc, char *argv[]) {
TRY_ENTRY();
std::fstream input;
input.exceptions(std::ios::badbit | std::ios::failbit);
std::string cmd;
size_t test = 0;
bool error = false;
size_t errors = 0;
setup_random();
if (argc != 2) {
std::cerr << "invalid arguments\n";
std::cerr << "Invalid arguments! Usage: " << argv[0] << " /path/to/tests.txt\n";
return 1;
}
input.open(argv[1], std::ios_base::in);
std::ofstream regen;
regen.exceptions(std::ios::badbit | std::ios::failbit);
bool verbose = false;
if (auto* envverbose = std::getenv("VERBOSE"); envverbose && envverbose == "1"sv)
verbose = true;
if (auto* envregen = std::getenv("REGEN"); envregen && envregen == "1"sv) {
regen.open("tests-regen.txt", std::ios::trunc);
std::cerr << "Writing calculated test results to ./tests-regen.txt\n";
}
input.exceptions(std::ios_base::badbit);
// If a test fails, this is set to what the line would need to be to pass the test (without the leading command)
std::string fail_line;
lineno = 0;
for (;;) {
++test;
input.exceptions(std::ios_base::badbit);
if (!(input >> cmd)) {
std::vector<char> linebuf(50000); // This test file has some massive lines in it
input.getline(linebuf.data(), 50000);
if (input.eof())
break;
++lineno;
std::string_view line{linebuf.data()};
fail_line.clear();
auto test_args = tools::split(line, " ");
if (test_args.empty()) {
std::cerr << "Warning: invalid empty test line at " << argv[1] << ":" << test << "\n";
continue;
}
input.exceptions(std::ios_base::badbit | std::ios_base::failbit | std::ios_base::eofbit);
auto& cmd = test_args[0];
if (cmd == "check_scalar") {
ec_scalar scalar;
bool expected, actual;
get(input, scalar, expected);
actual = check_scalar(scalar);
if (expected != actual) {
goto error;
}
auto [scalar, expected] = extract<ec_scalar, bool>(test_args);
bool actual = check_scalar(scalar);
if (expected != actual)
fail_line = make(scalar, actual);
} else if (cmd == "random_scalar") {
ec_scalar expected, actual;
get(input, expected);
auto [expected] = extract<ec_scalar>(test_args);
ec_scalar actual;
random_scalar(actual);
if (expected != actual) {
goto error;
}
if (expected != actual)
fail_line = make(actual);
} else if (cmd == "hash_to_scalar") {
std::vector<char> data;
ec_scalar expected, actual;
get(input, data, expected);
auto [data, expected] = extract<std::string_view, ec_scalar>(test_args);
ec_scalar actual;
crypto::hash_to_scalar(data.data(), data.size(), actual);
if (expected != actual) {
goto error;
}
if (expected != actual)
fail_line = make(data, actual);
} else if (cmd == "generate_keys") {
public_key expected1, actual1;
secret_key expected2, actual2;
get(input, expected1, expected2);
auto [expected1, expected2] = extract<public_key, secret_key>(test_args);
public_key actual1;
secret_key actual2;
generate_keys(actual1, actual2);
if (expected1 != actual1 || expected2 != actual2) {
goto error;
}
if (expected1 != actual1 || expected2 != actual2)
fail_line = make(actual1, actual2);
} else if (cmd == "check_key") {
public_key key;
bool expected, actual;
get(input, key, expected);
auto [key, expected] = extract<public_key, bool>(test_args);
bool actual;
actual = check_key(key);
if (expected != actual) {
goto error;
}
if (expected != actual)
fail_line = make(key, actual);
} else if (cmd == "secret_key_to_public_key") {
secret_key sec;
bool expected1, actual1;
auto [sec, expected1] = extract<secret_key, bool>(test_args);
bool actual1;
public_key expected2, actual2;
get(input, sec, expected1);
if (expected1) {
get(input, expected2);
}
if (expected1)
std::tie(expected2) = extract<public_key>(test_args, 2);
actual1 = secret_key_to_public_key(sec, actual2);
if (expected1 != actual1 || (expected1 && expected2 != actual2)) {
goto error;
fail_line = make(sec, actual1);
if (actual1) fail_line += " " + make(actual2);
}
} else if (cmd == "generate_key_derivation") {
public_key key1;
secret_key key2;
bool expected1, actual1;
auto [key1, key2, expected1] = extract<public_key, secret_key, bool>(test_args);
bool actual1;
key_derivation expected2, actual2;
get(input, key1, key2, expected1);
if (expected1) {
get(input, expected2);
}
if (expected1)
std::tie(expected2) = extract<key_derivation>(test_args, 3);
actual1 = generate_key_derivation(key1, key2, actual2);
if (expected1 != actual1 || (expected1 && expected2 != actual2)) {
goto error;
fail_line = make(key1, key2, actual1);
if (actual1) fail_line += " " + make(actual2);
}
} else if (cmd == "derive_public_key") {
key_derivation derivation;
size_t output_index;
public_key base;
bool expected1, actual1;
auto [derivation, output_index, base, expected1] = extract<key_derivation, size_t, public_key, bool>(test_args);
bool actual1;
public_key expected2, actual2;
get(input, derivation, output_index, base, expected1);
if (expected1) {
get(input, expected2);
}
if (expected1)
std::tie(expected2) = extract<public_key>(test_args, 4);
actual1 = derive_public_key(derivation, output_index, base, actual2);
if (expected1 != actual1 || (expected1 && expected2 != actual2)) {
goto error;
fail_line = make(derivation, output_index, base, actual1);
if (actual1)
fail_line += " " + make(actual2);
}
} else if (cmd == "derive_secret_key") {
key_derivation derivation;
size_t output_index;
secret_key base;
secret_key expected, actual;
get(input, derivation, output_index, base, expected);
auto [derivation, output_index, base, expected] = extract<key_derivation, size_t, secret_key, secret_key>(test_args);
secret_key actual;
derive_secret_key(derivation, output_index, base, actual);
if (expected != actual) {
goto error;
}
if (expected != actual)
fail_line = make(derivation, output_index, base, actual);
} else if (cmd == "generate_signature") {
chash prefix_hash;
public_key pub;
secret_key sec;
signature expected, actual;
get(input, prefix_hash, pub, sec, expected);
auto [prefix_hash, pub, sec, expected] = extract<chash, public_key, secret_key, signature>(test_args);
signature actual;
generate_signature(prefix_hash, pub, sec, actual);
if (expected != actual) {
goto error;
}
if (expected != actual)
fail_line = make(prefix_hash, pub, sec, actual);
} else if (cmd == "check_signature") {
chash prefix_hash;
public_key pub;
signature sig;
bool expected, actual;
get(input, prefix_hash, pub, sig, expected);
actual = check_signature(prefix_hash, pub, sig);
if (expected != actual) {
goto error;
}
auto [prefix_hash, pub, sig, expected] = extract<chash, public_key, signature, bool>(test_args);
bool actual = check_signature(prefix_hash, pub, sig);
if (expected != actual)
fail_line = make(prefix_hash, pub, sig, actual);
} else if (cmd == "hash_to_point") {
chash h;
ec_point expected, actual;
get(input, h, expected);
auto [h, expected] = extract<chash, ec_point>(test_args);
ec_point actual;
hash_to_point(h, actual);
if (expected != actual) {
goto error;
}
if (expected != actual)
fail_line = make(h, actual);
} else if (cmd == "hash_to_ec") {
public_key key;
ec_point expected, actual;
get(input, key, expected);
auto [key, expected] = extract<public_key, ec_point>(test_args);
ec_point actual;
hash_to_ec(key, actual);
if (expected != actual) {
goto error;
}
if (expected != actual)
fail_line = make(key, actual);
} else if (cmd == "generate_key_image") {
public_key pub;
secret_key sec;
key_image expected, actual;
get(input, pub, sec, expected);
auto [pub, sec, expected] = extract<public_key, secret_key, key_image>(test_args);
key_image actual;
generate_key_image(pub, sec, actual);
if (expected != actual) {
goto error;
}
} else if (cmd == "generate_ring_signature") {
chash prefix_hash;
key_image image;
if (expected != actual)
fail_line = make(pub, sec, actual);
} else if (cmd == "generate_ring_signature" || cmd == "check_ring_signature") {
bool generate = cmd == "generate_ring_signature";
auto [prefix_hash, image, pubs_count] = extract<chash, key_image, size_t>(test_args);
std::vector<public_key> vpubs;
std::vector<const public_key *> pubs;
size_t pubs_count;
vpubs.reserve(pubs_count);
size_t skip = 3;
for (size_t i = 0; i < pubs_count; i++)
vpubs.push_back(std::get<0>(extract<public_key>(test_args, skip+i)));
skip += pubs_count;
pubs.reserve(vpubs.size());
for (auto& vpub : vpubs)
pubs.push_back(&vpub);
secret_key sec;
size_t sec_index;
std::vector<signature> expected, actual;
size_t i;
get(input, prefix_hash, image, pubs_count);
vpubs.resize(pubs_count);
pubs.resize(pubs_count);
for (i = 0; i < pubs_count; i++) {
get(input, vpubs[i]);
pubs[i] = &vpubs[i];
if (generate) {
std::tie(sec, sec_index) = extract<secret_key, size_t>(test_args, skip);
skip += 2;
}
get(input, sec, sec_index);
expected.resize(pubs_count);
getvar(input, pubs_count * sizeof(signature), expected.data());
actual.resize(pubs_count);
generate_ring_signature(prefix_hash, image, pubs.data(), pubs_count, sec, sec_index, actual.data());
if (expected != actual) {
goto error;
}
} else if (cmd == "check_ring_signature") {
chash prefix_hash;
key_image image;
std::vector<public_key> vpubs;
std::vector<const public_key *> pubs;
size_t pubs_count;
std::vector<signature> sigs;
bool expected, actual;
size_t i;
get(input, prefix_hash, image, pubs_count);
vpubs.resize(pubs_count);
pubs.resize(pubs_count);
for (i = 0; i < pubs_count; i++) {
get(input, vpubs[i]);
pubs[i] = &vpubs[i];
sigs.reserve(pubs_count);
for (size_t i = 0; i < pubs_count; i++)
sigs.push_back(std::get<0>(extract<signature>(test_args, skip+i)));
skip += pubs_count;
std::string fail;
if (generate) {
std::vector<signature> actual(pubs_count);
generate_ring_signature(prefix_hash, image, pubs, sec, sec_index, actual.data());
if (sigs != actual)
for (auto& a : actual) {
fail += ' ';
fail += make(a);
}
} else { // check mode
auto [expected] = extract<bool>(test_args, skip++);
bool actual = check_ring_signature(prefix_hash, image, pubs, sigs.data());
if (expected != actual)
fail = actual ? " true" : " false";
}
sigs.resize(pubs_count);
getvar(input, pubs_count * sizeof(signature), sigs.data());
get(input, expected);
actual = check_ring_signature(prefix_hash, image, pubs.data(), pubs_count, sigs.data());
if (expected != actual) {
goto error;
if (!fail.empty()) {
fail_line = make(prefix_hash, image, pubs_count);
for (auto& vpub : vpubs) {
fail_line += ' ';
fail_line += make(vpub);
}
fail_line += ' ';
if (generate)
fail_line += make(sec, sec_index);
else {
for (auto& s : sigs) {
fail_line += ' ';
fail_line += make(s);
}
}
fail_line += std::move(fail);
}
} else {
throw std::ios_base::failure("Unknown function: " + cmd);
throw std::ios_base::failure("Unknown function: " + std::string{cmd});
}
continue;
error:
std::cerr << "Wrong result on test " << test << "\n";
error = true;
if (!fail_line.empty()) {
if (verbose)
std::cerr << "Wrong result for " << argv[1] << ":" << lineno << "\nExpected: " << line << "\nActual: " << fail_line << "\n";
errors++;
if (regen.is_open())
regen << cmd << ' ' << fail_line << '\n';
}
else if (regen.is_open())
regen << line << '\n';
}
return error ? 1 : 0;
CATCH_ENTRY_L0("main", 1);
if (errors > 0) {
std::cout << errors << " of " << lineno << " tests FAILED\n";
if (regen.is_open())
std::cerr << "Test errors occurred. The new results have been written to ./tests-regen.txt\n";
else
std::cerr << "Test errors occurred. To create a test file (./tests-regen.txt) based on the new results set environment variable REGEN=1\n";
}
else
std::cout << "All tests (" << lineno << ") passed\n";
return errors > 0;
}

File diff suppressed because one or more lines are too long

View File

@ -33,28 +33,13 @@
#include <iostream>
#include <type_traits>
#include <vector>
#include <lokimq/hex.h>
inline bool hexdecode(const char *from, std::size_t length, void *to) {
std::size_t i;
for (i = 0; i < length; i++) {
int v = 0;
if (from[2 * i] >= '0' && from[2 * i] <= '9') {
v = from[2 * i] - '0';
} else if (from[2 * i] >= 'a' && from[2 * i] <= 'f') {
v = from[2 * i] - 'a' + 10;
} else {
return false;
}
v <<= 4;
if (from[2 * i + 1] >= '0' && from[2 * i + 1] <= '9') {
v |= from[2 * i + 1] - '0';
} else if (from[2 * i + 1] >= 'a' && from[2 * i + 1] <= 'f') {
v |= from[2 * i + 1] - 'a' + 10;
} else {
return false;
}
*(reinterpret_cast<unsigned char *>(to) + i) = v;
}
const char* end = from + 2*length;
if (!lokimq::is_hex(from, end))
return false;
lokimq::from_hex(from, end, reinterpret_cast<char*>(to));
return true;
}

View File

@ -40,7 +40,7 @@
#include "multi_tx_test_base.h"
template<size_t a_ring_size, size_t a_outputs, bool a_rct, rct::RangeProofType range_proof_type = rct::RangeProofBorromean, int bp_version = 2>
template<size_t a_ring_size, size_t a_outputs, bool a_rct, rct::RangeProofType range_proof_type = rct::RangeProofType::Borromean, int bp_version = 2>
class test_check_tx_signature : private multi_tx_test_base<a_ring_size>
{
static_assert(0 < a_ring_size, "ring_size must be greater than 0");
@ -86,7 +86,7 @@ public:
{
if (rct)
{
if (m_tx.rct_signatures.type == rct::RCTTypeFull)
if (m_tx.rct_signatures.type == rct::RCTType::Full)
return rct::verRct(m_tx.rct_signatures);
else
return rct::verRctSimple(m_tx.rct_signatures);
@ -94,7 +94,7 @@ public:
else
{
const cryptonote::txin_to_key& txin = var::get<cryptonote::txin_to_key>(m_tx.vin[0]);
return crypto::check_ring_signature(m_tx_prefix_hash, txin.k_image, this->m_public_key_ptrs, ring_size, m_tx.signatures[0].data());
return crypto::check_ring_signature(m_tx_prefix_hash, txin.k_image, this->m_public_key_ptrs, m_tx.signatures[0].data());
}
}
@ -140,7 +140,7 @@ public:
m_txes.resize(a_num_txes + (extra_outs > 0 ? 1 : 0));
for (size_t n = 0; n < a_num_txes; ++n)
{
if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::tx_destination_entry{}, std::vector<uint8_t>(), m_txes[n], 0, tx_key, additional_tx_keys, {rct::RangeProofPaddedBulletproof, 2}, nullptr, tx_params))
if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::tx_destination_entry{}, std::vector<uint8_t>(), m_txes[n], 0, tx_key, additional_tx_keys, {rct::RangeProofType::PaddedBulletproof, 2}, nullptr, tx_params))
return false;
}
@ -152,7 +152,7 @@ public:
for (size_t n = 1; n < extra_outs; ++n)
destinations.push_back(tx_destination_entry(1, m_alice.get_keys().m_account_address, false));
if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::tx_destination_entry{}, std::vector<uint8_t>(), m_txes.back(), 0, tx_key, additional_tx_keys, {rct::RangeProofMultiOutputBulletproof, 2}, nullptr, tx_params))
if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::tx_destination_entry{}, std::vector<uint8_t>(), m_txes.back(), 0, tx_key, additional_tx_keys, {rct::RangeProofType::MultiOutputBulletproof, 2}, nullptr, tx_params))
return false;
}

View File

@ -36,7 +36,7 @@
#include "multi_tx_test_base.h"
template<size_t a_in_count, size_t a_out_count, bool a_rct, rct::RangeProofType range_proof_type = rct::RangeProofBorromean, int bp_version = 2>
template<size_t a_in_count, size_t a_out_count, bool a_rct, rct::RangeProofType range_proof_type = rct::RangeProofType::Borromean, int bp_version = 2>
class test_construct_tx : private multi_tx_test_base<a_in_count>
{
static_assert(0 < a_in_count, "in_count must be greater than 0");

View File

@ -45,7 +45,7 @@ public:
bool test()
{
cryptonote::keypair::generate(hw::get_device("default"));
cryptonote::keypair x{hw::get_device("default")};
return true;
}
};

View File

@ -53,13 +53,10 @@
#include "sc_reduce32.h"
#include "sc_check.h"
#include "cn_fast_hash.h"
#include "rct_mlsag.h"
#include "equality.h"
#include "range_proof.h"
#include "bulletproof.h"
#include "crypto_ops.h"
#include "multiexp.h"
#include "sig_mlsag.h"
#include "sig_clsag.h"
namespace po = boost::program_options;
@ -139,17 +136,17 @@ int main(int argc, char** argv)
TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 2, true);
TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 10, true);
TEST_PERFORMANCE5(filter, p, test_construct_tx, 2, 1, true, rct::RangeProofPaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_construct_tx, 2, 2, true, rct::RangeProofPaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_construct_tx, 2, 10, true, rct::RangeProofPaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_construct_tx, 2, 1, true, rct::RangeProofType::PaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_construct_tx, 2, 2, true, rct::RangeProofType::PaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_construct_tx, 2, 10, true, rct::RangeProofType::PaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_construct_tx, 10, 1, true, rct::RangeProofPaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_construct_tx, 10, 2, true, rct::RangeProofPaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_construct_tx, 10, 10, true, rct::RangeProofPaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_construct_tx, 10, 1, true, rct::RangeProofType::PaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_construct_tx, 10, 2, true, rct::RangeProofType::PaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_construct_tx, 10, 10, true, rct::RangeProofType::PaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_construct_tx, 100, 1, true, rct::RangeProofPaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_construct_tx, 100, 2, true, rct::RangeProofPaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_construct_tx, 100, 10, true, rct::RangeProofPaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_construct_tx, 100, 1, true, rct::RangeProofType::PaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_construct_tx, 100, 2, true, rct::RangeProofType::PaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_construct_tx, 100, 10, true, rct::RangeProofType::PaddedBulletproof, 2);
TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 1, 2, false);
TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 2, 2, false);
@ -157,19 +154,19 @@ int main(int argc, char** argv)
TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 100, 2, false);
TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 2, 10, false);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofBorromean);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofBorromean);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofBorromean);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofBorromean);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofType::Borromean);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofType::Borromean);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofType::Borromean);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofType::Borromean);
TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofPaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofMultiOutputBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofPaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofMultiOutputBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofPaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofMultiOutputBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofPaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofMultiOutputBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofType::PaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofType::MultiOutputBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofType::PaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofType::MultiOutputBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofType::PaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofType::MultiOutputBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofType::PaddedBulletproof, 2);
TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofType::MultiOutputBulletproof, 2);
TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 64);
TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 64);
@ -202,13 +199,6 @@ int main(int argc, char** argv)
TEST_PERFORMANCE1(filter, p, test_cn_fast_hash, 32);
TEST_PERFORMANCE1(filter, p, test_cn_fast_hash, 16384);
TEST_PERFORMANCE3(filter, p, test_sig_mlsag, 4, 2, 2); // MLSAG verification
TEST_PERFORMANCE3(filter, p, test_sig_mlsag, 8, 2, 2);
TEST_PERFORMANCE3(filter, p, test_sig_mlsag, 16, 2, 2);
TEST_PERFORMANCE3(filter, p, test_sig_mlsag, 32, 2, 2);
TEST_PERFORMANCE3(filter, p, test_sig_mlsag, 64, 2, 2);
TEST_PERFORMANCE3(filter, p, test_sig_mlsag, 128, 2, 2);
TEST_PERFORMANCE3(filter, p, test_sig_mlsag, 256, 2, 2);
TEST_PERFORMANCE3(filter, p, test_sig_clsag, 4, 2, 2); // CLSAG verification
TEST_PERFORMANCE3(filter, p, test_sig_clsag, 8, 2, 2);
TEST_PERFORMANCE3(filter, p, test_sig_clsag, 16, 2, 2);
@ -217,17 +207,11 @@ int main(int argc, char** argv)
TEST_PERFORMANCE3(filter, p, test_sig_clsag, 128, 2, 2);
TEST_PERFORMANCE3(filter, p, test_sig_clsag, 256, 2, 2);
TEST_PERFORMANCE2(filter, p, test_ringct_mlsag, 11, false);
TEST_PERFORMANCE2(filter, p, test_ringct_mlsag, 11, true);
TEST_PERFORMANCE2(filter, p, test_equality, memcmp32, true);
TEST_PERFORMANCE2(filter, p, test_equality, memcmp32, false);
TEST_PERFORMANCE2(filter, p, test_equality, verify32, false);
TEST_PERFORMANCE2(filter, p, test_equality, verify32, false);
TEST_PERFORMANCE1(filter, p, test_range_proof, true);
TEST_PERFORMANCE1(filter, p, test_range_proof, false);
TEST_PERFORMANCE2(filter, p, test_bulletproof, true, 1); // 1 bulletproof with 1 amount
TEST_PERFORMANCE2(filter, p, test_bulletproof, false, 1);

View File

@ -87,5 +87,5 @@ protected:
std::vector<cryptonote::tx_source_entry> m_sources;
crypto::public_key m_public_keys[ring_size];
const crypto::public_key* m_public_key_ptrs[ring_size];
std::vector<const crypto::public_key*> m_public_key_ptrs{ring_size};
};

View File

@ -1,63 +0,0 @@
// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
#include "ringct/rctSigs.h"
template<bool a_verify>
class test_range_proof
{
public:
static const size_t loop_count = 50;
static const bool verify = a_verify;
bool init()
{
rct::key mask;
sig = rct::proveRange(C, mask, 84932483243793);
return true;
}
bool test()
{
bool ret = true;
rct::key mask;
if (verify)
ret = rct::verRange(C, sig);
else
rct::proveRange(C, mask, 84932483243793);
return ret;
}
private:
rct::key C;
rct::rangeSig sig;
};

View File

@ -1,87 +0,0 @@
// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
#include "ringct/rctSigs.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "single_tx_test_base.h"
template<size_t ring_size, bool ver>
class test_ringct_mlsag : public single_tx_test_base
{
public:
static const size_t cols = ring_size;
static const size_t rows = 2; // single spend and commitment data
static const size_t loop_count = 1000;
bool init()
{
if (!single_tx_test_base::init())
return false;
rct::keyV xtmp = rct::skvGen(rows);
rct::keyM xm = rct::keyMInit(rows, cols);// = [[None]*N] #just used to generate test public keys
sk = rct::skvGen(rows);
P = rct::keyMInit(rows, cols);// = keyM[[None]*N] #stores the public keys;
ind = 2;
for (size_t j = 0 ; j < rows ; j++)
{
for (size_t i = 0 ; i < cols ; i++)
{
xm[i][j] = rct::skGen();
P[i][j] = rct::scalarmultBase(xm[i][j]);
}
}
for (size_t j = 0 ; j < rows ; j++)
{
sk[j] = xm[ind][j];
}
IIccss = MLSAG_Gen(rct::identity(), P, sk, NULL, NULL, ind, rows-1, hw::get_device("default"));
return true;
}
bool test()
{
if (ver)
MLSAG_Ver(rct::identity(), P, IIccss, rows-1);
else
MLSAG_Gen(rct::identity(), P, sk, NULL, NULL, ind, rows-1, hw::get_device("default"));
return true;
}
private:
rct::keyV sk;
rct::keyM P;
size_t ind{};
rct::mgSig IIccss;
};

View File

@ -1,172 +0,0 @@
// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
#include "ringct/rctSigs.h"
#include "ringct/rctTypes.h"
#include "device/device.hpp"
using namespace rct;
template<size_t a_N, size_t a_T, size_t a_w>
class test_sig_mlsag
{
public:
static const size_t loop_count = 1000;
static const size_t N = a_N;
static const size_t T = a_T;
static const size_t w = a_w;
bool init()
{
pubs.reserve(N);
pubs.resize(N);
r = keyV(w); // M[l[u]] = Com(0,r[u])
a = keyV(w); // P[l[u]] = Com(a[u],s[u])
s = keyV(w);
Q = keyV(T); // Q[j] = Com(b[j],t[j])
b = keyV(T);
t = keyV(T);
// Random keys
key temp;
for (size_t k = 0; k < N; k++)
{
skpkGen(temp,pubs[k].dest);
skpkGen(temp,pubs[k].mask);
}
// Signing and commitment keys (assumes fixed signing indices 0,1,...,w-1 for this test)
// TODO: random signing indices
C_offsets = keyV(w); // P[l[u]] - C_offsets[u] = Com(0,s[u]-s1[u])
s1 = keyV(w);
key a_sum = zero();
key s1_sum = zero();
messages = keyV(w);
for (size_t u = 0; u < w; u++)
{
skpkGen(r[u],pubs[u].dest); // M[u] = Com(0,r[u])
a[u] = skGen(); // P[u] = Com(a[u],s[u])
s[u] = skGen();
addKeys2(pubs[u].mask,s[u],a[u],H);
s1[u] = skGen(); // C_offsets[u] = Com(a[u],s1[u])
addKeys2(C_offsets[u],s1[u],a[u],H);
sc_add(a_sum.bytes,a_sum.bytes,a[u].bytes);
sc_add(s1_sum.bytes,s1_sum.bytes,s1[u].bytes);
messages[u] = skGen();
}
// Outputs
key b_sum = zero();
key t_sum = zero();
for (size_t j = 0; j < T-1; j++)
{
b[j] = skGen(); // Q[j] = Com(b[j],t[j])
t[j] = skGen();
addKeys2(Q[j],t[j],b[j],H);
sc_add(b_sum.bytes,b_sum.bytes,b[j].bytes);
sc_add(t_sum.bytes,t_sum.bytes,t[j].bytes);
}
// Value/mask balance for Q[T-1]
sc_sub(b[T-1].bytes,a_sum.bytes,b_sum.bytes);
sc_sub(t[T-1].bytes,s1_sum.bytes,t_sum.bytes);
addKeys2(Q[T-1],t[T-1],b[T-1],H);
// Build proofs
sigs.reserve(w);
sigs.resize(0);
ctkey sk;
for (size_t u = 0; u < w; u++)
{
sk.dest = r[u];
sk.mask = s[u];
sigs.push_back(proveRctMGSimple(messages[u],pubs,sk,s1[u],C_offsets[u],NULL,NULL,u,hw::get_device("default")));
}
return true;
}
bool test()
{
for (size_t u = 0; u < w; u++)
{
if (!verRctMGSimple(messages[u],sigs[u],pubs,C_offsets[u]))
{
return false;
}
}
// Check balanace
std::vector<MultiexpData> balance;
balance.reserve(w + T);
balance.resize(0);
key ZERO = zero();
key ONE = identity();
key MINUS_ONE;
sc_sub(MINUS_ONE.bytes,ZERO.bytes,ONE.bytes);
for (size_t u = 0; u < w; u++)
{
balance.push_back({ONE,C_offsets[u]});
}
for (size_t j = 0; j < T; j++)
{
balance.push_back({MINUS_ONE,Q[j]});
}
if (!(straus(balance) == ONE)) // group identity
{
return false;
}
return true;
}
private:
ctkeyV pubs;
keyV Q;
keyV r;
keyV s;
keyV s1;
keyV t;
keyV a;
keyV b;
keyV C_offsets;
keyV messages;
std::vector<mgSig> sigs;
};

View File

@ -47,7 +47,7 @@ public:
return false;
message = crypto::rand<crypto::hash>();
keys = cryptonote::keypair::generate(hw::get_device("default"));
keys = cryptonote::keypair{hw::get_device("default")};
crypto::generate_signature(message, keys.pub, keys.sec, m_signature);
return true;

View File

@ -536,14 +536,14 @@ static cryptonote::address_parse_info init_addr_parse_info(cryptonote::account_p
static void expand_tsx(cryptonote::transaction &tx)
{
auto & rv = tx.rct_signatures;
if (rv.type == rct::RCTTypeFull)
if (rv.type == rct::RCTType::Full)
{
rv.p.MGs.resize(1);
rv.p.MGs[0].II.resize(tx.vin.size());
for (size_t n = 0; n < tx.vin.size(); ++n)
rv.p.MGs[0].II[n] = rct::ki2rct(var::get<txin_to_key>(tx.vin[n]).k_image);
}
else if (tools::equals_any(rv.type, rct::RCTTypeSimple, rct::RCTTypeBulletproof, rct::RCTTypeBulletproof2))
else if (tools::equals_any(rv.type, rct::RCTType::Simple, rct::RCTType::Bulletproof, rct::RCTType::Bulletproof2))
{
CHECK_AND_ASSERT_THROW_MES(rv.p.MGs.size() == tx.vin.size(), "Bad MGs size");
for (size_t n = 0; n < tx.vin.size(); ++n)
@ -552,7 +552,7 @@ static void expand_tsx(cryptonote::transaction &tx)
rv.p.MGs[n].II[0] = rct::ki2rct(var::get<txin_to_key>(tx.vin[n]).k_image);
}
}
else if (rv.type == rct::RCTTypeCLSAG)
else if (rv.type == rct::RCTType::CLSAG)
{
if (!tx.pruned)
{
@ -607,7 +607,7 @@ const std::string gen_trezor_base::m_alice_spend_private = m_master_seed_str;
const std::string gen_trezor_base::m_alice_view_private = "a6ccd4ac344a295d1387f8d18c81bdd394f1845de84188e204514ef9370fd403";
gen_trezor_base::gen_trezor_base(){
m_rct_config = {rct::RangeProofPaddedBulletproof, 1};
m_rct_config = {rct::RangeProofType::PaddedBulletproof, 1};
m_test_get_tx_key = true;
m_network_type = cryptonote::TESTNET;
}
@ -843,7 +843,7 @@ bool gen_trezor_base::generate(std::vector<test_event_entry>& events)
bool res = wallet_tools::fill_tx_sources(m_wl_alice.get(), sources, TREZOR_TEST_MIXIN, std::nullopt, MK_COINS(2), m_bt, selected_transfers, num_blocks(events) - 1, 0, 1);
CHECK_AND_ASSERT_THROW_MES(res, "TX Fill sources failed");
construct_tx_to_key(tx_1, m_wl_alice.get(), m_bob_account, MK_COINS(1), sources, TREZOR_TEST_FEE, true, rct::RangeProofPaddedBulletproof, 1);
construct_tx_to_key(tx_1, m_wl_alice.get(), m_bob_account, MK_COINS(1), sources, TREZOR_TEST_FEE, true, rct::RangeProofType::PaddedBulletproof, 1);
events.push_back(tx_1);
MAKE_NEXT_BLOCK_TX1_HF(events, blk_6, blk_5r, m_miner_account, tx_1, CUR_HF);
MDEBUG("Post 1st tsx: " << (num_blocks(events) - 1) << " at block: " << get_block_hash(blk_6));
@ -1261,11 +1261,11 @@ void gen_trezor_base::set_hard_fork(uint8_t hf)
if (hf < 9){
throw std::runtime_error("Minimal supported Hardfork is 9");
} else if (hf <= 11){
rct_config({rct::RangeProofPaddedBulletproof, 1});
rct_config({rct::RangeProofType::PaddedBulletproof, 1});
} else if (hf == 12){
rct_config({rct::RangeProofPaddedBulletproof, 2});
rct_config({rct::RangeProofType::PaddedBulletproof, 2});
} else if (hf == HF_VERSION_CLSAG){
rct_config({rct::RangeProofPaddedBulletproof, 3});
rct_config({rct::RangeProofType::PaddedBulletproof, 3});
} else {
throw std::runtime_error("Unsupported HF");
}
@ -1608,7 +1608,7 @@ bool gen_trezor_live_refresh::generate(std::vector<test_event_entry>& events)
::crypto::public_key pub_ver;
::crypto::key_image ki;
::crypto::random32_unbiased((unsigned char*)r.data);
::crypto::random_scalar((unsigned char*)r.data);
::crypto::secret_key_to_public_key(r, R);
memcpy(D.data, rct::scalarmultKey(rct::pk2rct(R), rct::sk2rct(m_alice_account.get_keys().m_view_secret_key)).bytes, 32);

View File

@ -151,11 +151,11 @@ protected:
class tsx_builder {
public:
tsx_builder(): m_tester(nullptr), m_from(nullptr), m_account(0), m_mixin(TREZOR_TEST_MIXIN), m_fee(TREZOR_TEST_FEE),
m_rct_config({rct::RangeProofPaddedBulletproof, 1 }){}
m_rct_config({rct::RangeProofType::PaddedBulletproof, 1 }){}
tsx_builder(gen_trezor_base * tester): m_tester(tester), m_from(nullptr), m_account(0),
m_mixin(TREZOR_TEST_MIXIN), m_fee(TREZOR_TEST_FEE),
m_rct_config({rct::RangeProofPaddedBulletproof, 1 }){}
m_rct_config({rct::RangeProofType::PaddedBulletproof, 1 }){}
tsx_builder * cur_height(uint64_t cur_height) { m_cur_height = cur_height; return this; }
tsx_builder * mixin(size_t mixin=TREZOR_TEST_MIXIN) { m_mixin = mixin; return this; }

View File

@ -32,7 +32,7 @@
TEST(account, encrypt_keys)
{
cryptonote::keypair recovery_key = cryptonote::keypair::generate(hw::get_device("default"));
cryptonote::keypair recovery_key{hw::get_device("default")};
cryptonote::account_base account;
crypto::secret_key key = account.generate(recovery_key.sec);
const cryptonote::account_keys keys = account.get_keys();

View File

@ -132,7 +132,7 @@ TEST(bulletproofs, multi_splitting)
}
rct::ctkeyV outSk;
rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, 0 };
rct::RCTConfig rct_config { rct::RangeProofType::PaddedBulletproof, 0 };
rct::rctSig s = rct::genRctSimple(rct::zero(), sc, destinations, inamounts, outamounts, available, mixRing, amount_keys, NULL, NULL, index, outSk, rct_config, hw::get_device("default"));
ASSERT_TRUE(rct::verRctSimple(s));
for (size_t i = 0; i < n_outputs; ++i)

View File

@ -58,7 +58,7 @@ TEST(device, open_close)
{
hw::core::device_default dev;
crypto::secret_key key;
ASSERT_TRUE(dev.open_tx(key));
ASSERT_TRUE(dev.open_tx(key, cryptonote::txversion::v4_tx_types, cryptonote::txtype::standard));
ASSERT_TRUE(dev.close_tx());
}

View File

@ -483,25 +483,6 @@ TEST(ToHex, Ostream)
EXPECT_EQ(expected, out.str());
}
TEST(ToHex, Formatted)
{
std::stringstream out;
std::string expected{"<>"};
epee::to_hex::formatted(out, nullptr);
EXPECT_EQ(expected, out.str());
expected.append("<ffab0100>");
epee::to_hex::formatted(out, epee::as_byte_span("\xFF\xAB\x01"));
EXPECT_EQ(expected, out.str());
const std::vector<unsigned char> all_bytes = get_all_bytes();
expected.append("<").append(std_to_hex(all_bytes)).append(">");
epee::to_hex::formatted(out, epee::to_span(all_bytes));
EXPECT_EQ(expected, out.str());
}
TEST(StringTools, ParseNotHex)
{
std::string res;

View File

@ -121,7 +121,7 @@ TEST(loki_name_system, value_encrypt_and_decrypt)
auto mval = value;
ASSERT_TRUE(mval.encrypt(name_copy));
name_copy[0] = 'Z';
name_copy[0] = 'z';
ASSERT_FALSE(mval.decrypt(name_copy, type));
}
}

View File

@ -34,12 +34,12 @@ extern "C" {
#include "crypto/crypto-ops.h"
}
TEST(random32_unbiased, less_than_order)
TEST(random_scalar, less_than_order)
{
unsigned char tmp[32], tmp2[32];
for (int i = 0; i < 1000; ++i)
{
crypto::random32_unbiased(tmp);
crypto::random_scalar(tmp);
memcpy(tmp2, tmp, 32);
sc_reduce32(tmp2);
ASSERT_EQ(memcmp(tmp, tmp2, 32), 0);

File diff suppressed because it is too large Load Diff

View File

@ -58,7 +58,7 @@ crypto::chacha_key generate_chacha_key()
crypto::key_image generate_key_image()
{
crypto::key_image key_image;
cryptonote::keypair keypair = cryptonote::keypair::generate(hw::get_device("default"));
cryptonote::keypair keypair{hw::get_device("default")};
crypto::generate_key_image(keypair.pub, keypair.sec, key_image);
return key_image;
}

View File

@ -111,7 +111,7 @@ void try_parse(std::string_view blob)
return serialization::parse_binary(blob, s1);
}
TEST(Serialization, BinaryArchiveInts) {
TEST(serialization, binary_archive_integers_fixed) {
uint64_t x = 0xff00000000, x1;
serialization::binary_string_archiver oar;
@ -128,7 +128,7 @@ TEST(Serialization, BinaryArchiveInts) {
ASSERT_EQ(x, x1);
}
TEST(Serialization, BinaryArchiveVarInts) {
TEST(serialization, binary_archive_integers_variable) {
uint64_t x = 0xff00000000, x1;
serialization::binary_string_archiver oar;
@ -144,7 +144,7 @@ TEST(Serialization, BinaryArchiveVarInts) {
ASSERT_EQ(x, x1);
}
TEST(Serialization, Test1) {
TEST(serialization, custom_type_serialization) {
Struct1 s1;
s1.si.push_back(0);
{
@ -169,7 +169,7 @@ TEST(Serialization, Test1) {
ASSERT_THROW(try_parse(blob), std::runtime_error);
}
TEST(Serialization, Overflow) {
TEST(serialization, overflow) {
Blob x = { 0xff00000000 };
Blob x1;
@ -185,7 +185,7 @@ TEST(Serialization, Overflow) {
ASSERT_EQ(0, bigvector.size());
}
TEST(Serialization, serializes_vector_uint64_as_varint)
TEST(serialization, serializes_vector_uint64_as_varint)
{
std::vector<uint64_t> v;
std::string blob;
@ -240,7 +240,7 @@ TEST(Serialization, serializes_vector_uint64_as_varint)
ASSERT_EQ(lokimq::to_hex(blob), "0364cc0184fe02");
}
TEST(Serialization, serializes_vector_int64_as_fixed_int)
TEST(serialization, serializes_vector_int64_as_fixed_int)
{
std::vector<int64_t> v;
std::string blob;
@ -298,7 +298,7 @@ namespace
}
}
TEST(Serialization, serializes_transaction_signatures_correctly)
TEST(serialization, serializes_transaction_signatures_correctly)
{
using namespace cryptonote;
@ -468,107 +468,91 @@ TEST(Serialization, serializes_transaction_signatures_correctly)
ASSERT_THROW(serialization::parse_binary(blob, tx1), std::runtime_error);
}
TEST(Serialization, serializes_ringct_types)
template <typename T>
T round_trip(T& x)
{
std::string blob;
rct::key key0, key1;
rct::keyV keyv0, keyv1;
rct::keyM keym0, keym1;
rct::ctkey ctkey0, ctkey1;
rct::ctkeyV ctkeyv0, ctkeyv1;
rct::ctkeyM ctkeym0, ctkeym1;
rct::ecdhTuple ecdh0, ecdh1;
rct::boroSig boro0, boro1;
rct::mgSig mg0, mg1;
rct::clsag clsag0, clsag1;
rct::Bulletproof bp0, bp1;
rct::rctSig s0, s1;
cryptonote::transaction tx0, tx1;
static_assert(!std::is_const_v<T>);
std::string blob = serialization::dump_binary(x);
T y;
serialization::parse_binary(blob, y);
return y;
}
key0 = rct::skGen();
ASSERT_NO_THROW(blob = serialization::dump_binary(key0));
ASSERT_NO_THROW(serialization::parse_binary(blob, key1));
ASSERT_TRUE(key0 == key1);
TEST(serialization, serialize_rct_key) {
auto key = rct::skGen();
ASSERT_EQ(key, round_trip(key));
}
keyv0 = rct::skvGen(30);
for (size_t n = 0; n < keyv0.size(); ++n)
keyv0[n] = rct::skGen();
ASSERT_NO_THROW(blob = serialization::dump_binary(keyv0));
ASSERT_NO_THROW(serialization::parse_binary(blob, keyv1));
ASSERT_TRUE(keyv0.size() == keyv1.size());
for (size_t n = 0; n < keyv0.size(); ++n)
{
ASSERT_TRUE(keyv0[n] == keyv1[n]);
TEST(serialization, serialize_rct_key_vector) {
auto keyv = rct::skvGen(30);
for (auto& key : keyv)
key = rct::skGen();
ASSERT_EQ(keyv, round_trip(keyv));
}
TEST(serialization, serialize_rct_key_matrix) {
auto keym = rct::keyMInit(9, 12);
for (auto& col : keym)
for (auto& key : col)
key = rct::skGen();
ASSERT_EQ(keym, round_trip(keym));
}
TEST(serialization, serialize_rct_ctkey) {
rct::ctkey key;
rct::skpkGen(key.dest, key.mask);
rct::ctkey key2 = round_trip(key);
ASSERT_EQ(tools::view_guts(key), tools::view_guts(key2));
}
TEST(serialization, serialize_rct_ctkey_vector) {
rct::ctkeyV keyv(14);
for (auto& key : keyv)
rct::skpkGen(key.dest, key.mask);
auto keyv2 = round_trip(keyv);
ASSERT_EQ(keyv.size(), keyv2.size());
for (size_t i = 0; i < keyv.size(); i++)
ASSERT_EQ(tools::view_guts(keyv[i]), tools::view_guts(keyv2[i]));
}
TEST(serialization, serialize_rct_ctkey_matrix) {
rct::ctkeyM keym(9);
for (auto& col : keym) {
col.resize(11);
for (auto& key : col)
rct::skpkGen(key.dest, key.mask);
}
keym0 = rct::keyMInit(9, 12);
for (size_t n = 0; n < keym0.size(); ++n)
for (size_t i = 0; i < keym0[n].size(); ++i)
keym0[n][i] = rct::skGen();
ASSERT_NO_THROW(blob = serialization::dump_binary(keym0));
ASSERT_NO_THROW(serialization::parse_binary(blob, keym1));
ASSERT_TRUE(keym0.size() == keym1.size());
for (size_t n = 0; n < keym0.size(); ++n)
{
ASSERT_TRUE(keym0[n].size() == keym1[n].size());
for (size_t i = 0; i < keym0[n].size(); ++i)
{
ASSERT_TRUE(keym0[n][i] == keym1[n][i]);
}
auto keym2 = round_trip(keym);
ASSERT_EQ(keym.size(), keym2.size());
for (size_t c = 0; c < keym.size(); c++) {
ASSERT_EQ(keym[c].size(), keym2[c].size());
for (size_t r = 0; r < keym[c].size(); r++)
ASSERT_EQ(tools::view_guts(keym[c][r]), tools::view_guts(keym2[c][r]));
}
}
rct::skpkGen(ctkey0.dest, ctkey0.mask);
ASSERT_NO_THROW(blob = serialization::dump_binary(ctkey0));
ASSERT_NO_THROW(serialization::parse_binary(blob, ctkey1));
ASSERT_TRUE(!memcmp(&ctkey0, &ctkey1, sizeof(ctkey0)));
TEST(serialization, serialize_rct_ecdh) {
rct::ecdhTuple ecdh;
ecdh.mask = rct::skGen();
ecdh.amount = rct::skGen();
auto ecdh2 = round_trip(ecdh);
ASSERT_EQ(tools::view_guts(ecdh.mask), tools::view_guts(ecdh2.mask));
ASSERT_EQ(tools::view_guts(ecdh.amount), tools::view_guts(ecdh2.amount));
}
ctkeyv0 = std::vector<rct::ctkey>(14);
for (size_t n = 0; n < ctkeyv0.size(); ++n)
rct::skpkGen(ctkeyv0[n].dest, ctkeyv0[n].mask);
ASSERT_NO_THROW(blob = serialization::dump_binary(ctkeyv0));
ASSERT_NO_THROW(serialization::parse_binary(blob, ctkeyv1));
ASSERT_TRUE(ctkeyv0.size() == ctkeyv1.size());
for (size_t n = 0; n < ctkeyv0.size(); ++n)
{
ASSERT_TRUE(!memcmp(&ctkeyv0[n], &ctkeyv1[n], sizeof(ctkeyv0[n])));
}
ctkeym0 = std::vector<rct::ctkeyV>(9);
for (size_t n = 0; n < ctkeym0.size(); ++n)
{
ctkeym0[n] = std::vector<rct::ctkey>(11);
for (size_t i = 0; i < ctkeym0[n].size(); ++i)
rct::skpkGen(ctkeym0[n][i].dest, ctkeym0[n][i].mask);
}
ASSERT_NO_THROW(blob = serialization::dump_binary(ctkeym0));
ASSERT_NO_THROW(serialization::parse_binary(blob, ctkeym1));
ASSERT_TRUE(ctkeym0.size() == ctkeym1.size());
for (size_t n = 0; n < ctkeym0.size(); ++n)
{
ASSERT_TRUE(ctkeym0[n].size() == ctkeym1[n].size());
for (size_t i = 0; i < ctkeym0.size(); ++i)
{
ASSERT_TRUE(!memcmp(&ctkeym0[n][i], &ctkeym1[n][i], sizeof(ctkeym0[n][i])));
}
}
ecdh0.mask = rct::skGen();
ecdh0.amount = rct::skGen();
ASSERT_NO_THROW(blob = serialization::dump_binary(ecdh0));
ASSERT_NO_THROW(serialization::parse_binary(blob, ecdh1));
ASSERT_TRUE(!memcmp(&ecdh0.mask, &ecdh1.mask, sizeof(ecdh0.mask)));
ASSERT_TRUE(!memcmp(&ecdh0.amount, &ecdh1.amount, sizeof(ecdh0.amount)));
for (size_t n = 0; n < 64; ++n)
{
boro0.s0[n] = rct::skGen();
boro0.s1[n] = rct::skGen();
}
boro0.ee = rct::skGen();
ASSERT_NO_THROW(blob = serialization::dump_binary(boro0));
ASSERT_NO_THROW(serialization::parse_binary(blob, boro1));
ASSERT_TRUE(!memcmp(&boro0, &boro1, sizeof(boro0)));
TEST(serialization, serialize_boro_sig) {
rct::boroSig boro;
for (auto& s : boro.s0)
s = rct::skGen();
for (auto& s : boro.s1)
s = rct::skGen();
boro.ee = rct::skGen();
auto boro2 = round_trip(boro);
ASSERT_EQ(tools::view_guts(boro), tools::view_guts(boro2));
}
TEST(serialization, serializes_ringct)
{
// create a full rct signature to use its innards
std::vector<uint64_t> inamounts;
rct::ctkeyV sc, pc;
@ -595,49 +579,20 @@ TEST(Serialization, serializes_ringct_types)
amount_keys.push_back(rct::hash_to_scalar(rct::zero()));
rct::skpkGen(Sk, Pk);
destinations.push_back(Pk);
//compute rct data with mixin 3
const rct::RCTConfig rct_config{ rct::RangeProofPaddedBulletproof, 2 };
s0 = rct::genRctSimple(rct::zero(), sc, pc, destinations, inamounts, amounts, amount_keys, NULL, NULL, 0, 3, rct_config, hw::get_device("default"));
ASSERT_FALSE(s0.p.MGs.empty());
ASSERT_TRUE(s0.p.CLSAGs.empty());
mg0 = s0.p.MGs[0];
ASSERT_NO_THROW(blob = serialization::dump_binary(mg0));
ASSERT_NO_THROW(serialization::parse_binary(blob, mg1));
ASSERT_TRUE(mg0.ss.size() == mg1.ss.size());
for (size_t n = 0; n < mg0.ss.size(); ++n)
{
ASSERT_TRUE(mg0.ss[n] == mg1.ss[n]);
}
ASSERT_TRUE(mg0.cc == mg1.cc);
// mixRing and II are not serialized, they are meant to be reconstructed
ASSERT_TRUE(mg1.II.empty());
ASSERT_FALSE(s0.p.bulletproofs.empty());
bp0 = s0.p.bulletproofs.front();
ASSERT_NO_THROW(blob = serialization::dump_binary(bp0));
ASSERT_NO_THROW(serialization::parse_binary(blob, bp1));
bp1.V = bp0.V; // this is not saved, as it is reconstructed from other tx data
ASSERT_EQ(bp0, bp1);
const rct::RCTConfig rct_config_clsag{ rct::RangeProofPaddedBulletproof, 3 };
s0 = rct::genRctSimple(rct::zero(), sc, pc, destinations, inamounts, amounts, amount_keys, NULL, NULL, 0, 3, rct_config_clsag, hw::get_device("default"));
const rct::RCTConfig rct_config_clsag{ rct::RangeProofType::PaddedBulletproof, 3 };
auto s0 = rct::genRctSimple(rct::zero(), sc, pc, destinations, inamounts, amounts, amount_keys, NULL, NULL, 0, 3, rct_config_clsag, hw::get_device("default"));
ASSERT_FALSE(s0.p.CLSAGs.empty());
ASSERT_TRUE(s0.p.MGs.empty());
clsag0 = s0.p.CLSAGs[0];
ASSERT_NO_THROW(blob = serialization::dump_binary(clsag0));
ASSERT_NO_THROW(serialization::parse_binary(blob, clsag1));
ASSERT_TRUE(clsag0.s.size() == clsag1.s.size());
for (size_t n = 0; n < clsag0.s.size(); ++n)
{
ASSERT_TRUE(clsag0.s[n] == clsag1.s[n]);
}
ASSERT_TRUE(clsag0.c1 == clsag1.c1);
auto& clsag = s0.p.CLSAGs[0];
auto clsag1 = round_trip(clsag);
ASSERT_EQ(clsag.s, clsag1.s);
ASSERT_EQ(clsag.c1, clsag1.c1);
// I is not serialized, they are meant to be reconstructed
ASSERT_TRUE(clsag0.D == clsag1.D);
ASSERT_EQ(clsag.D, clsag1.D);
}
// TODO(loki): These tests are broken because they rely on testnet which has
@ -646,7 +601,7 @@ TEST(Serialization, serializes_ringct_types)
// - 2019-02-25 Doyle
#if 0
TEST(Serialization, portability_wallet)
TEST(serialization, portability_wallet)
{
const cryptonote::network_type nettype = cryptonote::TESTNET;
tools::wallet2 w(nettype);
@ -782,7 +737,7 @@ TEST(Serialization, portability_wallet)
}
#define OUTPUT_EXPORT_FILE_MAGIC "Loki output export\003"
TEST(Serialization, portability_outputs)
TEST(serialization, portability_outputs)
{
const bool restricted = false;
tools::wallet2 w(cryptonote::TESTNET, restricted);
@ -928,7 +883,7 @@ inline void serialize(Archive &a, unsigned_tx_set &x, const boost::serialization
a & x.txes;
a & x.transfers;
}
TEST(Serialization, portability_unsigned_tx)
TEST(serialization, portability_unsigned_tx)
{
// TODO(loki): We updated testnet genesis, is broken
const bool restricted = false;
@ -1128,7 +1083,7 @@ TEST(Serialization, portability_unsigned_tx)
}
#define SIGNED_TX_PREFIX "Loki signed tx set\004"
TEST(Serialization, portability_signed_tx)
TEST(serialization, portability_signed_tx)
{
const bool restricted = false;
tools::wallet2 w(cryptonote::TESTNET, restricted);

View File

@ -139,7 +139,7 @@ static bool verify_vote(service_nodes::quorum_vote_t const &vote,
TEST(service_nodes, vote_validation)
{
// Generate a quorum and the voter
cryptonote::keypair service_node_voter = cryptonote::keypair::generate(hw::get_device("default"));
cryptonote::keypair service_node_voter{hw::get_device("default")};
int voter_index = 0;
service_nodes::service_node_keys voter_keys;
@ -153,8 +153,8 @@ TEST(service_nodes, vote_validation)
for (size_t i = 0; i < state.validators.size(); ++i)
{
state.validators[i] = (i == voter_index) ? service_node_voter.pub : cryptonote::keypair::generate(hw::get_device("default")).pub;
state.workers[i] = cryptonote::keypair::generate(hw::get_device("default")).pub;
state.validators[i] = (i == voter_index) ? service_node_voter.pub : cryptonote::keypair{hw::get_device("default")}.pub;
state.workers[i] = cryptonote::keypair{hw::get_device("default")}.pub;
}
}
@ -194,9 +194,9 @@ TEST(service_nodes, vote_validation)
// Signature not valid
{
auto vote = valid_vote;
cryptonote::keypair other_voter = cryptonote::keypair::generate(hw::get_device("default"));
vote.signature = {};
auto vote = valid_vote;
cryptonote::keypair other_voter{hw::get_device("default")};
vote.signature = {};
cryptonote::vote_verification_context vvc = {};
bool result = verify_vote(vote, block_height, vvc, state);
@ -230,11 +230,11 @@ TEST(service_nodes, tx_extra_state_change_validation)
for (size_t i = 0; i < state.validators.size(); ++i)
{
cryptonote::keypair voter = cryptonote::keypair::generate(hw::get_device("default"));
cryptonote::keypair voter{hw::get_device("default")};
voters[i].pub = voter.pub;
voters[i].key = voter.sec;
state.validators[i] = voters[i].pub;
state.workers[i] = cryptonote::keypair::generate(hw::get_device("default")).pub;
state.workers[i] = cryptonote::keypair{hw::get_device("default")}.pub;
}
}

View File

@ -41,7 +41,7 @@
using namespace service_nodes;
crypto::public_key newPubKey() {
return cryptonote::keypair::generate(hw::get_device("default")).pub;
return cryptonote::keypair{hw::get_device("default")}.pub;
};
size_t calculateExcess(const swarm_snode_map_t& swarm_to_snodes) {