Skip to content

Commit

Permalink
Add safety asserts on invalid values, full spectrum tests (#1086)
Browse files Browse the repository at this point in the history
* Add full spectrum tests

* Remove boost/execution_monitor - remnant from local

* Better handling of unexpected results and refine tests

* Refine handling
  • Loading branch information
prasannavl authored Mar 18, 2022
1 parent aaf88f5 commit 4d10561
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 31 deletions.
24 changes: 15 additions & 9 deletions src/masternodes/loan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -481,38 +481,44 @@ CAmount CLoanView::GetLoanLiquidationPenalty()
return 5 * COIN / 100;
}

std::string GetInterestPerBlockHighPrecisionString(base_uint<128> value) {
boost::optional<std::string> GetInterestPerBlockHighPrecisionString(const base_uint<128>& value) {
struct HighPrecisionInterestValue {
typedef boost::multiprecision::int128_t int128;
typedef int64_t int64;

int128 value;

HighPrecisionInterestValue(base_uint<128> val) {
explicit HighPrecisionInterestValue(const base_uint<128>& val) {
value = int128("0x" + val.GetHex());
}

int64 GetInterestPerBlockSat() {
int64 GetInterestPerBlockSat() const {
return int64(value / HIGH_PRECISION_SCALER);
}

int64 GetInterestPerBlockSubSat() {
int64 GetInterestPerBlockSubSat() const {
return int64(value % HIGH_PRECISION_SCALER);
}

int64 GetInterestPerBlockMagnitude() {
int64 GetInterestPerBlockMagnitude() const {
return int64(value / HIGH_PRECISION_SCALER / COIN);
}

int128 GetInterestPerBlockDecimal() {
int128 GetInterestPerBlockDecimal() const {
auto v = GetInterestPerBlockSat();
return v == 0 ? value : value % (int128(HIGH_PRECISION_SCALER) * COIN);
}

std::string GetInterestPerBlockString() {
boost::optional<std::string> GetInterestPerBlockString() const {
std::ostringstream result;
result << GetInterestPerBlockMagnitude() << ".";
result << std::setw(24) << std::setfill('0') << GetInterestPerBlockDecimal();
auto mag = GetInterestPerBlockMagnitude();
auto dec = GetInterestPerBlockDecimal();
// While these can happen theoretically, they should be out of range of
// operating interest. If this happens, something else went wrong.
if (mag < 0 || dec < 0)
return {};

result << mag << "." << std::setw(24) << std::setfill('0') << dec;
return result.str();
}
};
Expand Down
2 changes: 1 addition & 1 deletion src/masternodes/loan.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ CAmount TotalInterest(const CInterestRateV2& rate, uint32_t height);
CAmount InterestPerBlock(const CInterestRateV2& rate, uint32_t height);
base_uint<128> TotalInterestCalculation(const CInterestRateV2& rate, uint32_t height);
CAmount CeilInterest(const base_uint<128>& value, uint32_t height);
std::string GetInterestPerBlockHighPrecisionString(base_uint<128> value);
boost::optional<std::string> GetInterestPerBlockHighPrecisionString(const base_uint<128>& value);

class CLoanTakeLoanMessage
{
Expand Down
10 changes: 8 additions & 2 deletions src/masternodes/rpc_loan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1400,7 +1400,7 @@ UniValue getinterest(const JSONRPCRequest& request) {
}

UniValue obj(UniValue::VOBJ);
for (std::map<DCT_ID, std::pair<base_uint<128>, base_uint<128> > >::iterator it=interest.begin(); it!=interest.end(); ++it)
for (auto it=interest.begin(); it != interest.end(); ++it)
{
auto tokenId = it->first;
auto interestRate = it->second;
Expand All @@ -1413,7 +1413,13 @@ UniValue getinterest(const JSONRPCRequest& request) {
obj.pushKV("interestPerBlock", ValueFromAmount(CeilInterest(interestPerBlock, height)));
if (height >= Params().GetConsensus().FortCanningHillHeight)
{
obj.pushKV("realizedInterestPerBlock", UniValue(UniValue::VNUM, GetInterestPerBlockHighPrecisionString(interestPerBlock)));
auto realizedInterestStr = GetInterestPerBlockHighPrecisionString(interestPerBlock);
// Ideally would be better to have a universal graceful shutdown methodology to force the node to
// stop for these unexpected state errors that violate operating params but still not enough
// memory inconsistency to crash risking wallet and data corruption.
if (!realizedInterestStr)
throw JSONRPCError(RPC_MISC_ERROR, "Invalid GetInterestPerBlockHighPrecisionString.");
obj.pushKV("realizedInterestPerBlock", UniValue(UniValue::VNUM, *realizedInterestStr));
}
ret.push_back(obj);
}
Expand Down
205 changes: 186 additions & 19 deletions src/test/loan_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
#include <validation.h>

#include <test/setup_common.h>

#include <boost/test/unit_test.hpp>
#include <boost/variant.hpp>
#include <algorithm>

inline uint256 NextTx()
{
Expand Down Expand Up @@ -74,25 +75,191 @@ extern std::vector<CAuctionBatch> CollectAuctionBatches(const CCollateralLoans&

BOOST_FIXTURE_TEST_SUITE(loan_tests, TestChain100Setup)

BOOST_AUTO_TEST_CASE(high_precision_interest_rate_tests)
BOOST_AUTO_TEST_CASE(high_precision_interest_rate_to_string_tests)
{
BOOST_CHECK_EQUAL(GetInterestPerBlockHighPrecisionString(base_uint<128>(0)), "0.000000000000000000000000");
BOOST_CHECK_EQUAL(GetInterestPerBlockHighPrecisionString(base_uint<128>(1)), "0.000000000000000000000001");
BOOST_CHECK_EQUAL(GetInterestPerBlockHighPrecisionString(base_uint<128>(1)), "0.000000000000000000000001");
BOOST_CHECK_EQUAL(GetInterestPerBlockHighPrecisionString(base_uint<128>(42058)), "0.000000000000000000042058");
BOOST_CHECK_EQUAL(GetInterestPerBlockHighPrecisionString(base_uint<128>(95129375)), "0.000000000000000095129375");
BOOST_CHECK_EQUAL(GetInterestPerBlockHighPrecisionString(base_uint<128>(117009132)), "0.000000000000000117009132");
BOOST_CHECK_EQUAL(GetInterestPerBlockHighPrecisionString(base_uint<128>(11700913242)), "0.000000000000011700913242");

base_uint<128> num;
num.SetHex("21012F95D4094B33"); // 2378234398782343987
BOOST_CHECK_EQUAL(GetInterestPerBlockHighPrecisionString(base_uint<128>(num)), "0.000002378234398782343987");
num.SetHex("3CDC4CA64879921C03BF061156E455BC"); // 80897539693407360060932882613242451388
BOOST_CHECK_EQUAL(GetInterestPerBlockHighPrecisionString(base_uint<128>(num)), "80897539693407.360060932882613242451388");
num.SetHex("10E5FBB8CA9E273D0B0353C23D90A6"); // 87741364994776235347880977943597222
BOOST_CHECK_EQUAL(GetInterestPerBlockHighPrecisionString(base_uint<128>(num)), "87741364994.776235347880977943597222");
num.SetHex("2D5C78FF9C3FE70F9F0B0C7"); // 877413626032608048611111111
BOOST_CHECK_EQUAL(GetInterestPerBlockHighPrecisionString(base_uint<128>(num)), "877.413626032608048611111111");
std::map<boost::variant<base_uint<128>, std::string>, std::string> testMap = {
{ 0, "0.000000000000000000000000" },
{ 1, "0.000000000000000000000001" },
{ 42058, "0.000000000000000000042058" },
{ 95129375, "0.000000000000000095129375" },
{ 117009132, "0.000000000000000117009132" },
{ 11700913242, "0.000000000000011700913242" },
// 2378234398782343987
{ "21012F95D4094B33", "0.000002378234398782343987" },
// 80897539693407360060932882613242451388
{ "3CDC4CA64879921C03BF061156E455BC" , "80897539693407.360060932882613242451388" },
// 87741364994776235347880977943597222
{ "10E5FBB8CA9E273D0B0353C23D90A6" , "87741364994.776235347880977943597222" },
// 877413626032608048611111111
{ "2D5C78FF9C3FE70F9F0B0C7" , "877.413626032608048611111111" },
{ std::numeric_limits<uint64_t>::min(), "0.000000000000000000000000" },
{ std::numeric_limits<uint64_t>::max(), "0.000018446744073709551615" },
{ std::numeric_limits<int64_t>::min(), "0.000009223372036854775808" },
{ std::numeric_limits<int64_t>::max(), "0.000009223372036854775807" },

// Full list by rotating 1s all over.. The reason for adding this to full spectrum
// test is since we use arbitrary bit ranges to achieve COIN ^ 3 precision. One vector of
// common mistakes would be due to improper cast and the first high 1 bit being interpreted
// as 2s complement and as such result in a negative error. This check verifies the entire
// range to ensure this doesn't happen.
//
{ "80000000000000000000000000000000", "170141183460469.231731687303715884105728" },
{ "40000000000000000000000000000000", "85070591730234.615865843651857942052864" },
{ "20000000000000000000000000000000", "42535295865117.307932921825928971026432" },
{ "10000000000000000000000000000000", "21267647932558.653966460912964485513216" },
{ "08000000000000000000000000000000", "10633823966279.326983230456482242756608" },
{ "04000000000000000000000000000000", "5316911983139.663491615228241121378304" },
{ "02000000000000000000000000000000", "2658455991569.831745807614120560689152" },
{ "01000000000000000000000000000000", "1329227995784.915872903807060280344576" },
{ "00800000000000000000000000000000", "664613997892.457936451903530140172288" },
{ "00400000000000000000000000000000", "332306998946.228968225951765070086144" },
{ "00200000000000000000000000000000", "166153499473.114484112975882535043072" },
{ "00100000000000000000000000000000", "83076749736.557242056487941267521536" },
{ "00080000000000000000000000000000", "41538374868.278621028243970633760768" },
{ "00040000000000000000000000000000", "20769187434.139310514121985316880384" },
{ "00020000000000000000000000000000", "10384593717.069655257060992658440192" },
{ "00010000000000000000000000000000", "5192296858.534827628530496329220096" },
{ "00008000000000000000000000000000", "2596148429.267413814265248164610048" },
{ "00004000000000000000000000000000", "1298074214.633706907132624082305024" },
{ "00002000000000000000000000000000", "649037107.316853453566312041152512" },
{ "00001000000000000000000000000000", "324518553.658426726783156020576256" },
{ "00000800000000000000000000000000", "162259276.829213363391578010288128" },
{ "00000400000000000000000000000000", "81129638.414606681695789005144064" },
{ "00000200000000000000000000000000", "40564819.207303340847894502572032" },
{ "00000100000000000000000000000000", "20282409.603651670423947251286016" },
{ "00000080000000000000000000000000", "10141204.801825835211973625643008" },
{ "00000040000000000000000000000000", "5070602.400912917605986812821504" },
{ "00000020000000000000000000000000", "2535301.200456458802993406410752" },
{ "00000010000000000000000000000000", "1267650.600228229401496703205376" },
{ "00000008000000000000000000000000", "633825.300114114700748351602688" },
{ "00000004000000000000000000000000", "316912.650057057350374175801344" },
{ "00000002000000000000000000000000", "158456.325028528675187087900672" },
{ "00000001000000000000000000000000", "79228.162514264337593543950336" },
{ "00000000800000000000000000000000", "39614.081257132168796771975168" },
{ "00000000400000000000000000000000", "19807.040628566084398385987584" },
{ "00000000200000000000000000000000", "9903.520314283042199192993792" },
{ "00000000100000000000000000000000", "4951.760157141521099596496896" },
{ "00000000080000000000000000000000", "2475.880078570760549798248448" },
{ "00000000040000000000000000000000", "1237.940039285380274899124224" },
{ "00000000020000000000000000000000", "618.970019642690137449562112" },
{ "00000000010000000000000000000000", "309.485009821345068724781056" },
{ "00000000008000000000000000000000", "154.742504910672534362390528" },
{ "00000000004000000000000000000000", "77.371252455336267181195264" },
{ "00000000002000000000000000000000", "38.685626227668133590597632" },
{ "00000000001000000000000000000000", "19.342813113834066795298816" },
{ "00000000000800000000000000000000", "9.671406556917033397649408" },
{ "00000000000400000000000000000000", "4.835703278458516698824704" },
{ "00000000000200000000000000000000", "2.417851639229258349412352" },
{ "00000000000100000000000000000000", "1.208925819614629174706176" },
{ "00000000000080000000000000000000", "0.604462909807314587353088" },
{ "00000000000040000000000000000000", "0.302231454903657293676544" },
{ "00000000000020000000000000000000", "0.151115727451828646838272" },
{ "00000000000010000000000000000000", "0.075557863725914323419136" },
{ "00000000000008000000000000000000", "0.037778931862957161709568" },
{ "00000000000004000000000000000000", "0.018889465931478580854784" },
{ "00000000000002000000000000000000", "0.009444732965739290427392" },
{ "00000000000001000000000000000000", "0.004722366482869645213696" },
{ "00000000000000800000000000000000", "0.002361183241434822606848" },
{ "00000000000000400000000000000000", "0.001180591620717411303424" },
{ "00000000000000200000000000000000", "0.000590295810358705651712" },
{ "00000000000000100000000000000000", "0.000295147905179352825856" },
{ "00000000000000080000000000000000", "0.000147573952589676412928" },
{ "00000000000000040000000000000000", "0.000073786976294838206464" },
{ "00000000000000020000000000000000", "0.000036893488147419103232" },
{ "00000000000000010000000000000000", "0.000018446744073709551616" },
{ "00000000000000008000000000000000", "0.000009223372036854775808" },
{ "00000000000000004000000000000000", "0.000004611686018427387904" },
{ "00000000000000002000000000000000", "0.000002305843009213693952" },
{ "00000000000000001000000000000000", "0.000001152921504606846976" },
{ "00000000000000000800000000000000", "0.000000576460752303423488" },
{ "00000000000000000400000000000000", "0.000000288230376151711744" },
{ "00000000000000000200000000000000", "0.000000144115188075855872" },
{ "00000000000000000100000000000000", "0.000000072057594037927936" },
{ "00000000000000000080000000000000", "0.000000036028797018963968" },
{ "00000000000000000040000000000000", "0.000000018014398509481984" },
{ "00000000000000000020000000000000", "0.000000009007199254740992" },
{ "00000000000000000010000000000000", "0.000000004503599627370496" },
{ "00000000000000000008000000000000", "0.000000002251799813685248" },
{ "00000000000000000004000000000000", "0.000000001125899906842624" },
{ "00000000000000000002000000000000", "0.000000000562949953421312" },
{ "00000000000000000001000000000000", "0.000000000281474976710656" },
{ "00000000000000000000800000000000", "0.000000000140737488355328" },
{ "00000000000000000000400000000000", "0.000000000070368744177664" },
{ "00000000000000000000200000000000", "0.000000000035184372088832" },
{ "00000000000000000000100000000000", "0.000000000017592186044416" },
{ "00000000000000000000080000000000", "0.000000000008796093022208" },
{ "00000000000000000000040000000000", "0.000000000004398046511104" },
{ "00000000000000000000020000000000", "0.000000000002199023255552" },
{ "00000000000000000000010000000000", "0.000000000001099511627776" },
{ "00000000000000000000008000000000", "0.000000000000549755813888" },
{ "00000000000000000000004000000000", "0.000000000000274877906944" },
{ "00000000000000000000002000000000", "0.000000000000137438953472" },
{ "00000000000000000000001000000000", "0.000000000000068719476736" },
{ "00000000000000000000000800000000", "0.000000000000034359738368" },
{ "00000000000000000000000400000000", "0.000000000000017179869184" },
{ "00000000000000000000000200000000", "0.000000000000008589934592" },
{ "00000000000000000000000100000000", "0.000000000000004294967296" },
{ "00000000000000000000000080000000", "0.000000000000002147483648" },
{ "00000000000000000000000040000000", "0.000000000000001073741824" },
{ "00000000000000000000000020000000", "0.000000000000000536870912" },
{ "00000000000000000000000010000000", "0.000000000000000268435456" },
{ "00000000000000000000000008000000", "0.000000000000000134217728" },
{ "00000000000000000000000004000000", "0.000000000000000067108864" },
{ "00000000000000000000000002000000", "0.000000000000000033554432" },
{ "00000000000000000000000001000000", "0.000000000000000016777216" },
{ "00000000000000000000000000800000", "0.000000000000000008388608" },
{ "00000000000000000000000000400000", "0.000000000000000004194304" },
{ "00000000000000000000000000200000", "0.000000000000000002097152" },
{ "00000000000000000000000000100000", "0.000000000000000001048576" },
{ "00000000000000000000000000080000", "0.000000000000000000524288" },
{ "00000000000000000000000000040000", "0.000000000000000000262144" },
{ "00000000000000000000000000020000", "0.000000000000000000131072" },
{ "00000000000000000000000000010000", "0.000000000000000000065536" },
{ "00000000000000000000000000008000", "0.000000000000000000032768" },
{ "00000000000000000000000000004000", "0.000000000000000000016384" },
{ "00000000000000000000000000002000", "0.000000000000000000008192" },
{ "00000000000000000000000000001000", "0.000000000000000000004096" },
{ "00000000000000000000000000000800", "0.000000000000000000002048" },
{ "00000000000000000000000000000400", "0.000000000000000000001024" },
{ "00000000000000000000000000000200", "0.000000000000000000000512" },
{ "00000000000000000000000000000100", "0.000000000000000000000256" },
{ "00000000000000000000000000000080", "0.000000000000000000000128" },
{ "00000000000000000000000000000040", "0.000000000000000000000064" },
{ "00000000000000000000000000000020", "0.000000000000000000000032" },
{ "00000000000000000000000000000010", "0.000000000000000000000016" },
{ "00000000000000000000000000000008", "0.000000000000000000000008" },
{ "00000000000000000000000000000004", "0.000000000000000000000004" },
{ "00000000000000000000000000000002", "0.000000000000000000000002" },
{ "00000000000000000000000000000001", "0.000000000000000000000001" },
};

for (const auto& kv: testMap) {
auto key = kv.first;
auto expectedResult = kv.second;

base_uint<128> input;
auto typeKind = key.which();
if (typeKind == 0) input = boost::get<base_uint<128>>(key);
else if (typeKind == 1) input = base_uint<128>(boost::get<std::string>(key));
else BOOST_TEST_FAIL("unknown type");

auto res = GetInterestPerBlockHighPrecisionString(input);
if (!res) BOOST_TEST_FAIL("negatives detected");
BOOST_CHECK_EQUAL(*res, expectedResult);
}

// Quick way to generate the nums and verify
// std::vector<base_uint<128>> nums;
// std::string hexStr = "8000 0000 0000 0000 0000 0000 0000 0000";
// hexStr.erase(std::remove(hexStr.begin(), hexStr.end(), ' '), hexStr.end());
// auto i = base_uint<128>(hexStr);
// for (auto n = 0; n < 128; n++) {
// nums.push_back(i);
// std::cout << " { \"" << i.GetHex() << "\", \"";
// std::cout << GetInterestPerBlockHighPrecisionString(i);
// std::cout << "\" }," << std::endl;
// i = i >> 1;
// }
}

BOOST_AUTO_TEST_CASE(loan_iterest_rate)
Expand Down

0 comments on commit 4d10561

Please sign in to comment.