Rename data/entry -> lns_extra, bit_field_* -> field_*

This commit is contained in:
Doyle 2020-03-03 12:20:54 +11:00
parent 1bff385cac
commit aaf078d4f2
3 changed files with 105 additions and 109 deletions

View File

@ -67,13 +67,16 @@ namespace lns
{
enum struct extra_field : uint8_t
{
owner = 1 << 0,
backup_owner = 1 << 1,
signature = 1 << 2,
encrypted_value = 1 << 3,
buy_no_backup = (extra_field::owner | extra_field::encrypted_value),
buy = (extra_field::buy_no_backup | extra_field::backup_owner),
all = (extra_field::buy | extra_field::signature),
owner = 1 << 0,
backup_owner = 1 << 1,
signature = 1 << 2,
encrypted_value = 1 << 3,
// Bit Masks
updatable_fields = (extra_field::owner | extra_field::backup_owner | extra_field::encrypted_value),
buy_no_backup = (extra_field::owner | extra_field::encrypted_value),
buy = (extra_field::buy_no_backup | extra_field::backup_owner),
all = (extra_field::updatable_fields | extra_field::signature),
};
};
@ -404,16 +407,17 @@ namespace cryptonote
crypto::hash name_hash;
crypto::hash prev_txid = crypto::null_hash; // previous txid that purchased the mapping
lns::extra_field fields;
crypto::generic_public_key owner = {}; // only serialized if command == tx_command::buy
crypto::generic_public_key backup_owner = {}; // only serialized if command == tx_command::buy
crypto::generic_signature signature = {}; // only serialized if command == tx_command::update
std::string encrypted_value; // encrypted binary format of the value in the name->value mapping
constexpr void unset_bit_field (lns::extra_field bit) { fields = static_cast<lns::extra_field>(static_cast<uint8_t>(fields) & ~static_cast<uint8_t>(bit)); }
constexpr void set_bit_field (lns::extra_field bit) { fields = static_cast<lns::extra_field>(static_cast<uint8_t>(fields) | static_cast<uint8_t>(bit)); }
constexpr bool bit_field_is_set (lns::extra_field bit) const { return (static_cast<uint8_t>(fields) & static_cast<uint8_t>(bit)); }
constexpr bool bit_field_match (lns::extra_field bit) const { return ((static_cast<uint8_t>(fields) ^ static_cast<uint8_t>(bit)) == 0); }
constexpr bool is_updating () const { return bit_field_is_set(lns::extra_field::signature); }
constexpr bool is_buying () const { return !is_updating() && (bit_field_is_set(lns::extra_field::buy) || bit_field_is_set(lns::extra_field::buy_no_backup)); }
crypto::generic_public_key owner = {};
crypto::generic_public_key backup_owner = {};
crypto::generic_signature signature = {};
std::string encrypted_value; // binary format of the name->value mapping
constexpr void set_field (lns::extra_field bit) { fields = static_cast<lns::extra_field>(static_cast<uint8_t>(fields) | static_cast<uint8_t>(bit)); }
constexpr bool field_is_set (lns::extra_field bit) const { return (static_cast<uint8_t>(fields) & static_cast<uint8_t>(bit)) == static_cast<uint8_t>(bit); }
constexpr bool field_any_set(lns::extra_field bit) const { return (static_cast<uint8_t>(fields) & static_cast<uint8_t>(bit)) > 0; }
constexpr bool is_updating() const { return field_is_set(lns::extra_field::signature) && field_any_set(lns::extra_field::updatable_fields); }
constexpr bool is_buying() const { return (fields == lns::extra_field::buy || fields == lns::extra_field::buy_no_backup); }
static tx_extra_loki_name_system make_buy(crypto::generic_public_key const &owner, crypto::generic_public_key const *backup_owner, lns::mapping_type type, crypto::hash const &name_hash, std::string const &encrypted_value, crypto::hash const &prev_txid)
{
@ -445,23 +449,23 @@ namespace cryptonote
result.signature = signature;
result.type = type;
result.name_hash = name_hash;
result.set_bit_field(lns::extra_field::signature);
result.set_field(lns::extra_field::signature);
if (encrypted_value.size())
{
result.set_bit_field(lns::extra_field::encrypted_value);
result.set_field(lns::extra_field::encrypted_value);
result.encrypted_value = std::string(reinterpret_cast<char const *>(encrypted_value.data()), encrypted_value.size());
}
if (owner)
{
result.set_bit_field(lns::extra_field::owner);
result.set_field(lns::extra_field::owner);
result.owner = *owner;
}
if (backup_owner)
{
result.set_bit_field(lns::extra_field::backup_owner);
result.set_field(lns::extra_field::backup_owner);
result.backup_owner = *backup_owner;
}
@ -475,10 +479,10 @@ namespace cryptonote
FIELD(name_hash)
FIELD(prev_txid)
ENUM_FIELD(fields, fields <= lns::extra_field::all)
if (bit_field_is_set(lns::extra_field::owner)) FIELD(owner);
if (bit_field_is_set(lns::extra_field::backup_owner)) FIELD(backup_owner);
if (bit_field_is_set(lns::extra_field::signature)) FIELD(signature);
if (bit_field_is_set(lns::extra_field::encrypted_value)) FIELD(encrypted_value);
if (field_is_set(lns::extra_field::owner)) FIELD(owner);
if (field_is_set(lns::extra_field::backup_owner)) FIELD(backup_owner);
if (field_is_set(lns::extra_field::signature)) FIELD(signature);
if (field_is_set(lns::extra_field::encrypted_value)) FIELD(encrypted_value);
END_SERIALIZE()
};

