Skip to content

Commit

Permalink
Implement rc curve generation in c++ steemit#2787
Browse files Browse the repository at this point in the history
  • Loading branch information
mvandeberg authored and theoreticalbts committed Aug 23, 2018
1 parent db06461 commit 83593e8
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 0 deletions.
1 change: 1 addition & 0 deletions libraries/plugins/rc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ add_library( rc_plugin
rc_utility.cpp
resource_count.cpp
resource_user.cpp
rc_curve.cpp
)

target_link_libraries( rc_plugin chain_plugin block_data_export_plugin steem_chain steem_jsonball steem_protocol )
Expand Down
100 changes: 100 additions & 0 deletions libraries/plugins/rc/rc_curve.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#include "rc_curve.hpp"

#include <steem/protocol/config.hpp>

#include <fc/exception/exception.hpp>
#include <fc/log/logger.hpp>

#include <math.h>
#include <limits>

namespace steem { namespace plugins { namespace rc {

template< typename T >
T check_and_cast( double num )
{
FC_ASSERT( num >= std::numeric_limits< T >::min() && num <= std::numeric_limits< T >::max(),
"Unexpected value out of range. value: ${v} type: ${t}", ("v", num)("t", fc::get_typename< T >::name()) );

return (T)num;
}

rc_resource_params generate_rc_curve( const rc_curve_gen_params& params )
{
try
{
const static double log_2 = std::log( 2.0 );

rc_resource_params result;

result.time_unit = params.time_unit;

uint32_t time_unit_sec = params.time_unit == rc_time_unit_seconds ? 1 : STEEM_BLOCK_INTERVAL;
fc::microseconds budget_time = params.budget_time;
fc::microseconds half_life = params.half_life;
double inelasticity_threshold = double( params.inelasticity_threshold_num ) / params.inelasticity_threshold_denom;

idump( (half_life.to_seconds()) );

double tau = half_life.to_seconds() / log_2;
double decay_per_sec_float = -1 * std::expm1( -1.0 / tau );

double budget_per_sec = double( params.budget ) / double( budget_time.to_seconds() );

uint32_t resource_unit_exponent = 0;
if( params.resource_unit_exponent )
{
resource_unit_exponent = *(params.resource_unit_exponent);
}
else
{
idump( (budget_per_sec)(decay_per_sec_float)(params.small_stockpile_size) );
double pool_eq = budget_per_sec / decay_per_sec_float;
double resource_unit_exponent_float = std::log( params.small_stockpile_size / pool_eq ) / std::log( params.resource_unit_base );
resource_unit_exponent = std::max( 0, check_and_cast< int32_t >( std::ceil( resource_unit_exponent_float ) ) );
}

result.resource_unit = check_and_cast< uint64_t >( std::pow( params.resource_unit_base, resource_unit_exponent ) );

budget_per_sec *= result.resource_unit;
double budget_per_time_unit = budget_per_sec * time_unit_sec;
result.budget_per_time_unit = check_and_cast< int32_t >( budget_per_time_unit + 0.5 );

double pool_eq = budget_per_sec / decay_per_sec_float;
result.pool_eq = check_and_cast< uint64_t >( pool_eq + 1 );
result.max_pool_size = check_and_cast< uint64_t >( ( pool_eq * 2.0 ) + 0.5 );

double a_point = (double)( params.a_point_num ) / (double)( params.a_point_denom );
double u_point = (double)( params.u_point_num ) / (double)( params.u_point_denom );
double k = u_point / ( a_point * ( 1.0 - u_point ) );

double B = inelasticity_threshold * pool_eq;
double A = tau / k;

if( A < 1.0 || B < 1.0 )
{
wlog( "Bad parameter vlaue (is time too short?)" );
FC_ASSERT( false, "Bad parameter vlaue (is time too short?)" );
}

double curve_shift_float = std::log( (0xFFFFFFFFFFFFFFFFull) / A ) / log_2;
uint64_t curve_shift = check_and_cast< uint64_t >( std::floor( curve_shift_float ) );
result.curve_params.coeff_a = check_and_cast< uint64_t >( A * ( std::pow( 2, curve_shift ) + 0.5 ) );
result.curve_params.coeff_b = check_and_cast< uint64_t >( B + 0.5 );
result.curve_params.shift = check_and_cast< uint8_t >( curve_shift );

double decay_per_time_unit_float = -1 * std::expm1( -1 * ( time_unit_sec * log_2 ) / half_life.to_seconds() );

double decay_per_time_unit_denom_shift_float = std::log( 0xFFFFFFFF / decay_per_time_unit_float ) / log_2;
uint64_t decay_per_time_unit_denom_shift = check_and_cast< uint64_t >( std::floor( decay_per_time_unit_denom_shift_float ) );
uint64_t decay_per_time_unit = check_and_cast< uint64_t >( decay_per_time_unit_float * std::pow( 2.0, decay_per_time_unit_denom_shift ) + 0.5 );

result.decay_params.decay_per_time_unit = decay_per_time_unit;
result.decay_params.decay_per_time_unit_denom_shift = decay_per_time_unit_denom_shift;

return result;
}
FC_CAPTURE_LOG_AND_RETHROW( (params) )
}

} } } //steed::plugins::rc
96 changes: 96 additions & 0 deletions libraries/plugins/rc/rc_curve.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* Note:
*
* This header is contained within a source directory because it is not meant to be used anywhere
* EXCEPT the rc plugin. The implementation uses floating point arithmetic which can be
* non-deterministic. This implementation is NOT suitable for a consensus level protocol.
*/

