2019-12-10 05:20:28 +01:00
# include "loki_name_system.h"
2019-11-01 01:58:48 +01:00
# include "checkpoints/checkpoints.h"
# include "common/loki.h"
2020-02-19 01:20:00 +01:00
# include "common/util.h"
2020-02-11 07:36:34 +01:00
# include "common/base32z.h"
2020-02-19 01:20:00 +01:00
# include "crypto/hash.h"
2019-11-01 01:58:48 +01:00
# include "cryptonote_basic/cryptonote_basic.h"
# include "cryptonote_basic/cryptonote_format_utils.h"
# include "cryptonote_core/cryptonote_tx_utils.h"
# include "cryptonote_basic/tx_extra.h"
2020-02-05 06:48:32 +01:00
# include "cryptonote_core/blockchain.h"
2020-03-05 00:54:39 +01:00
# include "loki_economy.h"
2019-11-01 01:58:48 +01:00
2020-03-03 05:38:18 +01:00
# include <lokimq/hex.h>
2020-02-03 03:03:32 +01:00
# include <sqlite3.h>
2019-11-01 01:58:48 +01:00
extern " C "
{
# include "sodium.h"
}
2019-11-28 01:12:28 +01:00
# undef LOKI_DEFAULT_LOG_CATEGORY
# define LOKI_DEFAULT_LOG_CATEGORY "lns"
2019-11-01 01:58:48 +01:00
namespace lns
{
2019-11-01 07:01:42 +01:00
enum struct lns_sql_type
{
2020-02-12 01:59:40 +01:00
save_owner ,
2019-11-01 01:58:48 +01:00
save_setting ,
save_mapping ,
2020-02-05 06:48:32 +01:00
pruning ,
2019-11-01 01:58:48 +01:00
get_sentinel_start ,
get_mapping ,
2020-02-12 01:59:40 +01:00
get_mappings ,
get_mappings_by_owner ,
2020-02-12 03:42:35 +01:00
get_mappings_by_owners ,
2020-02-05 06:48:32 +01:00
get_mappings_on_height_and_newer ,
2020-02-12 03:42:35 +01:00
get_owner ,
get_setting ,
2019-11-01 01:58:48 +01:00
get_sentinel_end ,
} ;
2020-01-30 03:55:16 +01:00
enum struct lns_db_setting_row
2019-11-01 01:58:48 +01:00
{
2020-01-30 03:55:16 +01:00
id ,
top_height ,
top_hash ,
version ,
2019-11-01 01:58:48 +01:00
} ;
2020-02-12 01:59:40 +01:00
enum struct owner_record_row
2019-11-01 01:58:48 +01:00
{
2020-01-30 03:55:16 +01:00
id ,
public_key ,
2019-11-01 01:58:48 +01:00
} ;
2020-01-30 03:55:16 +01:00
enum struct mapping_record_row
2019-11-01 01:58:48 +01:00
{
2020-01-30 03:55:16 +01:00
id ,
type ,
2020-02-10 06:50:59 +01:00
name_hash ,
2020-01-30 03:55:16 +01:00
value ,
2020-02-04 04:00:53 +01:00
txid ,
prev_txid ,
2020-01-30 03:55:16 +01:00
register_height ,
2020-02-12 01:59:40 +01:00
owner_id ,
2020-02-11 07:36:34 +01:00
_count ,
2019-11-01 01:58:48 +01:00
} ;
2020-02-04 04:00:53 +01:00
static bool sql_copy_blob ( sqlite3_stmt * statement , int row , void * dest , int dest_size )
2019-11-01 01:58:48 +01:00
{
void const * blob = sqlite3_column_blob ( statement , row ) ;
int blob_len = sqlite3_column_bytes ( statement , row ) ;
assert ( blob_len = = dest_size ) ;
2020-02-04 04:00:53 +01:00
if ( blob_len ! = dest_size )
{
LOG_PRINT_L0 ( " Unexpected blob size= " < < blob_len < < " , in LNS DB does not match expected size= " < < dest_size ) ;
return false ;
}
memcpy ( dest , blob , blob_len ) ;
return true ;
2019-11-01 01:58:48 +01:00
}
2019-12-10 05:08:24 +01:00
static bool sql_run_statement ( cryptonote : : network_type nettype , lns_sql_type type , sqlite3_stmt * statement , void * context )
2019-11-01 01:58:48 +01:00
{
bool data_loaded = false ;
bool result = false ;
for ( bool infinite_loop = true ; infinite_loop ; )
{
int step_result = sqlite3_step ( statement ) ;
switch ( step_result )
{
case SQLITE_ROW :
{
switch ( type )
{
default :
{
MERROR ( " Unhandled lns type enum with value: " < < ( int ) type < < " , in: " < < __func__ ) ;
}
break ;
2020-02-12 01:59:40 +01:00
case lns_sql_type : : get_owner :
2019-11-01 01:58:48 +01:00
{
2020-02-12 01:59:40 +01:00
auto * entry = reinterpret_cast < owner_record * > ( context ) ;
entry - > id = sqlite3_column_int ( statement , static_cast < int > ( owner_record_row : : id ) ) ;
if ( ! sql_copy_blob ( statement , static_cast < int > ( owner_record_row : : public_key ) , entry - > key . data , sizeof ( entry - > key . data ) ) )
2020-02-04 04:00:53 +01:00
return false ;
2019-11-01 01:58:48 +01:00
data_loaded = true ;
}
break ;
case lns_sql_type : : get_setting :
{
auto * entry = reinterpret_cast < settings_record * > ( context ) ;
2020-01-30 03:55:16 +01:00
entry - > top_height = static_cast < uint64_t > ( sqlite3_column_int64 ( statement , static_cast < int > ( lns_db_setting_row : : top_height ) ) ) ;
2020-02-04 04:00:53 +01:00
if ( ! sql_copy_blob ( statement , static_cast < int > ( lns_db_setting_row : : top_hash ) , entry - > top_hash . data , sizeof ( entry - > top_hash . data ) ) )
return false ;
2020-01-30 03:55:16 +01:00
entry - > version = sqlite3_column_int ( statement , static_cast < int > ( lns_db_setting_row : : version ) ) ;
2019-11-01 01:58:48 +01:00
data_loaded = true ;
}
break ;
2020-02-05 06:48:32 +01:00
case lns_sql_type : : get_mappings_on_height_and_newer : /* FALLTHRU */
2020-02-12 03:42:35 +01:00
case lns_sql_type : : get_mappings_by_owners : /* FALLTHRU */
2020-02-12 01:59:40 +01:00
case lns_sql_type : : get_mappings_by_owner : /* FALLTHRU */
case lns_sql_type : : get_mappings : /* FALLTHRU */
2019-11-01 01:58:48 +01:00
case lns_sql_type : : get_mapping :
{
2020-01-21 01:08:18 +01:00
mapping_record tmp_entry = { } ;
2020-02-19 02:35:10 +01:00
int type_int = static_cast < uint16_t > ( sqlite3_column_int ( statement , static_cast < int > ( mapping_record_row : : type ) ) ) ;
if ( type_int > = tools : : enum_count < mapping_type > )
return false ;
tmp_entry . type = static_cast < mapping_type > ( type_int ) ;
2020-01-30 03:55:16 +01:00
tmp_entry . register_height = static_cast < uint16_t > ( sqlite3_column_int ( statement , static_cast < int > ( mapping_record_row : : register_height ) ) ) ;
2020-02-12 01:59:40 +01:00
tmp_entry . owner_id = sqlite3_column_int ( statement , static_cast < int > ( mapping_record_row : : owner_id ) ) ;
2019-11-01 01:58:48 +01:00
2020-01-30 03:55:16 +01:00
size_t value_len = static_cast < size_t > ( sqlite3_column_bytes ( statement , static_cast < int > ( mapping_record_row : : value ) ) ) ;
auto * value = reinterpret_cast < char const * > ( sqlite3_column_text ( statement , static_cast < int > ( mapping_record_row : : value ) ) ) ;
2020-01-23 05:56:09 +01:00
tmp_entry . value = std : : string ( value , value_len ) ;
2020-02-10 06:50:59 +01:00
if ( ! sql_copy_blob ( statement , static_cast < int > ( mapping_record_row : : name_hash ) , tmp_entry . name_hash . data , sizeof ( tmp_entry . name_hash ) ) )
return false ;
2020-02-04 04:00:53 +01:00
if ( ! sql_copy_blob ( statement , static_cast < int > ( mapping_record_row : : txid ) , tmp_entry . txid . data , sizeof ( tmp_entry . txid ) ) )
return false ;
if ( ! sql_copy_blob ( statement , static_cast < int > ( mapping_record_row : : prev_txid ) , tmp_entry . prev_txid . data , sizeof ( tmp_entry . prev_txid ) ) )
return false ;
2020-02-12 01:59:40 +01:00
int last_column = tools : : enum_count < mapping_record_row > + 1 ;
if ( ! sql_copy_blob ( statement , last_column , tmp_entry . owner . data , sizeof ( tmp_entry . owner ) ) )
return false ;
2020-02-11 07:36:34 +01:00
2020-02-04 04:00:53 +01:00
data_loaded = true ;
2020-02-12 01:59:40 +01:00
if ( type = = lns_sql_type : : get_mapping )
2020-02-11 07:36:34 +01:00
{
2020-02-12 01:59:40 +01:00
auto * entry = reinterpret_cast < mapping_record * > ( context ) ;
* entry = std : : move ( tmp_entry ) ;
2020-02-11 07:36:34 +01:00
}
2020-02-12 01:59:40 +01:00
else
2019-11-01 01:58:48 +01:00
{
2020-01-23 05:56:09 +01:00
auto * records = reinterpret_cast < std : : vector < mapping_record > * > ( context ) ;
records - > emplace_back ( std : : move ( tmp_entry ) ) ;
}
2019-11-01 01:58:48 +01:00
}
break ;
}
}
break ;
case SQLITE_BUSY : break ;
case SQLITE_DONE :
{
infinite_loop = false ;
result = ( type > lns_sql_type : : get_sentinel_start & & type < lns_sql_type : : get_sentinel_end ) ? data_loaded : true ;
break ;
}
default :
{
LOG_PRINT_L1 ( " Failed to execute statement: " < < sqlite3_sql ( statement ) < < " , reason: " < < sqlite3_errstr ( step_result ) ) ;
infinite_loop = false ;
break ;
}
}
}
sqlite3_reset ( statement ) ;
sqlite3_clear_bindings ( statement ) ;
return result ;
}
2020-02-05 06:48:32 +01:00
bool mapping_record : : active ( cryptonote : : network_type nettype , uint64_t blockchain_height ) const
{
2020-02-14 07:04:29 +01:00
if ( ! loaded ) return false ;
uint64_t expiry_blocks = lns : : expiry_blocks ( nettype , static_cast < lns : : mapping_type > ( type ) ) ;
uint64_t const last_active_height = expiry_blocks = = NO_EXPIRY ? NO_EXPIRY : ( register_height + expiry_blocks ) ;
return last_active_height > = ( blockchain_height - 1 ) ;
2020-02-05 06:48:32 +01:00
}
2020-02-12 01:59:40 +01:00
static bool sql_compile_statement ( sqlite3 * db , char const * query , int query_len , sqlite3_stmt * * statement , bool optimise_for_multiple_usage = true )
2019-11-01 01:58:48 +01:00
{
2020-02-12 15:53:16 +01:00
# if SQLITE_VERSION_NUMBER >= 3020000
2020-02-14 07:06:44 +01:00
int prepare_result = sqlite3_prepare_v3 ( db , query , query_len , optimise_for_multiple_usage ? SQLITE_PREPARE_PERSISTENT : 0 , statement , nullptr /*pzTail*/ ) ;
2020-02-12 15:53:16 +01:00
# else
2020-02-14 07:06:44 +01:00
int prepare_result = sqlite3_prepare_v2 ( db , query , query_len , statement , nullptr /*pzTail*/ ) ;
2020-02-12 15:53:16 +01:00
# endif
2019-11-01 01:58:48 +01:00
bool result = prepare_result = = SQLITE_OK ;
if ( ! result ) MERROR ( " Can not compile SQL statement: " < < query < < " , reason: " < < sqlite3_errstr ( prepare_result ) ) ;
return result ;
}
sqlite3 * init_loki_name_system ( char const * file_path )
{
sqlite3 * result = nullptr ;
int sql_init = sqlite3_initialize ( ) ;
if ( sql_init ! = SQLITE_OK )
{
MERROR ( " Failed to initialize sqlite3: " < < sqlite3_errstr ( sql_init ) ) ;
return nullptr ;
}
int sql_open = sqlite3_open ( file_path , & result ) ;
if ( sql_open ! = SQLITE_OK )
{
MERROR ( " Failed to open LNS db at: " < < file_path < < " , reason: " < < sqlite3_errstr ( sql_init ) ) ;
return nullptr ;
}
return result ;
}
2020-02-14 07:04:29 +01:00
uint64_t expiry_blocks ( cryptonote : : network_type nettype , mapping_type type , uint64_t * renew_window )
2019-11-01 07:01:42 +01:00
{
2020-02-14 07:04:29 +01:00
uint64_t renew_window_ = 0 ;
uint64_t result = NO_EXPIRY ;
if ( is_lokinet_type ( type ) )
2019-11-01 07:01:42 +01:00
{
2020-02-14 07:04:29 +01:00
renew_window_ = BLOCKS_EXPECTED_IN_DAYS ( 31 ) ;
if ( type = = mapping_type : : lokinet_1year ) result = BLOCKS_EXPECTED_IN_YEARS ( 1 ) ;
else if ( type = = mapping_type : : lokinet_2years ) result = BLOCKS_EXPECTED_IN_YEARS ( 2 ) ;
else if ( type = = mapping_type : : lokinet_5years ) result = BLOCKS_EXPECTED_IN_YEARS ( 5 ) ;
else if ( type = = mapping_type : : lokinet_10years ) result = BLOCKS_EXPECTED_IN_YEARS ( 10 ) ;
result + = renew_window_ ;
if ( nettype = = cryptonote : : FAKECHAIN )
{
renew_window_ = 10 ;
result = 10 + renew_window_ ;
}
else if ( nettype = = cryptonote : : TESTNET )
{
renew_window_ = BLOCKS_EXPECTED_IN_DAYS ( 1 ) ;
result = BLOCKS_EXPECTED_IN_DAYS ( 1 ) + renew_window_ ;
}
2019-11-01 07:01:42 +01:00
}
if ( renew_window ) * renew_window = renew_window_ ;
return result ;
}
2020-02-19 01:20:00 +01:00
crypto : : hash tx_extra_signature_hash ( epee : : span < const uint8_t > blob , crypto : : hash const & prev_txid )
2020-02-18 02:18:47 +01:00
{
2020-02-20 07:00:18 +01:00
static_assert ( sizeof ( crypto : : hash ) = = crypto_generichash_BYTES , " Using libsodium generichash for signature hash, require we fit into crypto::hash " ) ;
2020-02-18 02:18:47 +01:00
crypto : : hash result = { } ;
2020-02-19 01:20:00 +01:00
if ( blob . size ( ) < = lns : : GENERIC_VALUE_MAX )
{
2020-02-20 07:00:18 +01:00
unsigned char buffer [ lns : : GENERIC_VALUE_MAX + sizeof ( prev_txid ) ] = { } ;
size_t buffer_len = blob . size ( ) + sizeof ( prev_txid ) ;
2020-02-19 01:20:00 +01:00
memcpy ( buffer , blob . data ( ) , blob . size ( ) ) ;
memcpy ( buffer + blob . size ( ) , prev_txid . data , sizeof ( prev_txid ) ) ;
2020-02-20 07:00:18 +01:00
crypto_generichash ( reinterpret_cast < unsigned char * > ( result . data ) , sizeof ( result ) , buffer , buffer_len , NULL /*key*/ , 0 /*key_len*/ ) ;
2020-02-19 01:20:00 +01:00
}
else
{
MERROR ( " Unexpected blob len= " < < blob . size ( ) < < " greater than the blob backing buffer capacity= " < < lns : : GENERIC_VALUE_MAX ) ;
}
2020-02-18 02:18:47 +01:00
return result ;
}
2019-12-12 07:20:33 +01:00
static bool char_is_num ( char c )
{
bool result = c > = ' 0 ' & & c < = ' 9 ' ;
return result ;
}
static bool char_is_alpha ( char c )
{
bool result = ( c > = ' a ' & & c < = ' z ' ) | | ( c > = ' A ' & & c < = ' Z ' ) ;
return result ;
}
static bool char_is_alphanum ( char c )
{
bool result = char_is_num ( c ) | | char_is_alpha ( c ) ;
return result ;
}
2020-02-19 02:35:10 +01:00
bool validate_lns_name ( mapping_type type , std : : string const & name , std : : string * reason )
2019-11-01 01:58:48 +01:00
{
2020-01-23 05:56:09 +01:00
std : : stringstream err_stream ;
2020-01-24 07:48:52 +01:00
LOKI_DEFER { if ( reason ) * reason = err_stream . str ( ) ; } ;
2020-01-23 05:56:09 +01:00
2020-02-11 07:36:34 +01:00
size_t max_name_len = lns : : GENERIC_NAME_MAX ;
2020-02-19 02:35:10 +01:00
if ( type = = mapping_type : : session ) max_name_len = lns : : SESSION_DISPLAY_NAME_MAX ;
else if ( type = = mapping_type : : wallet ) max_name_len = lns : : WALLET_NAME_MAX ;
2020-02-14 07:04:29 +01:00
else if ( is_lokinet_type ( type ) ) max_name_len = lns : : LOKINET_DOMAIN_NAME_MAX ;
2020-01-24 07:48:52 +01:00
// NOTE: Validate name length
2020-02-11 07:36:34 +01:00
if ( name . empty ( ) | | name . size ( ) > max_name_len )
2020-01-24 07:48:52 +01:00
{
if ( reason )
{
2020-02-11 07:36:34 +01:00
err_stream < < " LNS type= " < < type < < " , specifies mapping from name -> value where the name's length= " < < name . size ( ) < < " is 0 or exceeds the maximum length= " < < max_name_len < < " , given name= " < < name ;
2020-01-24 07:48:52 +01:00
}
return false ;
}
// NOTE: Validate domain specific requirements
2020-02-19 02:35:10 +01:00
if ( type = = mapping_type : : session )
2020-01-24 07:48:52 +01:00
{
}
2020-02-14 07:04:29 +01:00
else if ( is_lokinet_type ( type ) )
2019-11-01 01:58:48 +01:00
{
2020-01-24 07:48:52 +01:00
// Domain has to start with a letter or digit, and can have letters, digits, or hyphens in between and must end with a .loki
// ^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.loki$
2020-01-23 00:18:42 +01:00
{
2020-01-24 07:48:52 +01:00
char const SHORTEST_DOMAIN [ ] = " a.loki " ;
2020-02-11 07:36:34 +01:00
if ( name . size ( ) < static_cast < int > ( loki : : char_count ( SHORTEST_DOMAIN ) ) )
2020-01-23 00:18:42 +01:00
{
2020-01-23 05:56:09 +01:00
if ( reason )
2020-01-23 00:18:42 +01:00
{
2020-02-11 07:36:34 +01:00
err_stream < < " LNS type=lokinet, specifies mapping from name -> value where the name is shorter than the shortest possible name= " < < SHORTEST_DOMAIN < < " , given name= " < < name ;
2020-01-23 00:18:42 +01:00
}
2020-01-23 05:56:09 +01:00
return false ;
2020-01-23 00:18:42 +01:00
}
2019-12-10 05:08:24 +01:00
}
2020-01-24 07:48:52 +01:00
if ( ! char_is_alphanum ( name [ 0 ] ) ) // Must start with alphanumeric
2019-12-10 05:08:24 +01:00
{
2020-01-24 07:48:52 +01:00
if ( reason )
2020-01-23 05:56:09 +01:00
{
2020-02-11 07:36:34 +01:00
err_stream < < " LNS type=lokinet, specifies mapping from name -> value where the name does not start with an alphanumeric character, name= " < < name ;
2020-01-23 05:56:09 +01:00
}
2020-01-24 07:48:52 +01:00
return false ;
}
2019-12-10 05:08:24 +01:00
2020-01-24 07:48:52 +01:00
char const SUFFIX [ ] = " .loki " ;
2020-02-11 07:36:34 +01:00
char const * name_suffix = name . data ( ) + ( name . size ( ) - loki : : char_count ( SUFFIX ) ) ;
2020-01-24 07:48:52 +01:00
if ( memcmp ( name_suffix , SUFFIX , loki : : char_count ( SUFFIX ) ) ! = 0 ) // Must end with .loki
{
if ( reason )
2020-01-23 05:56:09 +01:00
{
2020-02-11 07:36:34 +01:00
err_stream < < " LNS type=lokinet, specifies mapping from name -> value where the name does not end with the domain .loki, name= " < < name ;
2020-01-23 05:56:09 +01:00
}
2020-01-24 07:48:52 +01:00
return false ;
2020-01-23 05:56:09 +01:00
}
2020-01-24 07:48:52 +01:00
char const * char_preceeding_suffix = name_suffix - 1 ;
if ( ! char_is_alphanum ( char_preceeding_suffix [ 0 ] ) ) // Characted preceeding suffix must be alphanumeric
2020-01-23 05:56:09 +01:00
{
2020-01-24 07:48:52 +01:00
if ( reason )
2020-01-23 05:56:09 +01:00
{
2020-02-11 07:36:34 +01:00
err_stream < < " LNS type=lokinet, specifies mapping from name -> value where the character preceeding the <char>.loki is not alphanumeric, char= " < < char_preceeding_suffix [ 0 ] < < " , name= " < < name ;
2020-01-23 00:18:42 +01:00
}
2020-01-24 07:48:52 +01:00
return false ;
2019-12-10 05:08:24 +01:00
}
2020-01-24 07:48:52 +01:00
2020-02-11 07:36:34 +01:00
char const * begin = name . data ( ) + 1 ;
2020-01-24 07:48:52 +01:00
char const * end = char_preceeding_suffix ;
for ( char const * it = begin ; it < end ; it + + ) // Inbetween start and preceeding suffix, alphanumeric and hyphen characters permitted
2019-12-10 05:08:24 +01:00
{
2020-01-24 07:48:52 +01:00
char c = it [ 0 ] ;
if ( ! ( char_is_alphanum ( c ) | | c = = ' - ' ) )
2020-01-23 00:18:42 +01:00
{
if ( reason )
{
2020-02-11 07:36:34 +01:00
err_stream < < " LNS type=lokinet, specifies mapping from name -> value where the domain name contains more than the permitted alphanumeric or hyphen characters name= " < < name ;
2020-01-23 00:18:42 +01:00
}
2019-12-10 05:08:24 +01:00
return false ;
2020-01-23 00:18:42 +01:00
}
2020-01-24 07:48:52 +01:00
}
}
return true ;
}
2020-02-19 02:35:10 +01:00
static bool check_lengths ( mapping_type type , std : : string const & value , size_t max , bool require_exact_len , std : : string * reason )
2020-01-24 07:48:52 +01:00
{
bool result = true ;
if ( require_exact_len )
{
2020-02-11 07:36:34 +01:00
if ( value . size ( ) ! = max )
2020-01-24 07:48:52 +01:00
result = false ;
}
else
{
2020-02-11 07:36:34 +01:00
if ( value . size ( ) > max | | value . size ( ) = = 0 )
2020-01-24 07:48:52 +01:00
{
result = false ;
}
}
if ( ! result )
{
if ( reason )
{
std : : stringstream err_stream ;
2020-02-10 06:50:59 +01:00
err_stream < < " LNS type= " < < type < < " , specifies mapping from name_hash->value where the value's length= " < < value . size ( ) ;
2020-01-24 07:48:52 +01:00
if ( require_exact_len ) err_stream < < " , does not equal the required length= " ;
else err_stream < < " is 0 or exceeds the maximum length= " ;
2020-02-11 07:36:34 +01:00
err_stream < < max < < " , given value= " < < value ;
2020-01-24 07:48:52 +01:00
* reason = err_stream . str ( ) ;
}
}
return result ;
}
2020-02-19 02:35:10 +01:00
bool validate_lns_value ( cryptonote : : network_type nettype , mapping_type type , std : : string const & value , lns_value * blob , std : : string * reason )
2020-01-24 07:48:52 +01:00
{
if ( blob ) * blob = { } ;
std : : stringstream err_stream ;
2019-11-01 01:58:48 +01:00
2020-01-24 07:48:52 +01:00
cryptonote : : address_parse_info addr_info = { } ;
2020-02-03 05:46:25 +01:00
2020-02-11 07:36:34 +01:00
static_assert ( GENERIC_VALUE_MAX > = SESSION_PUBLIC_KEY_BINARY_LENGTH , " lns_value assumes the largest blob size required, all other values should be able to fit into this buffer " ) ;
2020-02-03 05:46:25 +01:00
static_assert ( GENERIC_VALUE_MAX > = LOKINET_ADDRESS_BINARY_LENGTH , " lns_value assumes the largest blob size required, all other values should be able to fit into this buffer " ) ;
static_assert ( GENERIC_VALUE_MAX > = sizeof ( addr_info . address ) , " lns_value assumes the largest blob size required, all other values should be able to fit into this buffer " ) ;
2020-02-19 02:35:10 +01:00
if ( type = = mapping_type : : wallet )
2020-01-24 07:48:52 +01:00
{
2020-02-11 07:36:34 +01:00
if ( value . empty ( ) | | ! get_account_address_from_str ( addr_info , nettype , value ) )
2020-01-24 07:48:52 +01:00
{
if ( reason )
2020-01-23 05:56:09 +01:00
{
2020-02-11 07:36:34 +01:00
if ( value . empty ( ) )
2020-01-23 05:56:09 +01:00
{
2020-02-11 07:36:34 +01:00
err_stream < < " The value= " < < value ;
2020-01-24 07:48:52 +01:00
err_stream < < " , mapping into the wallet address, specifies a wallet address of 0 length " ;
2020-01-23 05:56:09 +01:00
}
else
{
2020-02-11 07:36:34 +01:00
err_stream < < " Could not convert the wallet address string, check it is correct, value= " < < value ;
2020-01-23 05:56:09 +01:00
}
2020-01-24 07:48:52 +01:00
* reason = err_stream . str ( ) ;
2020-01-23 05:56:09 +01:00
}
2020-01-24 07:48:52 +01:00
return false ;
2020-01-23 00:18:42 +01:00
}
}
2020-01-24 07:48:52 +01:00
else
{
int max_value_len = lns : : GENERIC_VALUE_MAX ;
bool value_require_exact_len = true ;
2020-02-14 07:04:29 +01:00
if ( is_lokinet_type ( type ) ) max_value_len = ( LOKINET_ADDRESS_BINARY_LENGTH * 2 ) ;
2020-02-19 02:35:10 +01:00
else if ( type = = mapping_type : : session ) max_value_len = ( SESSION_PUBLIC_KEY_BINARY_LENGTH * 2 ) ;
2020-01-24 07:48:52 +01:00
else value_require_exact_len = false ;
2020-02-11 07:36:34 +01:00
if ( ! check_lengths ( type , value , max_value_len , value_require_exact_len , reason ) )
2020-01-24 07:48:52 +01:00
return false ;
}
2019-11-01 01:58:48 +01:00
2020-02-19 02:35:10 +01:00
if ( type = = mapping_type : : wallet )
2020-01-24 07:48:52 +01:00
{
if ( blob )
{
blob - > len = sizeof ( addr_info . address ) ;
memcpy ( blob - > buffer . data ( ) , & addr_info . address , blob - > len ) ;
}
}
2020-02-14 07:04:29 +01:00
else if ( is_lokinet_type ( type ) )
2020-02-11 07:36:34 +01:00
{
if ( value . size ( ) ! = 52 )
{
if ( reason )
{
err_stream < < " The lokinet value= " < < value < < " , should be a 52 char base32z string, length= " < < value . size ( ) ;
* reason = err_stream . str ( ) ;
}
return false ;
}
crypto : : ed25519_public_key pkey ;
if ( ! base32z : : decode ( value , pkey ) )
{
if ( reason )
{
err_stream < < " The value= " < < value < < " , was not a decodable base32z value. " ;
* reason = err_stream . str ( ) ;
}
return false ;
}
if ( blob )
{
blob - > len = sizeof ( pkey ) ;
memcpy ( blob - > buffer . data ( ) , pkey . data , blob - > len ) ;
}
}
2020-01-24 07:48:52 +01:00
else
2019-11-01 01:58:48 +01:00
{
2020-01-24 07:48:52 +01:00
// NOTE: Check value is hex
2020-02-11 07:36:34 +01:00
if ( ( value . size ( ) % 2 ) ! = 0 )
2020-01-23 00:18:42 +01:00
{
if ( reason )
{
2020-02-11 07:36:34 +01:00
err_stream < < " The value= " < < value < < " , should be a hex string that has an even length to be convertible back into binary, length= " < < value . size ( ) ;
2020-01-24 07:48:52 +01:00
* reason = err_stream . str ( ) ;
2020-01-23 00:18:42 +01:00
}
2019-11-01 01:58:48 +01:00
return false ;
2020-01-23 00:18:42 +01:00
}
2020-03-03 05:38:18 +01:00
if ( ! lokimq : : is_hex ( value ) )
2020-01-23 00:18:42 +01:00
{
2020-03-03 05:38:18 +01:00
if ( reason )
2020-01-23 00:18:42 +01:00
{
2020-03-03 05:38:18 +01:00
err_stream < < " LNS type= " < < type < < " , specifies name -> value mapping where the value is not a hex string given value= " < < value ;
* reason = err_stream . str ( ) ;
2020-01-23 00:18:42 +01:00
}
2020-03-03 05:38:18 +01:00
return false ;
}
if ( blob ) // NOTE: Given blob, write the binary output
{
blob - > len = value . size ( ) / 2 ;
assert ( blob - > len < = blob - > buffer . size ( ) ) ;
lokimq : : from_hex ( value . begin ( ) , value . end ( ) , blob - > buffer . begin ( ) ) ;
2020-01-24 07:48:52 +01:00
}
2019-12-12 07:20:33 +01:00
2020-02-19 02:35:10 +01:00
if ( type = = mapping_type : : session )
2020-01-24 07:48:52 +01:00
{
2020-02-11 07:36:34 +01:00
if ( ! ( value [ 0 ] = = ' 0 ' & & value [ 1 ] = = ' 5 ' ) ) // NOTE: Session public keys are 33 bytes, with the first byte being 0x05 and the remaining 32 being the public key.
2020-01-23 00:18:42 +01:00
{
2020-01-23 05:56:09 +01:00
if ( reason )
{
2020-02-10 06:50:59 +01:00
err_stream < < " LNS type=session, specifies mapping from name_hash->ed25519 key where the key is not prefixed with 53 (0x05), prefix= " < < std : : to_string ( value [ 0 ] ) < < " ( " < < value [ 0 ] < < " ), given ed25519= " < < value ;
2020-01-24 07:48:52 +01:00
* reason = err_stream . str ( ) ;
2020-01-23 05:56:09 +01:00
}
return false ;
2020-01-23 00:18:42 +01:00
}
2020-01-24 07:48:52 +01:00
}
}
return true ;
}
2019-12-12 07:20:33 +01:00
2020-02-19 02:35:10 +01:00
bool validate_lns_value_binary ( mapping_type type , std : : string const & value , std : : string * reason )
2020-01-24 07:48:52 +01:00
{
int max_value_len = lns : : GENERIC_VALUE_MAX ;
bool value_require_exact_len = true ;
2020-02-14 07:04:29 +01:00
if ( is_lokinet_type ( type ) ) max_value_len = LOKINET_ADDRESS_BINARY_LENGTH ;
2020-02-19 02:35:10 +01:00
else if ( type = = mapping_type : : session ) max_value_len = SESSION_PUBLIC_KEY_BINARY_LENGTH ;
else if ( type = = mapping_type : : wallet ) max_value_len = sizeof ( cryptonote : : account_public_address ) ;
2020-01-24 07:48:52 +01:00
else value_require_exact_len = false ;
2020-02-11 07:36:34 +01:00
if ( ! check_lengths ( type , value , max_value_len , value_require_exact_len , reason ) )
2020-01-24 07:48:52 +01:00
return false ;
2020-01-23 05:56:09 +01:00
2020-02-19 02:35:10 +01:00
if ( type = = lns : : mapping_type : : wallet )
2020-01-24 07:48:52 +01:00
{
// TODO(doyle): Better address validation? Is it a valid address, is it a valid nettype address?
cryptonote : : account_public_address address ;
2020-02-11 07:36:34 +01:00
memcpy ( & address , value . data ( ) , sizeof ( address ) ) ;
2020-01-24 07:48:52 +01:00
if ( ! ( crypto : : check_key ( address . m_spend_public_key ) & & crypto : : check_key ( address . m_view_public_key ) ) )
{
if ( reason )
2020-01-23 05:56:09 +01:00
{
2020-01-24 07:48:52 +01:00
std : : stringstream err_stream ;
2020-02-10 06:50:59 +01:00
err_stream < < " LNS type= " < < type < < " , specifies mapping from name_hash->wallet address where the wallet address's blob, does not generate valid public spend/view keys " ;
2020-01-24 07:48:52 +01:00
* reason = err_stream . str ( ) ;
2020-01-23 05:56:09 +01:00
}
2020-01-24 07:48:52 +01:00
return false ;
2019-12-12 07:20:33 +01:00
}
}
2019-11-01 01:58:48 +01:00
return true ;
}
2020-02-20 06:54:28 +01:00
static std : : ostream & operator < < ( std : : ostream & stream , cryptonote : : tx_extra_loki_name_system const & data )
2020-02-18 02:18:47 +01:00
{
2020-02-18 02:52:03 +01:00
stream < < " LNS Extra={ " ;
2020-02-20 06:54:28 +01:00
if ( data . command = = lns : : tx_command_t : : buy )
2020-02-18 02:18:47 +01:00
stream < < " owner= " < < data . owner ;
else
stream < < " signature= " < < epee : : string_tools : : pod_to_hex ( data . signature ) ;
2020-02-10 06:50:59 +01:00
stream < < " , type= " < < data . type < < " , name_hash= " < < data . name_hash < < " } " ;
2020-02-18 02:18:47 +01:00
return stream ;
2020-02-04 04:00:53 +01:00
}
2020-02-18 02:18:47 +01:00
static bool validate_against_previous_mapping ( lns : : name_system_db const & lns_db , uint64_t blockchain_height , cryptonote : : transaction const & tx , cryptonote : : tx_extra_loki_name_system & data , std : : string * reason = nullptr )
2020-02-04 04:00:53 +01:00
{
std : : stringstream err_stream ;
crypto : : hash expected_prev_txid = crypto : : null_hash ;
2020-02-10 06:50:59 +01:00
lns : : mapping_record mapping = lns_db . get_mapping ( data . type , data . name_hash ) ;
2020-02-20 06:54:28 +01:00
const bool updating = data . command = = lns : : tx_command_t : : update ;
2020-02-18 02:18:47 +01:00
if ( updating & & ! mapping )
2020-02-04 04:00:53 +01:00
{
2020-02-18 02:18:47 +01:00
if ( reason )
2020-02-04 04:00:53 +01:00
{
2020-02-20 06:54:28 +01:00
err_stream < < tx < < " , " < < data < < " , update requested but mapping does not exist. " ;
2020-02-18 02:18:47 +01:00
* reason = err_stream . str ( ) ;
2020-02-04 04:00:53 +01:00
}
2020-02-18 02:18:47 +01:00
return false ;
}
2020-02-04 04:00:53 +01:00
2020-02-18 02:18:47 +01:00
if ( mapping )
{
expected_prev_txid = mapping . txid ;
2020-02-14 07:04:29 +01:00
if ( updating ) // We can always update unless the mapping has expired
2020-02-18 02:18:47 +01:00
{
2020-02-14 07:04:29 +01:00
if ( ! mapping . active ( lns_db . network_type ( ) , blockchain_height ) )
2020-02-18 02:18:47 +01:00
{
2020-02-18 04:02:45 +01:00
if ( reason )
{
2020-02-20 06:54:28 +01:00
err_stream < < tx < < " , " < < data < < " , TX requested to update mapping that has already expired " ;
2020-02-18 04:02:45 +01:00
* reason = err_stream . str ( ) ;
}
2020-02-18 02:18:47 +01:00
return false ;
}
2020-02-05 06:48:32 +01:00
2020-02-18 02:52:03 +01:00
if ( data . value = = mapping . value )
{
if ( reason )
{
2020-02-20 06:54:28 +01:00
err_stream < < tx < < " , " < < data < < " , value to update to is already the same as the mapping value " ;
2020-02-18 02:52:03 +01:00
* reason = err_stream . str ( ) ;
}
return false ;
}
2020-02-18 02:18:47 +01:00
// Validate signature
{
2020-02-19 01:20:00 +01:00
crypto : : hash hash = tx_extra_signature_hash ( epee : : span < const uint8_t > ( reinterpret_cast < const uint8_t * > ( data . value . data ( ) ) , data . value . size ( ) ) , expected_prev_txid ) ;
2020-02-18 02:18:47 +01:00
if ( crypto_sign_verify_detached ( data . signature . data , reinterpret_cast < unsigned char * > ( hash . data ) , sizeof ( hash . data ) , mapping . owner . data ) ! = 0 )
{
if ( reason )
{
2020-02-20 06:54:28 +01:00
err_stream < < tx < < " , " < < data < < " , failed to verify signature for LNS update " ;
2020-02-18 02:18:47 +01:00
* reason = err_stream . str ( ) ;
}
return false ;
}
}
2020-02-04 04:00:53 +01:00
2020-02-18 02:18:47 +01:00
data . owner = mapping . owner ;
2020-02-04 04:00:53 +01:00
}
2020-02-18 02:18:47 +01:00
else
2020-02-04 04:00:53 +01:00
{
2020-02-14 07:04:29 +01:00
if ( ! is_lokinet_type ( data . type ) )
2020-02-04 04:00:53 +01:00
{
2020-02-18 02:18:47 +01:00
if ( reason )
{
lns : : owner_record owner = lns_db . get_owner_by_id ( mapping . owner_id ) ;
2020-02-10 06:50:59 +01:00
err_stream < < tx < < " , " < < data < < " , non-lokinet entries can NOT be renewed, mapping already exists with name_hash= " < < mapping . name_hash < < " , owner= " < < owner . key < < " , type= " < < mapping . type ;
2020-02-18 02:18:47 +01:00
* reason = err_stream . str ( ) ;
}
return false ;
2020-02-04 04:00:53 +01:00
}
2020-02-18 02:18:47 +01:00
uint64_t renew_window = 0 ;
2020-02-14 07:04:29 +01:00
uint64_t expiry_blocks = lns : : expiry_blocks ( lns_db . network_type ( ) , data . type , & renew_window ) ;
2020-02-18 02:18:47 +01:00
uint64_t const renew_window_offset = expiry_blocks - renew_window ;
uint64_t const min_renew_height = mapping . register_height + renew_window_offset ;
if ( min_renew_height > = blockchain_height )
2020-02-04 04:00:53 +01:00
{
2020-02-20 06:54:28 +01:00
err_stream < < tx < < " , " < < data < < " , trying to renew too early, the earliest renew height= " < < min_renew_height < < " , urrent height= " < < blockchain_height ;
2020-02-04 04:00:53 +01:00
* reason = err_stream . str ( ) ;
2020-02-18 02:18:47 +01:00
return false ; // Trying to renew too early
2020-02-04 04:00:53 +01:00
}
2020-02-18 02:18:47 +01:00
if ( mapping . active ( lns_db . network_type ( ) , blockchain_height ) )
2020-02-04 04:00:53 +01:00
{
2020-02-18 02:18:47 +01:00
// Lokinet entry expired i.e. it's no longer active. A purchase for this name is valid
// Check that the request originates from the owner of this mapping
lns : : owner_record const requester = lns_db . get_owner_by_key ( data . owner ) ;
if ( ! requester )
{
if ( reason )
{
2020-02-20 06:54:28 +01:00
err_stream < < tx < < " , " < < data < < " , trying to renew existing mapping but owner specified in LNS extra does not exist, rejected " ;
2020-02-18 02:18:47 +01:00
* reason = err_stream . str ( ) ;
}
return false ;
}
lns : : owner_record const owner = lns_db . get_owner_by_id ( mapping . owner_id ) ;
if ( ! owner )
{
if ( reason )
{
2020-02-20 06:54:28 +01:00
err_stream < < tx < < " , " < < data < < " , unexpected owner_id= " < < mapping . owner_id < < " does not exist " ;
2020-02-18 02:18:47 +01:00
* reason = err_stream . str ( ) ;
}
return false ;
}
if ( requester . id ! = owner . id )
{
if ( reason )
{
2020-02-20 06:54:28 +01:00
err_stream < < tx < < " , " < < data < < " , actual owner= " < < owner . key < < " , with owner_id= " < < mapping . owner_id < < " , does not match requester= " < < requester . key < < " , with id= " < < requester . id ;
2020-02-18 02:18:47 +01:00
* reason = err_stream . str ( ) ;
}
return false ;
}
2020-02-04 04:00:53 +01:00
}
2020-02-20 06:54:28 +01:00
// else mapping has expired, new purchase is valid
2020-02-04 04:00:53 +01:00
}
}
if ( data . prev_txid ! = expected_prev_txid )
{
if ( reason )
{
2020-02-20 06:54:28 +01:00
err_stream < < tx < < " , " < < data < < " , specified prior owner txid= " < < data . prev_txid < < " , but LNS DB reports= " < < expected_prev_txid < < " , possible competing TX was submitted and accepted before this TX was processed " ;
2020-02-04 04:00:53 +01:00
* reason = err_stream . str ( ) ;
}
return false ;
}
return true ;
}
bool name_system_db : : validate_lns_tx ( uint8_t hf_version , uint64_t blockchain_height , cryptonote : : transaction const & tx , cryptonote : : tx_extra_loki_name_system * entry , std : : string * reason ) const
2019-11-01 01:58:48 +01:00
{
2019-12-13 04:31:58 +01:00
cryptonote : : tx_extra_loki_name_system entry_ ;
if ( ! entry ) entry = & entry_ ;
2020-01-23 05:56:09 +01:00
std : : stringstream err_stream ;
2019-12-13 04:31:58 +01:00
2020-02-18 02:18:47 +01:00
if ( tx . type ! = cryptonote : : txtype : : loki_name_system )
{
if ( reason )
{
2020-02-20 06:54:28 +01:00
err_stream < < tx < < " , uses wrong tx type, expected= " < < cryptonote : : txtype : : loki_name_system ;
2020-02-18 02:18:47 +01:00
* reason = err_stream . str ( ) ;
}
return false ;
}
2019-12-13 04:31:58 +01:00
if ( ! cryptonote : : get_loki_name_system_from_tx_extra ( tx . extra , * entry ) )
2019-11-01 01:58:48 +01:00
{
2020-01-24 07:48:52 +01:00
if ( reason )
{
2020-02-20 06:54:28 +01:00
err_stream < < tx < < " , didn't have loki name service in the tx_extra " ;
2020-01-24 07:48:52 +01:00
* reason = err_stream . str ( ) ;
}
2019-11-01 01:58:48 +01:00
return false ;
}
2020-02-06 02:18:22 +01:00
if ( entry - > version ! = 0 )
{
if ( reason )
{
2020-02-20 06:54:28 +01:00
err_stream < < tx < < " , " < < * entry < < " , unexpected version= " < < std : : to_string ( entry - > version ) < < " , expected=0 " ;
2020-02-06 02:18:22 +01:00
* reason = err_stream . str ( ) ;
}
return false ;
}
2020-02-12 23:53:01 +01:00
if ( ! lns : : mapping_type_allowed ( hf_version , entry - > type ) )
2020-01-21 07:16:08 +01:00
{
2020-01-24 07:48:52 +01:00
if ( reason )
{
2020-02-20 06:54:28 +01:00
err_stream < < tx < < " , " < < * entry < < " , specifying type= " < < entry - > type < < " that is disallowed " ;
2020-01-24 07:48:52 +01:00
* reason = err_stream . str ( ) ;
}
2020-01-21 07:16:08 +01:00
return false ;
}
2020-01-24 07:48:52 +01:00
2020-02-11 07:36:34 +01:00
if ( ! validate_lns_value_binary ( entry - > type , entry - > value , reason ) )
2020-02-10 06:50:59 +01:00
return false ;
2019-11-01 01:58:48 +01:00
2020-02-04 04:00:53 +01:00
if ( ! validate_against_previous_mapping ( * this , blockchain_height , tx , * entry , reason ) )
return false ;
2020-02-10 06:50:59 +01:00
static const crypto : : hash null_name_hash = name_to_hash ( " " ) ; // Sanity check the empty name hash
if ( entry - > name_hash = = null_name_hash | | entry - > name_hash = = crypto : : null_hash )
{
if ( reason )
{
err_stream < < " LNS TX= " < < cryptonote : : get_transaction_hash ( tx ) < < " , specified the null name hash " ;
* reason = err_stream . str ( ) ;
}
return false ;
}
2020-02-14 07:04:29 +01:00
const bool updating = entry - > command = = lns : : tx_command_t : : update ;
uint64_t burn = cryptonote : : get_burned_amount_from_tx_extra ( tx . extra ) ;
2020-02-10 06:50:59 +01:00
uint64_t burn_required = updating ? 0 : burn_needed ( hf_version , entry - > type ) ;
2020-01-24 02:13:59 +01:00
if ( burn ! = burn_required )
2019-12-13 04:31:58 +01:00
{
2020-01-24 07:48:52 +01:00
if ( reason )
{
2020-02-06 02:18:22 +01:00
char const * over_or_under = burn > burn_required ? " too much " : " insufficient " ;
2020-02-20 06:54:28 +01:00
err_stream < < tx < < " , " < < * entry < < " , burned " < < over_or_under < < " loki= " < < burn < < " , require= " < < burn_required ;
2020-02-14 07:04:29 +01:00
if ( updating ) err_stream < < " , updating requires just the ordinary transaction fee " ;
2020-01-24 07:48:52 +01:00
* reason = err_stream . str ( ) ;
}
2019-12-13 04:31:58 +01:00
return false ;
}
2019-11-01 01:58:48 +01:00
return true ;
}
2020-03-05 00:15:57 +01:00
static std : : string lowercase_string ( std : : string src )
2019-12-10 05:08:24 +01:00
{
2020-03-05 00:15:57 +01:00
for ( char & ch : src )
2019-12-10 05:08:24 +01:00
{
if ( ch > = ' A ' & & ch < = ' Z ' )
ch = ch + ( ' a ' - ' A ' ) ;
}
2020-03-05 00:15:57 +01:00
return src ;
2020-02-14 07:04:29 +01:00
}
bool validate_mapping_type ( std : : string const & mapping_type_str , lns : : mapping_type * mapping_type , std : : string * reason )
{
std : : string mapping = lowercase_string ( mapping_type_str ) ;
2019-12-10 05:08:24 +01:00
2020-02-14 07:04:29 +01:00
lns : : mapping_type mapping_type_ ;
if ( mapping = = " session " ) mapping_type_ = lns : : mapping_type : : session ;
2019-12-10 05:08:24 +01:00
else
{
2020-02-14 07:04:29 +01:00
if ( reason ) * reason = " Failed to convert lns mapping (was not proper integer, or not one of the recognised: \" session \" ), string was= " + mapping_type_str ;
return false ;
2019-12-10 05:08:24 +01:00
}
2020-02-14 07:04:29 +01:00
if ( mapping_type ) * mapping_type = static_cast < lns : : mapping_type > ( mapping_type_ ) ;
2019-12-10 05:08:24 +01:00
return true ;
}
2020-02-10 06:50:59 +01:00
crypto : : hash name_to_hash ( std : : string const & name )
{
crypto : : hash result = { } ;
static_assert ( sizeof ( result ) > = crypto_generichash_BYTES , " Sodium can generate arbitrary length hashes, but recommend the minimum size for a secure hash must be >= crypto_generichash_BYTES " ) ;
crypto_generichash_blake2b ( reinterpret_cast < unsigned char * > ( result . data ) ,
sizeof ( result ) ,
reinterpret_cast < const unsigned char * > ( name . data ( ) ) ,
static_cast < unsigned long long > ( name . size ( ) ) ,
nullptr /*key*/ ,
0 /*keylen*/ ) ;
return result ;
}
2020-01-30 03:24:02 +01:00
static bool build_default_tables ( sqlite3 * db )
{
constexpr char BUILD_TABLE_SQL [ ] = R " (
2020-02-12 01:59:40 +01:00
CREATE TABLE IF NOT EXISTS " owner " (
2019-11-01 07:01:42 +01:00
" id " INTEGER PRIMARY KEY AUTOINCREMENT ,
" public_key " BLOB NOT NULL UNIQUE
) ;
CREATE TABLE IF NOT EXISTS " settings " (
2020-02-12 04:48:51 +01:00
" id " INTEGER PRIMARY KEY NOT NULL ,
2019-11-01 07:01:42 +01:00
" top_height " INTEGER NOT NULL ,
" top_hash " VARCHAR NOT NULL ,
" version " INTEGER NOT NULL
) ;
CREATE TABLE IF NOT EXISTS " mappings " (
" id " INTEGER PRIMARY KEY NOT NULL ,
" type " INTEGER NOT NULL ,
2020-02-10 06:50:59 +01:00
" name_hash " BLOB NOT NULL ,
2019-11-01 07:01:42 +01:00
" value " BLOB NOT NULL ,
2020-02-04 04:00:53 +01:00
" txid " BLOB NOT NULL ,
" prev_txid " BLOB NOT NULL ,
2019-11-01 07:01:42 +01:00
" register_height " INTEGER NOT NULL ,
2020-02-12 01:59:40 +01:00
" owner_id " INTEGER NOT NULL REFERENCES " owner " ( " id " )
2019-11-01 07:01:42 +01:00
) ;
2020-02-10 06:50:59 +01:00
CREATE UNIQUE INDEX IF NOT EXISTS " name_hash_type_id " ON mappings ( " name_hash " , " type " ) ;
2020-01-30 03:24:02 +01:00
) " ;
2019-11-01 07:01:42 +01:00
2019-11-01 01:58:48 +01:00
char * table_err_msg = nullptr ;
int table_created = sqlite3_exec ( db , BUILD_TABLE_SQL , nullptr /*callback*/ , nullptr /*callback context*/ , & table_err_msg ) ;
if ( table_created ! = SQLITE_OK )
{
MERROR ( " Can not generate SQL table for LNS: " < < ( table_err_msg ? table_err_msg : " ?? " ) ) ;
sqlite3_free ( table_err_msg ) ;
return false ;
}
return true ;
}
2020-01-30 03:55:16 +01:00
enum struct db_version { v1 , } ;
2019-11-01 01:58:48 +01:00
auto constexpr DB_VERSION = db_version : : v1 ;
2019-11-01 07:01:42 +01:00
bool name_system_db : : init ( cryptonote : : network_type nettype , sqlite3 * db , uint64_t top_height , crypto : : hash const & top_hash )
2019-11-01 01:58:48 +01:00
{
if ( ! db ) return false ;
2019-11-01 07:01:42 +01:00
this - > db = db ;
this - > nettype = nettype ;
2020-02-12 01:59:40 +01:00
char constexpr GET_MAPPINGS_BY_OWNER_SQL [ ] = R " (SELECT * FROM " mappings " JOIN " owner " ON " mappings " . " owner_id " = " owner " . " id " WHERE " public_key " = ?) " ;
char constexpr GET_MAPPINGS_ON_HEIGHT_AND_NEWER_SQL [ ] = R " (SELECT * FROM " mappings " JOIN " owner " on " mappings " . " owner_id " = " owner " . " id " WHERE " register_height " >= ?) " ;
2020-02-10 06:50:59 +01:00
char constexpr GET_MAPPING_SQL [ ] = R " (SELECT * FROM " mappings " JOIN " owner " on " mappings " . " owner_id " = " owner " . " id " WHERE " type " = ? AND " name_hash " = ?) " ;
2020-02-12 01:59:40 +01:00
char constexpr GET_OWNER_BY_ID_SQL [ ] = R " (SELECT * FROM " owner " WHERE " id " = ?) " ;
char constexpr GET_OWNER_BY_KEY_SQL [ ] = R " (SELECT * FROM " owner " WHERE " public_key " = ?) " ;
2020-02-12 04:48:51 +01:00
char constexpr GET_SETTINGS_SQL [ ] = R " (SELECT * FROM " settings " WHERE " id " = 1) " ;
2020-02-12 01:59:40 +01:00
char constexpr PRUNE_MAPPINGS_SQL [ ] = R " (DELETE FROM " mappings " WHERE " register_height " >= ?) " ;
char constexpr PRUNE_OWNERS_SQL [ ] = R " (DELETE FROM " owner " WHERE " id " NOT IN (SELECT " owner_id " FROM " mappings " )) " ;
2020-02-10 06:50:59 +01:00
char constexpr SAVE_MAPPING_SQL [ ] = R " (INSERT OR REPLACE INTO " mappings " ( " type " , " name_hash " , " value " , " txid " , " prev_txid " , " register_height " , " owner_id " ) VALUES (?,?,?,?,?,?,?)) " ;
2020-02-12 01:59:40 +01:00
char constexpr SAVE_OWNER_SQL [ ] = R " (INSERT INTO " owner " ( " public_key " ) VALUES (?);) " ;
2020-02-12 04:48:51 +01:00
char constexpr SAVE_SETTINGS_SQL [ ] = R " (INSERT OR REPLACE INTO " settings " ( " id " , " top_height " , " top_hash " , " version " ) VALUES (1,?,?,?)) " ;
2020-02-11 07:36:34 +01:00
sqlite3_stmt * test ;
2019-11-01 01:58:48 +01:00
if ( ! build_default_tables ( db ) )
return false ;
2020-02-12 01:59:40 +01:00
if (
! sql_compile_statement ( db , GET_MAPPINGS_BY_OWNER_SQL , loki : : array_count ( GET_MAPPINGS_BY_OWNER_SQL ) , & get_mappings_by_owner_sql ) | |
! sql_compile_statement ( db , GET_MAPPINGS_ON_HEIGHT_AND_NEWER_SQL , loki : : array_count ( GET_MAPPINGS_ON_HEIGHT_AND_NEWER_SQL ) , & get_mappings_on_height_and_newer_sql ) | |
2020-02-05 06:48:32 +01:00
! sql_compile_statement ( db , GET_MAPPING_SQL , loki : : array_count ( GET_MAPPING_SQL ) , & get_mapping_sql ) | |
! sql_compile_statement ( db , GET_SETTINGS_SQL , loki : : array_count ( GET_SETTINGS_SQL ) , & get_settings_sql ) | |
2020-02-12 01:59:40 +01:00
! sql_compile_statement ( db , GET_OWNER_BY_ID_SQL , loki : : array_count ( GET_OWNER_BY_ID_SQL ) , & get_owner_by_id_sql ) | |
! sql_compile_statement ( db , GET_OWNER_BY_KEY_SQL , loki : : array_count ( GET_OWNER_BY_KEY_SQL ) , & get_owner_by_key_sql ) | |
2020-02-05 06:48:32 +01:00
! sql_compile_statement ( db , PRUNE_MAPPINGS_SQL , loki : : array_count ( PRUNE_MAPPINGS_SQL ) , & prune_mappings_sql ) | |
2020-02-12 01:59:40 +01:00
! sql_compile_statement ( db , PRUNE_OWNERS_SQL , loki : : array_count ( PRUNE_OWNERS_SQL ) , & prune_owners_sql ) | |
! sql_compile_statement ( db , SAVE_MAPPING_SQL , loki : : array_count ( SAVE_MAPPING_SQL ) , & save_mapping_sql ) | |
! sql_compile_statement ( db , SAVE_SETTINGS_SQL , loki : : array_count ( SAVE_SETTINGS_SQL ) , & save_settings_sql ) | |
! sql_compile_statement ( db , SAVE_OWNER_SQL , loki : : array_count ( SAVE_OWNER_SQL ) , & save_owner_sql )
)
2019-11-01 01:58:48 +01:00
{
return false ;
}
if ( settings_record settings = get_settings ( ) )
{
if ( settings . top_height = = top_height & & settings . top_hash = = top_hash )
{
this - > last_processed_height = settings . top_height ;
assert ( settings . version = = static_cast < int > ( DB_VERSION ) ) ;
}
else
{
2020-02-12 01:59:40 +01:00
char constexpr DROP_TABLE_SQL [ ] = R " (DROP TABLE IF EXISTS " owner " ; DROP TABLE IF EXISTS " settings " ; DROP TABLE IF EXISTS " mappings " ) " ;
2019-11-01 01:58:48 +01:00
sqlite3_exec ( db , DROP_TABLE_SQL , nullptr /*callback*/ , nullptr /*callback context*/ , nullptr ) ;
if ( ! build_default_tables ( db ) ) return false ;
}
}
return true ;
}
2020-01-31 05:26:19 +01:00
struct scoped_db_transaction
2019-11-01 01:58:48 +01:00
{
2020-01-31 05:26:19 +01:00
scoped_db_transaction ( name_system_db & lns_db ) ;
~ scoped_db_transaction ( ) ;
operator bool ( ) const { return initialised ; }
name_system_db & lns_db ;
bool commit = false ; // If true, on destruction- END the transaction otherwise ROLLBACK all SQLite events prior for the lns_db
bool initialised = false ;
2019-11-01 01:58:48 +01:00
} ;
2020-01-31 05:26:19 +01:00
scoped_db_transaction : : scoped_db_transaction ( name_system_db & lns_db )
: lns_db ( lns_db )
2019-11-01 01:58:48 +01:00
{
2020-01-31 05:26:19 +01:00
if ( lns_db . transaction_begun )
2019-11-01 01:58:48 +01:00
{
2020-01-31 05:26:19 +01:00
MERROR ( " Failed to begin transaction, transaction exists previously that was not closed properly " ) ;
return ;
}
char * sql_err = nullptr ;
if ( sqlite3_exec ( lns_db . db , " BEGIN; " , nullptr , nullptr , & sql_err ) ! = SQLITE_OK )
{
MERROR ( " Failed to begin transaction " < < " , reason= " < < ( sql_err ? sql_err : " ?? " ) ) ;
2019-11-01 01:58:48 +01:00
sqlite3_free ( sql_err ) ;
2020-01-31 05:26:19 +01:00
return ;
2019-11-01 01:58:48 +01:00
}
2020-01-31 05:26:19 +01:00
initialised = true ;
lns_db . transaction_begun = true ;
}
scoped_db_transaction : : ~ scoped_db_transaction ( )
{
if ( ! initialised ) return ;
if ( ! lns_db . transaction_begun )
{
MERROR ( " Trying to apply non-existent transaction (no prior history of a db transaction beginning) to the LNS DB " ) ;
return ;
}
char * sql_err = nullptr ;
if ( sqlite3_exec ( lns_db . db , commit ? " END; " : " ROLLBACK; " , NULL , NULL , & sql_err ) ! = SQLITE_OK )
{
MERROR ( " Failed to " < < ( commit ? " end " : " rollback " ) < < " transaction to LNS DB, reason= " < < ( sql_err ? sql_err : " ?? " ) ) ;
sqlite3_free ( sql_err ) ;
return ;
}
lns_db . transaction_begun = false ;
2019-11-01 01:58:48 +01:00
}
2020-02-05 06:48:32 +01:00
static bool add_lns_entry ( lns : : name_system_db & lns_db , uint64_t height , cryptonote : : tx_extra_loki_name_system const & entry , crypto : : hash const & tx_hash )
{
2020-02-12 01:59:40 +01:00
int64_t owner_id = 0 ;
if ( owner_record owner = lns_db . get_owner_by_key ( entry . owner ) ) owner_id = owner . id ;
if ( owner_id = = 0 )
2020-02-05 06:48:32 +01:00
{
2020-02-20 06:54:28 +01:00
if ( entry . command = = lns : : tx_command_t : : update )
2020-02-18 02:18:47 +01:00
{
MERROR ( " Owner does not exist but TX received is trying to update an existing mapping (i.e. owner should already exist). TX= " < < tx_hash < < " should have failed validation prior. " ) ;
return false ;
}
2020-02-12 01:59:40 +01:00
if ( ! lns_db . save_owner ( entry . owner , & owner_id ) )
2020-02-05 06:48:32 +01:00
{
2020-02-10 06:50:59 +01:00
LOG_PRINT_L1 ( " Failed to save LNS owner to DB tx: " < < tx_hash < < " , type: " < < entry . type < < " , name_hash: " < < entry . name_hash < < " , owner: " < < entry . owner ) ;
2020-02-05 06:48:32 +01:00
return false ;
}
}
2020-02-12 01:59:40 +01:00
assert ( owner_id ! = 0 ) ;
2020-02-05 06:48:32 +01:00
2020-02-12 01:59:40 +01:00
if ( ! lns_db . save_mapping ( tx_hash , entry , height , owner_id ) )
2020-02-05 06:48:32 +01:00
{
2020-02-10 06:50:59 +01:00
LOG_PRINT_L1 ( " Failed to save LNS entry to DB tx: " < < tx_hash < < " , type: " < < entry . type < < " , name_hash: " < < entry . name_hash < < " , owner: " < < entry . owner ) ;
2020-02-05 06:48:32 +01:00
return false ;
}
return true ;
}
2019-11-01 07:01:42 +01:00
bool name_system_db : : add_block ( const cryptonote : : block & block , const std : : vector < cryptonote : : transaction > & txs )
2019-11-01 01:58:48 +01:00
{
uint64_t height = cryptonote : : get_block_height ( block ) ;
2019-11-27 03:15:06 +01:00
if ( last_processed_height > = height )
return true ;
2020-02-05 06:53:41 +01:00
scoped_db_transaction db_transaction ( * this ) ;
if ( ! db_transaction )
return false ;
2020-01-21 07:16:08 +01:00
if ( block . major_version > = cryptonote : : network_version_15_lns )
2019-11-01 01:58:48 +01:00
{
for ( cryptonote : : transaction const & tx : txs )
{
if ( tx . type ! = cryptonote : : txtype : : loki_name_system )
continue ;
cryptonote : : tx_extra_loki_name_system entry = { } ;
2020-01-23 05:56:09 +01:00
std : : string fail_reason ;
2020-02-04 04:00:53 +01:00
if ( ! validate_lns_tx ( block . major_version , height , tx , & entry , & fail_reason ) )
2019-12-13 04:31:58 +01:00
{
2020-01-30 03:55:16 +01:00
LOG_PRINT_L0 ( " LNS TX: Failed to validate for tx= " < < get_transaction_hash ( tx ) < < " . This should have failed validation earlier reason= " < < fail_reason ) ;
2019-12-13 04:31:58 +01:00
assert ( " Failed to validate acquire name service. Should already have failed validation prior " = = nullptr ) ;
2020-01-31 05:26:19 +01:00
return false ;
2019-12-13 04:31:58 +01:00
}
2019-11-01 01:58:48 +01:00
2020-02-04 04:00:53 +01:00
crypto : : hash const & tx_hash = cryptonote : : get_transaction_hash ( tx ) ;
2020-02-05 06:53:41 +01:00
if ( ! add_lns_entry ( * this , height , entry , tx_hash ) )
return false ;
2019-11-01 07:01:42 +01:00
}
2019-11-01 01:58:48 +01:00
}
last_processed_height = height ;
save_settings ( height , cryptonote : : get_block_hash ( block ) , static_cast < int > ( DB_VERSION ) ) ;
2020-02-05 06:53:41 +01:00
db_transaction . commit = true ;
2019-11-01 01:58:48 +01:00
return true ;
}
2020-02-05 06:48:32 +01:00
static bool get_txid_lns_entry ( cryptonote : : Blockchain const & blockchain , crypto : : hash txid , cryptonote : : tx_extra_loki_name_system & extra )
{
std : : vector < cryptonote : : transaction > txs ;
std : : vector < crypto : : hash > missed_txs ;
if ( ! blockchain . get_transactions ( { txid } , txs , missed_txs ) | | txs . empty ( ) )
return false ;
return cryptonote : : get_loki_name_system_from_tx_extra ( txs [ 0 ] . extra , extra ) ;
}
static bool find_closest_valid_lns_tx_extra_in_blockchain ( cryptonote : : Blockchain const & blockchain ,
lns : : mapping_record const & mapping ,
uint64_t blockchain_height ,
cryptonote : : tx_extra_loki_name_system & extra ,
crypto : : hash & tx_hash ,
uint64_t & extra_height )
{
uint64_t prev_height = static_cast < uint64_t > ( - 1 ) ;
crypto : : hash prev_txid = mapping . prev_txid ;
cryptonote : : tx_extra_loki_name_system prev_entry = { } ;
if ( ! get_txid_lns_entry ( blockchain , prev_txid , prev_entry ) ) return false ;
for ( ; ; )
{
std : : vector < uint64_t > prev_heights = blockchain . get_transactions_heights ( { prev_txid } ) ;
if ( prev_heights . empty ( ) )
{
MERROR ( " Unexpected error querying TXID= " < < prev_txid < < " , height from DB for LNS " ) ;
return false ;
}
prev_height = prev_heights [ 0 ] ;
if ( prev_height > = blockchain_height )
{
// Previous owner of mapping is after the detach height, iterate back to
// next prev entry by getting the relevant transaction, extract the LNS
// extra and continue searching.
if ( ! get_txid_lns_entry ( blockchain , prev_txid , prev_entry ) )
return false ;
prev_txid = prev_entry . prev_txid ;
}
else
{
break ;
}
}
bool result = prev_height < blockchain_height & & prev_txid ! = crypto : : null_hash ;
if ( result )
{
tx_hash = prev_txid ;
extra = std : : move ( prev_entry ) ;
extra_height = prev_height ;
}
return result ;
}
void name_system_db : : block_detach ( cryptonote : : Blockchain const & blockchain , uint64_t height )
{
std : : vector < mapping_record > new_mappings = { } ;
{
sqlite3_stmt * statement = get_mappings_on_height_and_newer_sql ;
sqlite3_clear_bindings ( statement ) ;
sqlite3_bind_int ( statement , 1 /*sql param index*/ , height ) ;
sql_run_statement ( nettype , lns_sql_type : : get_mappings_on_height_and_newer , statement , & new_mappings ) ;
}
struct lns_parts
{
uint64_t height ;
crypto : : hash tx_hash ;
cryptonote : : tx_extra_loki_name_system entry ;
} ;
std : : vector < lns_parts > entries ;
for ( auto const & mapping : new_mappings )
{
cryptonote : : tx_extra_loki_name_system entry = { } ;
uint64_t entry_height = 0 ;
crypto : : hash tx_hash = { } ;
if ( ! find_closest_valid_lns_tx_extra_in_blockchain ( blockchain , mapping , height , entry , tx_hash , entry_height ) ) continue ;
entries . push_back ( { entry_height , tx_hash , entry } ) ;
}
prune_db ( height ) ;
for ( auto const & lns : entries )
{
if ( ! add_lns_entry ( * this , lns . height , lns . entry , lns . tx_hash ) )
MERROR ( " Unexpected failure to add historical LNS into the DB on reorganization from tx= " < < lns . tx_hash ) ;
}
}
2020-02-12 01:59:40 +01:00
bool name_system_db : : save_owner ( crypto : : ed25519_public_key const & key , int64_t * row_id )
2019-11-01 01:58:48 +01:00
{
2020-02-12 01:59:40 +01:00
sqlite3_stmt * statement = save_owner_sql ;
2019-11-01 01:58:48 +01:00
sqlite3_clear_bindings ( statement ) ;
sqlite3_bind_blob ( statement , 1 /*sql param index*/ , & key , sizeof ( key ) , nullptr /*destructor*/ ) ;
2020-02-12 01:59:40 +01:00
bool result = sql_run_statement ( nettype , lns_sql_type : : save_owner , statement , nullptr ) ;
2019-11-01 01:58:48 +01:00
if ( row_id ) * row_id = sqlite3_last_insert_rowid ( db ) ;
return result ;
}
2020-02-12 01:59:40 +01:00
bool name_system_db : : save_mapping ( crypto : : hash const & tx_hash , cryptonote : : tx_extra_loki_name_system const & src , uint64_t height , int64_t owner_id )
2019-11-01 01:58:48 +01:00
{
2019-11-01 07:01:42 +01:00
sqlite3_stmt * statement = save_mapping_sql ;
2020-02-04 04:00:53 +01:00
sqlite3_bind_int ( statement , static_cast < int > ( mapping_record_row : : type ) , static_cast < uint16_t > ( src . type ) ) ;
2020-02-10 06:50:59 +01:00
sqlite3_bind_blob ( statement , static_cast < int > ( mapping_record_row : : name_hash ) , src . name_hash . data , sizeof ( src . name_hash ) , nullptr /*destructor*/ ) ;
2020-02-04 04:00:53 +01:00
sqlite3_bind_blob ( statement , static_cast < int > ( mapping_record_row : : value ) , src . value . data ( ) , src . value . size ( ) , nullptr /*destructor*/ ) ;
sqlite3_bind_blob ( statement , static_cast < int > ( mapping_record_row : : txid ) , tx_hash . data , sizeof ( tx_hash ) , nullptr /*destructor*/ ) ;
sqlite3_bind_blob ( statement , static_cast < int > ( mapping_record_row : : prev_txid ) , src . prev_txid . data , sizeof ( src . prev_txid ) , nullptr /*destructor*/ ) ;
2020-01-30 03:55:16 +01:00
sqlite3_bind_int64 ( statement , static_cast < int > ( mapping_record_row : : register_height ) , static_cast < int64_t > ( height ) ) ;
2020-02-12 01:59:40 +01:00
sqlite3_bind_int64 ( statement , static_cast < int > ( mapping_record_row : : owner_id ) , owner_id ) ;
2019-12-10 05:08:24 +01:00
bool result = sql_run_statement ( nettype , lns_sql_type : : save_mapping , statement , nullptr ) ;
2019-11-01 01:58:48 +01:00
return result ;
}
bool name_system_db : : save_settings ( uint64_t top_height , crypto : : hash const & top_hash , int version )
{
2019-11-01 07:01:42 +01:00
sqlite3_stmt * statement = save_settings_sql ;
2020-01-30 03:55:16 +01:00
sqlite3_bind_int64 ( statement , static_cast < int > ( lns_db_setting_row : : top_height ) , top_height ) ;
2020-02-12 04:48:51 +01:00
sqlite3_bind_blob ( statement , static_cast < int > ( lns_db_setting_row : : top_hash ) , top_hash . data , sizeof ( top_hash ) , nullptr /*destructor*/ ) ;
2020-01-30 03:55:16 +01:00
sqlite3_bind_int ( statement , static_cast < int > ( lns_db_setting_row : : version ) , version ) ;
2019-12-10 05:08:24 +01:00
bool result = sql_run_statement ( nettype , lns_sql_type : : save_setting , statement , nullptr ) ;
2019-11-01 01:58:48 +01:00
return result ;
}
2020-02-05 06:48:32 +01:00
bool name_system_db : : prune_db ( uint64_t height )
2019-11-01 07:01:42 +01:00
{
2020-02-05 06:48:32 +01:00
{
sqlite3_stmt * statement = prune_mappings_sql ;
sqlite3_bind_int64 ( statement , 1 /*sql param index*/ , height ) ;
if ( ! sql_run_statement ( nettype , lns_sql_type : : pruning , statement , nullptr ) ) return false ;
}
2019-11-01 07:01:42 +01:00
2020-02-05 06:48:32 +01:00
{
2020-02-12 01:59:40 +01:00
sqlite3_stmt * statement = prune_owners_sql ;
2020-02-05 06:48:32 +01:00
if ( ! sql_run_statement ( nettype , lns_sql_type : : pruning , statement , nullptr ) ) return false ;
}
2019-11-01 07:01:42 +01:00
2020-02-05 06:48:32 +01:00
return true ;
2019-11-01 07:01:42 +01:00
}
2020-02-12 01:59:40 +01:00
owner_record name_system_db : : get_owner_by_key ( crypto : : ed25519_public_key const & key ) const
2019-11-01 01:58:48 +01:00
{
2020-02-12 01:59:40 +01:00
sqlite3_stmt * statement = get_owner_by_key_sql ;
2019-11-01 01:58:48 +01:00
sqlite3_clear_bindings ( statement ) ;
sqlite3_bind_blob ( statement , 1 /*sql param index*/ , & key , sizeof ( key ) , nullptr /*destructor*/ ) ;
2020-02-12 01:59:40 +01:00
owner_record result = { } ;
result . loaded = sql_run_statement ( nettype , lns_sql_type : : get_owner , statement , & result ) ;
2019-11-01 01:58:48 +01:00
return result ;
}
2020-02-12 01:59:40 +01:00
owner_record name_system_db : : get_owner_by_id ( int64_t owner_id ) const
2019-11-01 01:58:48 +01:00
{
2020-02-12 01:59:40 +01:00
sqlite3_stmt * statement = get_owner_by_id_sql ;
2019-11-01 01:58:48 +01:00
sqlite3_clear_bindings ( statement ) ;
2020-02-12 01:59:40 +01:00
sqlite3_bind_int ( statement , 1 /*sql param index*/ , owner_id ) ;
2019-11-01 01:58:48 +01:00
2020-02-12 01:59:40 +01:00
owner_record result = { } ;
result . loaded = sql_run_statement ( nettype , lns_sql_type : : get_owner , statement , & result ) ;
2019-11-01 01:58:48 +01:00
return result ;
}
2020-02-10 06:50:59 +01:00
mapping_record name_system_db : : get_mapping ( mapping_type type , crypto : : hash const & name_hash ) const
2019-11-01 01:58:48 +01:00
{
2019-11-01 07:01:42 +01:00
sqlite3_stmt * statement = get_mapping_sql ;
2019-11-01 01:58:48 +01:00
sqlite3_clear_bindings ( statement ) ;
2020-02-10 06:50:59 +01:00
sqlite3_bind_int ( statement , 1 /*sql param index*/ , static_cast < uint16_t > ( type ) ) ;
sqlite3_bind_blob ( statement , 2 /*sql param index*/ , name_hash . data , sizeof ( name_hash ) , nullptr /*destructor*/ ) ;
2019-11-01 01:58:48 +01:00
mapping_record result = { } ;
2019-12-10 05:08:24 +01:00
result . loaded = sql_run_statement ( nettype , lns_sql_type : : get_mapping , statement , & result ) ;
2019-11-01 01:58:48 +01:00
return result ;
}
2020-02-10 06:50:59 +01:00
std : : vector < mapping_record > name_system_db : : get_mappings ( std : : vector < uint16_t > const & types , crypto : : hash const & name_hash ) const
2020-02-12 01:59:40 +01:00
{
std : : string sql_statement ;
// Generate string statement
2020-02-14 07:04:29 +01:00
if ( types . size ( ) )
2020-02-12 01:59:40 +01:00
{
2020-02-10 06:50:59 +01:00
char constexpr SQL_PREFIX [ ] = R " (SELECT * FROM " mappings " JOIN " owner " ON " mappings " . " owner_id " = " owner " . " id " WHERE " name_hash " = ? AND " type " in () " ;
2020-02-12 01:59:40 +01:00
char constexpr SQL_SUFFIX [ ] = R " ()) " ;
std : : stringstream stream ;
stream < < SQL_PREFIX ;
for ( size_t i = 0 ; i < types . size ( ) ; i + + )
{
stream < < " ? " ;
if ( i < ( types . size ( ) - 1 ) ) stream < < " , " ;
}
stream < < SQL_SUFFIX ;
sql_statement = stream . str ( ) ;
}
2020-02-14 07:04:29 +01:00
else
{
sql_statement = R " (SELECT * FROM " mappings " JOIN " owner " ON " mappings " . " owner_id " = " owner " . " id " WHERE " name " = ?) " ;
}
2020-02-12 01:59:40 +01:00
// Compile Statement
std : : vector < mapping_record > result ;
sqlite3_stmt * statement = nullptr ;
if ( ! sql_compile_statement ( db , sql_statement . c_str ( ) , sql_statement . size ( ) , & statement , false /*optimise_for_multiple_usage*/ ) )
return result ;
// Bind parameters statements
int sql_param_index = 1 ;
2020-02-10 06:50:59 +01:00
sqlite3_bind_blob ( statement , sql_param_index + + , name_hash . data , sizeof ( name_hash ) , nullptr /*destructor*/ ) ;
2020-02-12 01:59:40 +01:00
for ( auto type : types )
sqlite3_bind_int ( statement , sql_param_index + + , type ) ;
assert ( ( sql_param_index - 1 ) = = static_cast < int > ( 1 /*name*/ + types . size ( ) ) ) ;
// Execute
sql_run_statement ( nettype , lns_sql_type : : get_mappings , statement , & result ) ;
return result ;
}
2020-02-12 03:42:35 +01:00
std : : vector < mapping_record > name_system_db : : get_mappings_by_owners ( std : : vector < crypto : : ed25519_public_key > const & keys ) const
2020-01-21 01:08:18 +01:00
{
2020-02-12 03:42:35 +01:00
std : : string sql_statement ;
// Generate string statement
2020-01-21 01:08:18 +01:00
{
2020-02-12 03:42:35 +01:00
char constexpr SQL_PREFIX [ ] = R " (SELECT * FROM " mappings " JOIN " owner " ON " mappings " . " owner_id " = " owner " . " id " WHERE " public_key " in () " ;
char constexpr SQL_SUFFIX [ ] = R " ()) " ;
std : : stringstream stream ;
stream < < SQL_PREFIX ;
for ( size_t i = 0 ; i < keys . size ( ) ; i + + )
{
stream < < " ? " ;
if ( i < ( keys . size ( ) - 1 ) ) stream < < " , " ;
}
stream < < SQL_SUFFIX ;
sql_statement = stream . str ( ) ;
2020-01-21 01:08:18 +01:00
}
2020-02-12 03:42:35 +01:00
// Compile Statement
std : : vector < mapping_record > result ;
sqlite3_stmt * statement = nullptr ;
if ( ! sql_compile_statement ( db , sql_statement . c_str ( ) , sql_statement . size ( ) , & statement , false /*optimise_for_multiple_usage*/ ) )
return result ;
// Bind parameters statements
int sql_param_index = 1 ;
for ( auto const & key : keys )
sqlite3_bind_blob ( statement , sql_param_index + + , key . data , sizeof ( key ) , nullptr /*destructor*/ ) ;
assert ( ( sql_param_index - 1 ) = = static_cast < int > ( keys . size ( ) ) ) ;
// Execute
sql_run_statement ( nettype , lns_sql_type : : get_mappings_by_owners , statement , & result ) ;
return result ;
}
std : : vector < mapping_record > name_system_db : : get_mappings_by_owner ( crypto : : ed25519_public_key const & key ) const
{
std : : vector < mapping_record > result = { } ;
2020-02-12 01:59:40 +01:00
sqlite3_stmt * statement = get_mappings_by_owner_sql ;
2020-02-11 07:36:34 +01:00
sqlite3_clear_bindings ( statement ) ;
sqlite3_bind_blob ( statement , 1 /*sql param index*/ , key . data , sizeof ( key ) , nullptr /*destructor*/ ) ;
2020-02-12 01:59:40 +01:00
sql_run_statement ( nettype , lns_sql_type : : get_mappings_by_owner , statement , & result ) ;
2020-02-11 07:36:34 +01:00
return result ;
}
2019-11-01 01:58:48 +01:00
settings_record name_system_db : : get_settings ( ) const
{
2020-02-12 04:48:51 +01:00
sqlite3_stmt * statement = get_settings_sql ;
2019-11-01 01:58:48 +01:00
settings_record result = { } ;
2019-12-10 05:08:24 +01:00
result . loaded = sql_run_statement ( nettype , lns_sql_type : : get_setting , statement , & result ) ;
2019-11-01 01:58:48 +01:00
return result ;
}
} ; // namespace service_nodes