Far too many changes to track, but the key ones are:

1. rewrite of the Haven variation of the Pricing Record class to support:
   - versioning of the PR format
   - nested supply_data and asset_data structs
   - verification of the signature using a variable-length string (not R+S)

2. calculation of the slippage tallies for a block in add_block(), so that
   we can work out the yield that is due to be paid out for the block.

Loads of little fixes and cleanups.
This commit is contained in:
Some Random Crypto Guy
2024-01-10 23:21:56 +00:00
parent 6b791a3df2
commit 5ba22c2ec9
18 changed files with 465 additions and 236 deletions
+116 -48
View File
@@ -39,18 +39,44 @@ namespace oracle
namespace
{
struct asset_data_serialized
{
std::string asset_type;
uint64_t spot_price;
uint64_t ma_price;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(asset_type)
KV_SERIALIZE(spot_price)
KV_SERIALIZE(ma_price)
END_KV_SERIALIZE_MAP()
};
struct supply_data_serialized
{
uint64_t FULM;
uint64_t FUSD;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(FULM)
KV_SERIALIZE(FUSD)
END_KV_SERIALIZE_MAP()
};
struct pr_serialized
{
uint64_t pr_version;
uint64_t spot;
uint64_t moving_average;
uint64_t height;
supply_data supply;
std::vector<asset_data> assets;
uint64_t timestamp;
std::string signature;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(pr_version)
KV_SERIALIZE(spot)
KV_SERIALIZE(moving_average)
KV_SERIALIZE(height)
KV_SERIALIZE(supply)
KV_SERIALIZE(assets)
KV_SERIALIZE(timestamp)
KV_SERIALIZE(signature)
END_KV_SERIALIZE_MAP()
@@ -59,13 +85,53 @@ namespace oracle
pricing_record::pricing_record() noexcept
: pr_version(0)
, spot(0)
, moving_average(0)
, assets()
, timestamp(0)
, signature()
{
std::memset(signature, 0, sizeof(signature));
}
bool supply_data::_load(epee::serialization::portable_storage& src, epee::serialization::section* hparent)
{
supply_data_serialized in{};
if (in._load(src, hparent))
{
// Copy everything into the local instance
fulm = in.FULM;
fusd = in.FUSD;
return true;
}
// Report error here?
return false;
}
bool supply_data::store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const
{
assert(false);
return true;
}
bool asset_data::_load(epee::serialization::portable_storage& src, epee::serialization::section* hparent)
{
asset_data_serialized in{};
if (in._load(src, hparent))
{
// Copy everything into the local instance
asset_type = in.asset_type;
spot_price = in.spot_price;
ma_price = in.ma_price;
return true;
}
// Report error here?
return false;
}
bool asset_data::store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const
{
assert(false);
return true;
}
bool pricing_record::_load(epee::serialization::portable_storage& src, epee::serialization::section* hparent)
{
pr_serialized in{};
@@ -73,9 +139,13 @@ namespace oracle
{
// Copy everything into the local instance
pr_version = in.pr_version;
spot = in.spot;
moving_average = in.moving_average;
height = in.height;
supply = in.supply;
assets = in.assets;
timestamp = in.timestamp;
// Signature arrives in HEX format, but needs to be used in BINARY format - convert it here
signature.resize(in.signature.length() >> 1);
for (unsigned int i = 0; i < in.signature.length(); i += 2) {
std::string byteString = in.signature.substr(i, 2);
signature[i>>1] = (char) strtol(byteString.c_str(), NULL, 16);
@@ -95,37 +165,42 @@ namespace oracle
ss << std::hex << std::setw(2) << std::setfill('0') << (0xff & signature[i]);
sig_hex += ss.str();
}
const pr_serialized out{pr_version,spot,moving_average,timestamp,sig_hex};
const pr_serialized out{pr_version, height, supply, assets, timestamp, sig_hex};
return out.store(dest, hparent);
}
pricing_record::pricing_record(const pricing_record& orig) noexcept
: pr_version(orig.pr_version)
, spot(orig.spot)
, moving_average(orig.moving_average)
, height(orig.height)
, supply(orig.supply)
, assets(orig.assets)
, timestamp(orig.timestamp)
, signature(orig.signature)
{
std::memcpy(signature, orig.signature, sizeof(signature));
}
pricing_record& pricing_record::operator=(const pricing_record& orig) noexcept
{
pr_version = orig.pr_version;
spot = orig.spot;
moving_average = orig.moving_average;
height = orig.height;
supply = orig.supply;
assets = orig.assets;
timestamp = orig.timestamp;
::memcpy(signature, orig.signature, sizeof(signature));
signature = orig.signature;
return *this;
}
uint64_t pricing_record::operator[](const std::string& asset_type) const
{
if (asset_type == "FULM") return spot;
if (asset_type == "FUSD") {
boost::multiprecision::uint128_t exchange_128 = COIN;
exchange_128 *= COIN;
exchange_128 /= spot;
return exchange_128.convert_to<uint64_t>();
for (const auto& asset: assets) {
if (asset.asset_type != asset_type) continue;
if (asset_type == "FULM") return asset.spot_price;
if (asset_type == "FUSD") {
boost::multiprecision::uint128_t exchange_128 = COIN;
exchange_128 *= COIN;
exchange_128 /= asset.spot_price;
return exchange_128.convert_to<uint64_t>();
}
}
return 0;
}
@@ -133,10 +208,11 @@ namespace oracle
bool pricing_record::equal(const pricing_record& other) const noexcept
{
return ((pr_version == other.pr_version) &&
(spot == other.spot) &&
(moving_average == other.moving_average) &&
(height == other.height) &&
(supply == other.supply) &&
(assets == other.assets) &&
(timestamp == other.timestamp) &&
(!::memcmp(signature, other.signature, sizeof(signature))));
(signature == other.signature));
}
bool pricing_record::empty() const noexcept
@@ -147,7 +223,7 @@ namespace oracle
bool pricing_record::verifySignature(const std::string& public_key) const
{
CHECK_AND_ASSERT_THROW_MES(!public_key.empty(), "Pricing record verification failed. NULL public key. PK Size: " << public_key.size()); // TODO: is this necessary or the one below already covers this case, meannin it will produce empty pubkey?
CHECK_AND_ASSERT_THROW_MES(!public_key.empty(), "Pricing record verification failed. NULL public key. PK Size: " << public_key.size());
// extract the key
EVP_PKEY* pubkey;
@@ -159,19 +235,21 @@ namespace oracle
BIO_free(bio);
CHECK_AND_ASSERT_THROW_MES(pubkey != NULL, "Pricing record verification failed. NULL public key.");
// Convert our internal 64-byte binary representation into 128-byte hex string
std::string sig_hex;
for (unsigned int i=0; i<64; i++) {
std::stringstream ss;
ss << std::hex << std::setw(2) << std::setfill('0') << (0xff & signature[i]);
sig_hex += ss.str();
}
// Build the JSON string, so that we can verify the signature
std::ostringstream oss;
oss << "{\"pr_version\":" << pr_version;
oss << ",\"spot\":" << spot;
oss << ",\"moving_average\":" << moving_average;
oss << ",\"height\":" << height;
oss << ",\"supply\":{\"FULM\":" << supply.fulm <<",\"FUSD\":" << supply.fusd << "}";
oss << ",\"assets\":[";
bool first = true;
for (const auto& asset: assets) {
if (first)
first=false;
else
oss << ",";
oss << "{\"asset_type\":\"" << asset.asset_type << "\",\"spot_price\":" << asset.spot_price << ",\"ma_price\":" << asset.ma_price << "}";
}
oss << "]";
oss << ",\"timestamp\":" << timestamp;
oss << "}";
std::string message = oss.str();
@@ -184,7 +262,7 @@ namespace oracle
if (ret == 1) {
ret = EVP_DigestVerifyUpdate(ctx, message.data(), message.length());
if (ret == 1) {
ret = EVP_DigestVerifyFinal(ctx, (const unsigned char *)signature, 64);
ret = EVP_DigestVerifyFinal(ctx, (const unsigned char *)signature.data(), signature.size());
}
}
}
@@ -203,11 +281,6 @@ namespace oracle
return false;
}
bool pricing_record::has_missing_rates() const noexcept
{
return (spot == 0) || (moving_average == 0);
}
// overload for pr validation for block
bool pricing_record::valid(cryptonote::network_type nettype, uint32_t hf_version, uint64_t bl_timestamp, uint64_t last_bl_timestamp) const
{
@@ -219,11 +292,6 @@ namespace oracle
if (this->empty())
return true;
if (this->has_missing_rates()) {
LOG_ERROR("Pricing record has missing rates.");
return false;
}
if (!verifySignature(get_config(nettype).ORACLE_PUBLIC_KEY)) {
LOG_ERROR("Invalid pricing record signature.");
return false;
+58 -49
View File
@@ -42,6 +42,7 @@
#include <cstdint>
#include <string>
#include <cstring>
#include <serialization/containers.h>
#include "cryptonote_config.h"
#include "crypto/hash.h"
@@ -64,34 +65,69 @@ namespace oracle
uint64_t timestamp;
};
#pragma pack(pop)
struct supply_data {
uint64_t fulm;
uint64_t fusd;
//! Load from epee p2p format
bool _load(epee::serialization::portable_storage& src, epee::serialization::section* hparent);
//! Store in epee p2p format
bool store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const;
};
inline bool operator==(const supply_data& a, const supply_data& b) noexcept
{
return (a.fulm == b.fulm &&
a.fusd == b.fusd);
}
struct asset_data {
std::string asset_type;
uint64_t spot_price;
uint64_t ma_price;
//! Load from epee p2p format
bool _load(epee::serialization::portable_storage& src, epee::serialization::section* hparent);
//! Store in epee p2p format
bool store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const;
};
inline bool operator==(const asset_data& a, const asset_data& b) noexcept
{
return (a.asset_type == b.asset_type &&
a.spot_price == b.spot_price &&
a.ma_price == b.ma_price);
}
class pricing_record
{
public:
public:
// Fields
uint64_t pr_version;
uint64_t spot;
uint64_t moving_average;
uint64_t timestamp;
unsigned char signature[64];
// Fields
uint64_t pr_version;
uint64_t height;
supply_data supply;
std::vector<asset_data> assets;
uint64_t timestamp;
std::vector<uint8_t> signature;
// Default c'tor
pricing_record() noexcept;
//! Load from epee p2p format
bool _load(epee::serialization::portable_storage& src, epee::serialization::section* hparent);
//! Store in epee p2p format
bool store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const;
pricing_record(const pricing_record& orig) noexcept;
~pricing_record() = default;
bool equal(const pricing_record& other) const noexcept;
bool empty() const noexcept;
bool verifySignature(const std::string& public_key) const;
bool has_missing_rates() const noexcept;
bool valid(cryptonote::network_type nettype, uint32_t hf_version, uint64_t bl_timestamp, uint64_t last_bl_timestamp) const;
// Default c'tor
pricing_record() noexcept;
//! Load from epee p2p format
bool _load(epee::serialization::portable_storage& src, epee::serialization::section* hparent);
//! Store in epee p2p format
bool store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const;
pricing_record(const pricing_record& orig) noexcept;
~pricing_record() = default;
bool equal(const pricing_record& other) const noexcept;
bool empty() const noexcept;
bool verifySignature(const std::string& public_key) const;
bool valid(cryptonote::network_type nettype, uint32_t hf_version, uint64_t bl_timestamp, uint64_t last_bl_timestamp) const;
pricing_record& operator=(const pricing_record& orig) noexcept;
uint64_t operator[](const std::string& asset_type) const;
pricing_record& operator=(const pricing_record& orig) noexcept;
uint64_t operator[](const std::string& asset_type) const;
};
inline bool operator==(const pricing_record& a, const pricing_record& b) noexcept
@@ -104,31 +140,4 @@ namespace oracle
return !a.equal(b);
}
class pricing_record_v1
{
public:
uint8_t pr_version;
uint64_t price;
uint64_t timestamp;
bool write_to_pr(oracle::pricing_record &pr)
{
pr.pr_version = 0;
pr.spot = 0;
pr.moving_average = 0;
pr.timestamp = 0;
std::memset(pr.signature, 0, sizeof(oracle::pricing_record::signature));
return true;
};
bool read_from_pr(oracle::pricing_record &pr)
{
pr_version = 0;
price = 0;
timestamp = 0;
return true;
};
};
} // oracle