#pragma once
#include <steem/plugins/rc/rc_utility.hpp>

// This header shouldn't be needed, but fc reflection was not working
// and something in here makes it work.
#include <steem/protocol/types.hpp>

#include <fc/time.hpp>

namespace steem { namespace plugins { namespace rc {

/**
* The design of these classes is to be json compatible with the parameters
* defined for use with rc_generate_resource_parameters.py
*
* Some of the structures are redundant compared to existing C++ types but
* are designed for this purpose.
*/

struct rc_time_type
{
uint32_t seconds = 0;
uint32_t minutes = 0;
uint32_t hours = 0;
uint32_t days = 0;
uint32_t weeks = 0;
uint32_t months = 0;
uint32_t years = 0;

operator fc::microseconds() const
{
return fc::seconds( seconds )
+ fc::minutes( minutes )
+ fc::hours( hours )
+ fc::days( days )
+ fc::days( weeks * 7 )
+ fc::days( months * 30 )
+ fc::days( years * 365 );
}
};

struct rc_curve_gen_params
{
rc_time_unit_type time_unit = rc_time_unit_seconds;
rc_time_type budget_time;
uint64_t budget;
rc_time_type half_life;
uint32_t inelasticity_threshold_num = 1;
uint32_t inelasticity_threshold_denom = 128;
uint64_t small_stockpile_size = 0x0000000100000000ull; // 2^32
uint32_t resource_unit_base = 10;
fc::optional< int32_t > resource_unit_exponent;
uint32_t a_point_num = 1;
uint32_t a_point_denom = 32;
uint32_t u_point_num = 1;
uint32_t u_point_denom = 2;
};

rc_resource_params generate_rc_curve( const rc_curve_gen_params& params );

} } } // steem::plugins::rc

FC_REFLECT( steem::plugins::rc::rc_time_type,
(seconds)
(minutes)
(hours)
(days)
(weeks)
(months)
(years)
)
//*
FC_REFLECT( steem::plugins::rc::rc_curve_gen_params,
(time_unit)
(budget_time)
(budget)
(half_life)
(inelasticity_threshold_num)
(inelasticity_threshold_denom)
(small_stockpile_size)
(resource_unit_base)
(resource_unit_exponent)
(a_point_num)
(a_point_denom)
(u_point_num)
(u_point_denom)
)
//*/

0 comments on commit 83593e8

Please sign in to comment.