Documented a bit of code that I had started to forget.

Remade hashing according to the dry principle,
eliminating much code repetition.
Introduced c++20 "concepts" to radically reduce
verbose and incomprehensible template metacode.

modified:   src/db_accessors.h
modified:   src/ristretto255.h
modified:   src/testbed.cpp
This commit is contained in:
Cheng 2023-09-24 15:21:18 +10:00
parent 60eece9269
commit fc9f82b6e5
No known key found for this signature in database
GPG Key ID: 571C3A9C3B9E6FCA
4 changed files with 51 additions and 65 deletions

View File

@ -35,8 +35,11 @@ void queue_error_message(const char* psz) {
void queue_fatal_error(const char* psz) { void queue_fatal_error(const char* psz) {
// Used where throwing immediately would be disastrous, as in a destructor or when constructing the main frame // Used where throwing immediately would be disastrous, as in a destructor or when constructing the main frame
if (!errorCode)errorCode = 10; if (!errorCode)errorCode = 10;
queue_error_message(psz); queue_error_message(psz); // Message continues to display,
singletonFrame->Close(); // even after App window that contains the frame has closed
singletonFrame->Close(); // Generates close event, which is handled as usual after this returns
// Main window closes after the close message is handled, but App still running, displaying the message
// put up by queue_error_message. When that message is closed, App finally terminates.
} }
MyException::MyException(const char* sz, int i, const char* func__, const char* FILE__) noexcept : MyException::MyException(const char* sz, int i, const char* func__, const char* FILE__) noexcept :

View File

@ -107,7 +107,7 @@ namespace ro {
template < typename T, template < typename T,
typename std::enable_if<!is_sqlite3_field_type<T>::value, int >::type dummy_arg = 0 > typename std::enable_if<!is_sqlite3_field_type<T>::value, int >::type dummy_arg = 0 >
void bind(int i, const T& j) { void bind(int i, const T& j) {
static_assert(ro::is_serializable<T>::value, "Don't know how to store this type in a database"); static_assert(ro::serializable<T>, "Don't know how to store this type in a database");
(*this)->Isqlite3_bind(i, ro::serialize(j)); (*this)->Isqlite3_bind(i, ro::serialize(j));
} }
typedef Icompiled_sql::sql_result result; typedef Icompiled_sql::sql_result result;

View File

@ -175,6 +175,8 @@ namespace ro {
apt to unhelpfully and unexpectedly turn it into a wxString, apt to unhelpfully and unexpectedly turn it into a wxString,
If you make wxStrings hashable, suprising things become hashable. If you make wxStrings hashable, suprising things become hashable.
However, we do make the strange data structure provided by wxString.ToUTF8() hashable,
so that the wxString will not be implicitly hashable, but will be explicitly hashable.
*/ */
// data structure containing a serialized signed integer. // data structure containing a serialized signed integer.
@ -334,7 +336,7 @@ namespace ro {
currency is fixed at 2^64-1 which will suffice for a thousand years. currency is fixed at 2^64-1 which will suffice for a thousand years.
Or we might allow arbitrary precision floating point with powers of Or we might allow arbitrary precision floating point with powers of
a thousand, so that sensible numbers to a human are represented by a thousand, so that sensible numbers to a human are represented by
sensible numbers in the actuall representation. sensible numbers in the actual representation.
secret keys, scalars are actually much larger numbers, modulo secret keys, scalars are actually much larger numbers, modulo
0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ecU 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ecU
@ -359,8 +361,13 @@ namespace ro {
} }
static constexpr bool value = is_serializable::template test<T,Args...>(); static constexpr bool value = is_serializable::template test<T,Args...>();
}; };
static_assert(ro::is_serializable<std::span<const char>>::value);
static_assert(ro::is_serializable<std::span<const byte>>::value); template<typename... Args>
concept serializable = is_serializable<Args...>::value;
static_assert( !serializable<double>
&& serializable<std::span<const byte>, char*, std::span<const char>>,
"concepts needed");
template<class T> ro::CompileSizedString< (2 * sizeof(T))>bin2hex(const T& pt) { template<class T> ro::CompileSizedString< (2 * sizeof(T))>bin2hex(const T& pt) {
ro::CompileSizedString< (2 * sizeof(T))>sz; ro::CompileSizedString< (2 * sizeof(T))>sz;
@ -392,7 +399,12 @@ namespace ro {
} //End ro namespace } //End ro namespace
namespace ristretto255 { namespace ristretto255 {
using ro::to_base64_string, ro::is_serializable; using
ro::to_base64_string, ro::is_serializable,
ro::serialize, ro::bin2hex, ro::hex2bin,
ro::bin2hex, ro::CompileSizedString,
ro::serializable;
;
// a class representing ristretto255 elliptic points // a class representing ristretto255 elliptic points
class point; class point;
@ -449,64 +461,35 @@ namespace ristretto255 {
assert(i == 0); assert(i == 0);
} }
template<typename T> template<serializable T, typename... Args>
typename std::enable_if< hsh<hashsize>& hashinto(const T& j, Args... args) {
ro::is_serializable<const T>::value,
ristretto255::hsh<hashsize>&
>::type operator << (const T& j) {
int i{ 1 };
if constexpr (std::is_same_v<std::remove_cvref_t<T>, std::span<const byte> >) {
i = crypto_generichash_blake2b_update(
&(this->st),
&j[0],
j.size()
);
}
else if constexpr (std::is_same_v<std::remove_cvref_t<T>, const char*>) {
i = crypto_generichash_blake2b_update(
&(this->st),
(const unsigned char*)(j),
strlen(j) + 1
);
}
else {
static_assert(is_serializable<const T>::value, "don't know a machine and compiler independent representation of this type");
auto sj = ro::serialize(j); auto sj = ro::serialize(j);
i = crypto_generichash_blake2b_update( int i = crypto_generichash_blake2b_update(
&(this->st), &(this->st),
(const unsigned char*)&sj[0], (const unsigned char*)&sj[0],
sj.size() sj.size()
); );
if (i) throw HashReuseException();
if constexpr (sizeof...(args) > 0) {
(*this).hashinto(args...);
} }
return *this;
}
template<serializable T> hsh<hashsize>& operator << (const T& j) {
auto sj = ro::serialize(j);
auto i = crypto_generichash_blake2b_update(
&(this->st),
(const unsigned char*)&sj[0],
sj.size()
);
if (i) throw HashReuseException(); if (i) throw HashReuseException();
return *this; return *this;
} }
template<typename T, typename... Args,
typename std::enable_if< is_serializable<const T, Args...>::value, int >::type dummy_arg = 0
>explicit hsh(const T first, Args... args) {
int i{ crypto_generichash_blake2b_init(
&st,
nullptr,0,
hashsize / 8)
};
assert(i == 0);
(*this) << first;
if constexpr (sizeof...(args) > 0) {
(*this).hashinto(args...);
}
}
template<typename T, typename... Args,
typename std::enable_if< ro::is_serializable<const T>::value, int >::type dummy_arg = 0
> void hashinto(const T first, Args... args) {
*this << first;
if constexpr (sizeof...(args) > 0) {
(*this).hashinto(args...);
}
}
}; };
static_assert(!ro::is_serializable<hsh<256> >::value, "Don't want to partially hash partial hashes"); static_assert(!serializable<hsh<256> >, "Don't want to partially hash partial hashes");
// This constructs a finalized hash. // This constructs a finalized hash.
// If it has one argument, and that argument is hsh (unfinalized hash) object, // If it has one argument, and that argument is hsh (unfinalized hash) object,
@ -553,9 +536,7 @@ namespace ristretto255 {
assert(i == 0); assert(i == 0);
if (i) throw HashReuseException(); if (i) throw HashReuseException();
} }
template<typename T, typename... Args, template< serializable T, typename... Args>explicit hash(const T& first, Args... args) {
typename std::enable_if< ro::is_serializable<const T, Args...>::value, int >::type dummy_arg = 0
>explicit hash(const T& first, Args... args) {
hsh<hashsize> in; hsh<hashsize> in;
in << first; in << first;
if constexpr (sizeof...(args) > 0) { if constexpr (sizeof...(args) > 0) {

View File

@ -25,11 +25,13 @@ void ascii2test();
extern const uint8_t* const ascii2six; extern const uint8_t* const ascii2six;
namespace testbed { namespace testbed {
using ristretto255::hash, ristretto255::hsh, ristretto255::scalar, using /*ristretto255::hash, ristretto255::hsh, */ristretto255::scalar,
ristretto255::point, ro::serialize, ro::bin2hex, ro::hex2bin, ristretto255::point, ro::serialize, ro::bin2hex, ro::hex2bin,
ro::bin2hex, ro::fasthash,ro::CompileSizedString ; ro::bin2hex, ro::fasthash, ro::CompileSizedString,
ro::base58, ro::serializable;
static constexpr char SrcFilename[]{ "src/testbed.cpp" }; static constexpr char SrcFilename[]{ "src/testbed.cpp" };
/* experimental code called during unit test /* experimental code called during unit test
Anything here is a residue of forgotten experiments, Anything here is a residue of forgotten experiments,
and can safely be thrown away and can safely be thrown away
@ -55,7 +57,7 @@ If using postmessage, execution of the code waits for the dialog to
If using queumessage, the testbed code will complete while the dialog If using queumessage, the testbed code will complete while the dialog
is waiting, data cannot be returned for use in the testbed code, is waiting, data cannot be returned for use in the testbed code,
and uncaught exceptions will appear in the main UI. and uncaught exceptions in the dialog queued will appear in the main UI.
*/ */
void testbed() { void testbed() {