View File

@ -640,155 +640,152 @@ static bool verify_lns_signature(crypto::hash const &hash, crypto::generic_signa
return true;
}
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)
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 &lns_extra, std::string *reason = nullptr)
{
std::stringstream err_stream;
LOKI_DEFER { if (reason && reason->empty()) *reason = err_stream.str(); };
crypto::hash expected_prev_txid = crypto::null_hash;
std::string name_hash = hash_to_base64(data.name_hash);
lns::mapping_record mapping = lns_db.get_mapping(data.type, name_hash);
if (data.is_updating() && !mapping)
std::string name_hash = hash_to_base64(lns_extra.name_hash);
lns::mapping_record mapping = lns_db.get_mapping(lns_extra.type, name_hash);
if (lns_extra.is_updating() && !mapping)
{
if (reason) err_stream << tx << ", " << data << ", update requested but mapping does not exist.";
if (reason) err_stream << tx << ", " << lns_extra << ", update requested but mapping does not exist.";
return false;
}
if (mapping)
{
expected_prev_txid = mapping.txid;
if (data.is_updating())
if (lns_extra.is_updating())
{
char const SPECIFYING_SAME_VALUE_ERR[] = ", field to update is specifying the same mapping ";
char const VALUE_SPECIFIED_BUT_NOT_REQUESTED[] = ", given field to update but field is not requested to be serialised=";
if (is_lokinet_type(data.type) && !mapping.active(lns_db.network_type(), blockchain_height))
if (is_lokinet_type(lns_extra.type) && !mapping.active(lns_db.network_type(), blockchain_height))
{
if (reason) err_stream << tx << ", " << data << ", TX requested to update mapping that has already expired";
if (reason) err_stream << tx << ", " << lns_extra << ", TX requested to update mapping that has already expired";
return false;
}
if (!data.bit_field_is_set(lns::extra_field::owner) &&
!data.bit_field_is_set(lns::extra_field::encrypted_value) &&
!data.bit_field_is_set(lns::extra_field::backup_owner))
if (!lns_extra.field_is_set(lns::extra_field::owner) &&
!lns_extra.field_is_set(lns::extra_field::encrypted_value) &&
!lns_extra.field_is_set(lns::extra_field::backup_owner))
{
if (reason) err_stream << tx << ", " << data << ", TX requested to update but specified no fields to update";
if (reason) err_stream << tx << ", " << lns_extra << ", TX requested to update but specified no fields to update";
return false;
}
if (data.bit_field_is_set(lns::extra_field::encrypted_value))
if (lns_extra.field_is_set(lns::extra_field::encrypted_value))
{
if (mapping.encrypted_value == data.encrypted_value)
auto span_a = epee::strspan<uint8_t>(lns_extra.encrypted_value);
auto span_b = mapping.encrypted_value.to_span();
if (span_a.size() == span_b.size() && memcmp(span_a.data(), span_b.data(), span_a.size()) == 0)
{
if (reason) err_stream << tx << ", " << data << SPECIFYING_SAME_VALUE_ERR << "value";
if (reason) err_stream << tx << ", " << lns_extra << SPECIFYING_SAME_VALUE_ERR << "value";
return false;
}
}
else if (data.encrypted_value.size())
else if (lns_extra.encrypted_value.size())
{
if (reason) err_stream << tx << ", " << data << VALUE_SPECIFIED_BUT_NOT_REQUESTED << "value";
if (reason) err_stream << tx << ", " << lns_extra << VALUE_SPECIFIED_BUT_NOT_REQUESTED << "value";
return false;
}
if (data.bit_field_is_set(lns::extra_field::owner))
if (lns_extra.field_is_set(lns::extra_field::owner))
{
if (data.owner == mapping.owner)
if (lns_extra.owner == mapping.owner)
{
if (reason) err_stream << tx << ", " << data << SPECIFYING_SAME_VALUE_ERR << "owner";
if (reason) err_stream << tx << ", " << lns_extra << SPECIFYING_SAME_VALUE_ERR << "owner";
return false;
}
}
else if (data.owner.ed25519)
else if (lns_extra.owner.ed25519)
{
if (reason) err_stream << tx << ", " << data << VALUE_SPECIFIED_BUT_NOT_REQUESTED << "owner";
if (reason) err_stream << tx << ", " << lns_extra << VALUE_SPECIFIED_BUT_NOT_REQUESTED << "owner";
return false;
}
if (data.bit_field_is_set(lns::extra_field::backup_owner))
if (lns_extra.field_is_set(lns::extra_field::backup_owner))
{
if (data.backup_owner == mapping.backup_owner)
if (lns_extra.backup_owner == mapping.backup_owner)
{
if (reason) err_stream << tx << ", " << data << SPECIFYING_SAME_VALUE_ERR << "backup owner";
if (reason) err_stream << tx << ", " << lns_extra << SPECIFYING_SAME_VALUE_ERR << "backup owner";
return false;
}
}
else if (data.backup_owner)
else if (lns_extra.backup_owner)
{
if (reason) err_stream << tx << ", " << data << VALUE_SPECIFIED_BUT_NOT_REQUESTED << "backup owner";
if (reason) err_stream << tx << ", " << lns_extra << VALUE_SPECIFIED_BUT_NOT_REQUESTED << "backup owner";
return false;
}
// Validate signature
{
auto value = epee::strspan<uint8_t>(data.encrypted_value);
auto value = epee::strspan<uint8_t>(lns_extra.encrypted_value);
crypto::hash hash = tx_extra_signature_hash(value,
data.bit_field_is_set(lns::extra_field::owner) ? &data.owner : nullptr,
data.bit_field_is_set(lns::extra_field::backup_owner) ? &data.backup_owner : nullptr,
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 (!verify_lns_signature(hash, data.signature, mapping.owner) && !verify_lns_signature(hash, data.signature, mapping.backup_owner))
if (!verify_lns_signature(hash, lns_extra.signature, mapping.owner) && !verify_lns_signature(hash, lns_extra.signature, mapping.backup_owner))
{
if (reason) err_stream << tx << ", " << data << ", failed to verify signature for LNS update";
if (reason) err_stream << tx << ", " << lns_extra << ", failed to verify signature for LNS update";
return false;
}
}
data.owner = mapping.owner;
data.backup_owner = mapping.backup_owner;
lns_extra.owner = mapping.owner;
lns_extra.backup_owner = mapping.backup_owner;
}
else
{
if (!is_lokinet_type(data.type))
if (!is_lokinet_type(lns_extra.type))
{
if (reason)
{
lns::owner_record owner = lns_db.get_owner_by_id(mapping.owner_id);
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;
*reason = err_stream.str();
err_stream << tx << ", " << lns_extra << ", non-lokinet entries can NOT be renewed, mapping already exists with name_hash=" << mapping.name_hash << ", owner=" << owner.key << ", type=" << mapping.type;
}
return false;
}
if (!(data.bit_field_match(lns::extra_field::buy) || data.bit_field_match(lns::extra_field::buy_no_backup)))
if (!(lns_extra.field_is_set(lns::extra_field::buy) || lns_extra.field_is_set(lns::extra_field::buy_no_backup)))
{
if (reason) err_stream << tx << ", " << data << ", TX is buying mapping but serialized unexpected fields not relevant for buying";
if (reason) err_stream << tx << ", " << lns_extra << ", TX is buying mapping but serialized unexpected fields not relevant for buying";
return false;
}
uint64_t renew_window = 0;
uint64_t expiry_blocks = lns::expiry_blocks(lns_db.network_type(), data.type, &renew_window);
uint64_t expiry_blocks = lns::expiry_blocks(lns_db.network_type(), lns_extra.type, &renew_window);
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)
{
if (reason)
{
err_stream << tx << ", " << data << ", trying to renew too early, the earliest renew height=" << min_renew_height << ", urrent height=" << blockchain_height;
*reason = err_stream.str();
}
return false; // Trying to renew too early
if (reason) err_stream << tx << ", " << lns_extra << ", trying to renew too early, the earliest renew height=" << min_renew_height << ", urrent height=" << blockchain_height;
return false;
}
if (mapping.active(lns_db.network_type(), blockchain_height))
{
// 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);
lns::owner_record const requester = lns_db.get_owner_by_key(lns_extra.owner);
if (!requester)
{
if (reason) err_stream << tx << ", " << data << ", trying to renew existing mapping but owner specified in LNS extra does not exist, rejected";
if (reason) err_stream << tx << ", " << lns_extra << ", trying to renew existing mapping but owner specified in LNS extra does not exist, rejected";
return false;
}
lns::owner_record const owner = lns_db.get_owner_by_id(mapping.owner_id);
if (!owner)
{
if (reason) err_stream << tx << ", " << data << ", unexpected owner_id=" << mapping.owner_id << " does not exist";
if (reason) err_stream << tx << ", " << lns_extra << ", unexpected owner_id=" << mapping.owner_id << " does not exist";
return false;
}
if (requester.id != owner.id)
{
if (reason) err_stream << tx << ", " << data << ", actual owner=" << owner.key << ", with owner_id=" << mapping.owner_id << ", does not match requester=" << requester.key << ", with id=" << requester.id;
if (reason) err_stream << tx << ", " << lns_extra << ", actual owner=" << owner.key << ", with owner_id=" << mapping.owner_id << ", does not match requester=" << requester.key << ", with id=" << requester.id;
return false;
}
}
@ -796,22 +793,22 @@ static bool validate_against_previous_mapping(lns::name_system_db const &lns_db,
}
}
if (data.prev_txid != expected_prev_txid)
if (lns_extra.prev_txid != expected_prev_txid)
{
if (reason) 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";
if (reason) err_stream << tx << ", " << lns_extra << ", specified prior owner txid=" << lns_extra.prev_txid << ", but LNS DB reports=" << expected_prev_txid << ", possible competing TX was submitted and accepted before this TX was processed";
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
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 *lns_extra, std::string *reason) const
{
std::stringstream err_stream;
LOKI_DEFER { if (reason && reason->empty()) *reason = err_stream.str(); };
cryptonote::tx_extra_loki_name_system entry_;
if (!entry) entry = &entry_;
cryptonote::tx_extra_loki_name_system lns_extra_;
if (!lns_extra) lns_extra = &lns_extra_;
if (tx.type != cryptonote::txtype::loki_name_system)
{
@ -819,43 +816,43 @@ bool name_system_db::validate_lns_tx(uint8_t hf_version, uint64_t blockchain_hei
return false;
}
if (!cryptonote::get_loki_name_system_from_tx_extra(tx.extra, *entry))
if (!cryptonote::get_loki_name_system_from_tx_extra(tx.extra, *lns_extra))
{
if (reason) err_stream << tx << ", didn't have loki name service in the tx_extra";
return false;
}
if (entry->version != 0)
if (lns_extra->version != 0)
{
if (reason) err_stream << tx << ", " << *entry << ", unexpected version=" << std::to_string(entry->version) << ", expected=0";
if (reason) err_stream << tx << ", " << *lns_extra << ", unexpected version=" << std::to_string(lns_extra->version) << ", expected=0";
return false;
}
if (!lns::mapping_type_allowed(hf_version, entry->type))
if (!lns::mapping_type_allowed(hf_version, lns_extra->type))
{
if (reason) err_stream << tx << ", " << *entry << ", specifying type=" << entry->type << " that is disallowed";
if (reason) err_stream << tx << ", " << *lns_extra << ", specifying type=" << lns_extra->type << " that is disallowed";
return false;
}
if (entry->is_buying())
if (lns_extra->is_buying())
{
if (!entry->owner)
if (!lns_extra->owner)
{
if (reason) err_stream << tx << ", " << *entry << ", can't specify LNS extra without owner";
if (reason) err_stream << tx << ", " << *lns_extra << ", can't specify LNS extra without owner";
return false;
}
if (entry->owner == entry->backup_owner)
if (lns_extra->owner == lns_extra->backup_owner)
{
if (reason) err_stream << tx << ", " << *entry << ", specifying owner the same as the backup owner";
if (reason) err_stream << tx << ", " << *lns_extra << ", specifying owner the same as the backup owner";
return false;
}
}
else if (entry->is_updating())
else if (lns_extra->is_updating())
{
if (!entry->signature)
if (!lns_extra->signature)
{
if (reason) err_stream << tx << ", " << *entry << ", signature to validate is the null-signature";
if (reason) err_stream << tx << ", " << *lns_extra << ", signature to validate is the null-signature";
return false;
}
}
@ -863,21 +860,21 @@ bool name_system_db::validate_lns_tx(uint8_t hf_version, uint64_t blockchain_hei
{
if (reason)
{
err_stream << tx << ", " << *entry
err_stream << tx << ", " << *lns_extra
<< ", TX extra does not specify valid combination of bits in serialized fields="
<< std::bitset<sizeof(entry->fields) * 8>(static_cast<size_t>(entry->fields));
<< std::bitset<sizeof(lns_extra->fields) * 8>(static_cast<size_t>(lns_extra->fields));
}
return false;
}
if (!validate_encrypted_mapping_value(entry->type, entry->encrypted_value, reason))
if (!validate_encrypted_mapping_value(lns_extra->type, lns_extra->encrypted_value, reason))
return false;
if (!validate_against_previous_mapping(*this, blockchain_height, tx, *entry, reason))
if (!validate_against_previous_mapping(*this, blockchain_height, tx, *lns_extra, reason))
return false;
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 (lns_extra->name_hash == null_name_hash || lns_extra->name_hash == crypto::null_hash)
{
if (reason)
{
@ -888,16 +885,11 @@ bool name_system_db::validate_lns_tx(uint8_t hf_version, uint64_t blockchain_hei
}
uint64_t burn = cryptonote::get_burned_amount_from_tx_extra(tx.extra);
uint64_t const burn_required = entry->is_buying() ? burn_needed(hf_version, entry->type) : 0;
uint64_t const burn_required = lns_extra->is_buying() ? burn_needed(hf_version, lns_extra->type) : 0;
if (burn != burn_required)
{
if (reason)
{
char const *over_or_under = burn > burn_required ? "too much " : "insufficient ";
err_stream << tx << ", " << *entry << ", burned " << over_or_under << "loki=" << burn << ", require=" << burn_required;
if (!entry->is_buying()) err_stream << ", updating requires just the ordinary transaction fee";
*reason = err_stream.str();
}
char const *over_or_under = burn > burn_required ? "too much " : "insufficient ";
if (reason) err_stream << tx << ", " << *lns_extra << ", burned " << over_or_under << "loki=" << burn << ", require=" << burn_required;
return false;
}

View File

@ -1493,7 +1493,7 @@ bool loki_name_system_invalid_tx_extra_params::generate(std::vector<test_event_e
std::string name = "my_lns_name";
cryptonote::tx_extra_loki_name_system valid_data = {};
valid_data.set_bit_field(lns::extra_field::buy_no_backup);
valid_data.set_field(lns::extra_field::buy_no_backup);
valid_data.owner = miner_key.key;
valid_data.type = lns::mapping_type::wallet;
valid_data.encrypted_value = helper_encrypt_lns_value(name, miner_key.wallet_value).to_string();
@ -1902,8 +1902,8 @@ bool loki_name_system_name_value_max_lengths::generate(std::vector<test_event_en
lns_keys_t miner_key = make_lns_keys(miner);
cryptonote::tx_extra_loki_name_system data = {};
data.set_bit_field(lns::extra_field::buy_no_backup);
data.owner = miner_key.key;
data.set_field(lns::extra_field::buy_no_backup);
data.owner = miner_key.key;
// Wallet
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::wallet))