peerplays_migrated/libraries/plugins/peerplays_sidechain/hive/asset.cpp
2021-04-26 13:05:27 +02:00

263 lines
8.6 KiB
C++

#include <graphene/peerplays_sidechain/hive/asset.hpp>
#include <fc/io/json.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/rational.hpp>
#define ASSET_AMOUNT_KEY "amount"
#define ASSET_PRECISION_KEY "precision"
#define ASSET_NAI_KEY "nai"
namespace graphene { namespace peerplays_sidechain { namespace hive {
std::string asset_symbol_type::to_string() const {
return fc::json::to_string(fc::variant(*this, 5));
}
asset_symbol_type asset_symbol_type::from_string(const std::string &str) {
return fc::json::from_string(str).as<hive::asset_symbol_type>(5);
}
void asset_symbol_type::to_nai_string(char *buf) const {
static_assert(HIVE_ASSET_SYMBOL_NAI_STRING_LENGTH >= 12, "This code will overflow a short buffer");
uint32_t x = to_nai();
buf[11] = '\0';
buf[10] = ((x % 10) + '0');
x /= 10;
buf[9] = ((x % 10) + '0');
x /= 10;
buf[8] = ((x % 10) + '0');
x /= 10;
buf[7] = ((x % 10) + '0');
x /= 10;
buf[6] = ((x % 10) + '0');
x /= 10;
buf[5] = ((x % 10) + '0');
x /= 10;
buf[4] = ((x % 10) + '0');
x /= 10;
buf[3] = ((x % 10) + '0');
x /= 10;
buf[2] = ((x) + '0');
buf[1] = '@';
buf[0] = '@';
}
asset_symbol_type asset_symbol_type::from_nai_string(const char *p, uint8_t decimal_places) {
try {
FC_ASSERT(p != nullptr, "NAI string cannot be a null");
FC_ASSERT(std::strlen(p) == HIVE_ASSET_SYMBOL_NAI_STRING_LENGTH - 1, "Incorrect NAI string length");
FC_ASSERT(p[0] == '@' && p[1] == '@', "Invalid NAI string prefix");
uint32_t nai = boost::lexical_cast<uint32_t>(p + 2);
return asset_symbol_type::from_nai(nai, decimal_places);
}
FC_CAPTURE_AND_RETHROW();
}
// Highly optimized implementation of Damm algorithm
// https://en.wikipedia.org/wiki/Damm_algorithm
uint8_t asset_symbol_type::damm_checksum_8digit(uint32_t value) {
FC_ASSERT(value < 100000000);
const uint8_t t[] = {
0, 30, 10, 70, 50, 90, 80, 60, 40, 20,
70, 0, 90, 20, 10, 50, 40, 80, 60, 30,
40, 20, 0, 60, 80, 70, 10, 30, 50, 90,
10, 70, 50, 0, 90, 80, 30, 40, 20, 60,
60, 10, 20, 30, 0, 40, 50, 90, 70, 80,
30, 60, 70, 40, 20, 0, 90, 50, 80, 10,
50, 80, 60, 90, 70, 20, 0, 10, 30, 40,
80, 90, 40, 50, 30, 60, 20, 0, 10, 70,
90, 40, 30, 80, 60, 10, 70, 20, 0, 50,
20, 50, 80, 10, 40, 30, 60, 70, 90, 0};
uint32_t q0 = value / 10;
uint32_t d0 = value % 10;
uint32_t q1 = q0 / 10;
uint32_t d1 = q0 % 10;
uint32_t q2 = q1 / 10;
uint32_t d2 = q1 % 10;
uint32_t q3 = q2 / 10;
uint32_t d3 = q2 % 10;
uint32_t q4 = q3 / 10;
uint32_t d4 = q3 % 10;
uint32_t q5 = q4 / 10;
uint32_t d5 = q4 % 10;
uint32_t d6 = q5 % 10;
uint32_t d7 = q5 / 10;
uint8_t x = t[d7];
x = t[x + d6];
x = t[x + d5];
x = t[x + d4];
x = t[x + d3];
x = t[x + d2];
x = t[x + d1];
x = t[x + d0];
return x / 10;
}
uint32_t asset_symbol_type::asset_num_from_nai(uint32_t nai, uint8_t decimal_places) {
// Can be replaced with some clever bitshifting
uint32_t nai_check_digit = nai % 10;
uint32_t nai_data_digits = nai / 10;
FC_ASSERT((nai_data_digits >= SMT_MIN_NAI) & (nai_data_digits <= SMT_MAX_NAI), "NAI out of range");
FC_ASSERT(nai_check_digit == damm_checksum_8digit(nai_data_digits), "Invalid check digit");
switch (nai_data_digits) {
case HIVE_NAI_HIVE:
FC_ASSERT(decimal_places == HIVE_PRECISION_HIVE);
return HIVE_ASSET_NUM_HIVE;
case HIVE_NAI_HBD:
FC_ASSERT(decimal_places == HIVE_PRECISION_HBD);
return HIVE_ASSET_NUM_HBD;
case HIVE_NAI_VESTS:
FC_ASSERT(decimal_places == HIVE_PRECISION_VESTS);
return HIVE_ASSET_NUM_VESTS;
default:
FC_ASSERT(decimal_places <= HIVE_ASSET_MAX_DECIMALS, "Invalid decimal_places");
return (nai_data_digits << HIVE_NAI_SHIFT) | SMT_ASSET_NUM_CONTROL_MASK | decimal_places;
}
}
uint32_t asset_symbol_type::to_nai() const {
uint32_t nai_data_digits = 0;
// Can be replaced with some clever bitshifting
switch (asset_num) {
case HIVE_ASSET_NUM_HIVE:
nai_data_digits = HIVE_NAI_HIVE;
break;
case HIVE_ASSET_NUM_HBD:
nai_data_digits = HIVE_NAI_HBD;
break;
case HIVE_ASSET_NUM_VESTS:
nai_data_digits = HIVE_NAI_VESTS;
break;
default:
FC_ASSERT(space() == smt_nai_space);
nai_data_digits = (asset_num >> HIVE_NAI_SHIFT);
}
uint32_t nai_check_digit = damm_checksum_8digit(nai_data_digits);
return nai_data_digits * 10 + nai_check_digit;
}
bool asset_symbol_type::is_vesting() const {
switch (space()) {
case legacy_space: {
switch (asset_num) {
case HIVE_ASSET_NUM_HIVE:
return false;
case HIVE_ASSET_NUM_HBD:
// HBD is certainly liquid.
return false;
case HIVE_ASSET_NUM_VESTS:
return true;
default:
FC_ASSERT(false, "Unknown asset symbol");
}
}
case smt_nai_space:
// 6th bit of asset_num is used as vesting/liquid variant indicator.
return asset_num & SMT_ASSET_NUM_VESTING_MASK;
default:
FC_ASSERT(false, "Unknown asset symbol");
}
}
asset_symbol_type asset_symbol_type::get_paired_symbol() const {
switch (space()) {
case legacy_space: {
switch (asset_num) {
case HIVE_ASSET_NUM_HIVE:
return from_asset_num(HIVE_ASSET_NUM_VESTS);
case HIVE_ASSET_NUM_HBD:
return *this;
case HIVE_ASSET_NUM_VESTS:
return from_asset_num(HIVE_ASSET_NUM_HIVE);
default:
FC_ASSERT(false, "Unknown asset symbol");
}
}
case smt_nai_space: {
// Toggle 6th bit of this asset_num.
auto paired_asset_num = asset_num ^ (SMT_ASSET_NUM_VESTING_MASK);
return from_asset_num(paired_asset_num);
}
default:
FC_ASSERT(false, "Unknown asset symbol");
}
}
asset_symbol_type::asset_symbol_space asset_symbol_type::space() const {
asset_symbol_type::asset_symbol_space s = legacy_space;
switch (asset_num) {
case HIVE_ASSET_NUM_HIVE:
case HIVE_ASSET_NUM_HBD:
case HIVE_ASSET_NUM_VESTS:
s = legacy_space;
break;
default:
s = smt_nai_space;
}
return s;
}
void asset_symbol_type::validate() const {
switch (asset_num) {
case HIVE_ASSET_NUM_HIVE:
case HIVE_ASSET_NUM_HBD:
case HIVE_ASSET_NUM_VESTS:
break;
default: {
uint32_t nai_data_digits = (asset_num >> HIVE_NAI_SHIFT);
uint32_t nai_1bit = (asset_num & SMT_ASSET_NUM_CONTROL_MASK);
uint32_t nai_decimal_places = (asset_num & SMT_ASSET_NUM_PRECISION_MASK);
FC_ASSERT((nai_data_digits >= SMT_MIN_NAI) &
(nai_data_digits <= SMT_MAX_NAI) &
(nai_1bit == SMT_ASSET_NUM_CONTROL_MASK) &
(nai_decimal_places <= HIVE_ASSET_MAX_DECIMALS),
"Cannot determine space for asset ${n}", ("n", asset_num));
}
}
// this assert is duplicated by above code in all cases
// FC_ASSERT( decimals() <= HIVE_ASSET_MAX_DECIMALS );
}
}}} // namespace graphene::peerplays_sidechain::hive
namespace fc {
void to_variant(const graphene::peerplays_sidechain::hive::asset &var, fc::variant &vo, uint32_t max_depth) {
try {
variant v = mutable_variant_object(ASSET_AMOUNT_KEY, boost::lexical_cast<std::string>(var.amount.value))(ASSET_PRECISION_KEY, uint64_t(var.symbol.decimals()))(ASSET_NAI_KEY, var.symbol.to_nai_string());
vo = v;
}
FC_CAPTURE_AND_RETHROW()
}
void from_variant(const fc::variant &var, graphene::peerplays_sidechain::hive::asset &vo, uint32_t max_depth) {
try {
FC_ASSERT(var.is_object(), "Asset has to be treated as object.");
const auto &v_object = var.get_object();
FC_ASSERT(v_object.contains(ASSET_AMOUNT_KEY), "Amount field doesn't exist.");
FC_ASSERT(v_object[ASSET_AMOUNT_KEY].is_string(), "Expected a string type for value '${key}'.", ("key", ASSET_AMOUNT_KEY));
vo.amount = boost::lexical_cast<int64_t>(v_object[ASSET_AMOUNT_KEY].as<std::string>(max_depth));
FC_ASSERT(vo.amount >= 0, "Asset amount cannot be negative");
FC_ASSERT(v_object.contains(ASSET_PRECISION_KEY), "Precision field doesn't exist.");
FC_ASSERT(v_object[ASSET_PRECISION_KEY].is_uint64(), "Expected an unsigned integer type for value '${key}'.", ("key", ASSET_PRECISION_KEY));
FC_ASSERT(v_object.contains(ASSET_NAI_KEY), "NAI field doesn't exist.");
FC_ASSERT(v_object[ASSET_NAI_KEY].is_string(), "Expected a string type for value '${key}'.", ("key", ASSET_NAI_KEY));
vo.symbol = graphene::peerplays_sidechain::hive::asset_symbol_type::from_nai_string(v_object[ASSET_NAI_KEY].as<std::string>(max_depth).c_str(), v_object[ASSET_PRECISION_KEY].as<uint8_t>(max_depth));
}
FC_CAPTURE_AND_RETHROW()
}
} // namespace fc