263 lines
8.6 KiB
C++
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
|