implement payment splitter proposal

This commit is contained in:
Daniel Larimer 2015-07-18 00:52:28 -04:00
parent e4c29cbe78
commit c6b848ef18
6 changed files with 399 additions and 2 deletions

View file

@ -44,6 +44,7 @@
#include <graphene/chain/witness_evaluator.hpp>
#include <graphene/chain/worker_evaluator.hpp>
#include <graphene/chain/balance_evaluator.hpp>
#include <graphene/chain/splitter_evaluator.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
@ -150,6 +151,11 @@ void database::initialize_evaluators()
register_evaluator<withdraw_permission_delete_evaluator>();
register_evaluator<worker_create_evaluator>();
register_evaluator<balance_claim_evaluator>();
register_evaluator<splitter_create_evaluator>();
register_evaluator<splitter_update_evaluator>();
register_evaluator<splitter_pay_evaluator>();
register_evaluator<splitter_payout_evaluator>();
register_evaluator<splitter_delete_evaluator>();
}
void database::initialize_indexes()
@ -177,6 +183,7 @@ void database::initialize_indexes()
add_index< primary_index<vesting_balance_index> >();
add_index< primary_index<worker_index> >();
add_index< primary_index<balance_index> >();
add_index< primary_index<splitter_index> >();
//Implementation object indexes
add_index< primary_index<transaction_index > >();

View file

@ -14,6 +14,7 @@
#include <graphene/chain/protocol/withdraw_permission.hpp>
#include <graphene/chain/protocol/witness.hpp>
#include <graphene/chain/protocol/worker.hpp>
#include <graphene/chain/protocol/splitter.hpp>
namespace graphene { namespace chain {
@ -59,7 +60,12 @@ namespace graphene { namespace chain {
custom_operation,
assert_operation,
balance_claim_operation,
override_transfer_operation
override_transfer_operation,
splitter_create_operation,
splitter_update_operation,
splitter_pay_operation,
splitter_payout_operation,
splitter_delete_operation
> operation;
/// @} // operations group

View file

@ -0,0 +1,147 @@
/* Copyright (c) 2015, Cryptonomex, Inc. */
#pragma once
#include <graphene/chain/protocol/base.hpp>
namespace graphene { namespace chain {
struct market_buyback
{
asset_id_type asset_to_buy;
price limit_price;
void validate()const {
limit_price.validate();
FC_ASSERT( limit_price.quote.asset_id == asset_to_buy );
}
};
typedef static_variant<account_id_type,market_buyback> payment_target_type;
struct payment_target
{
uint16_t weight = 0;
payment_target_type target;
};
struct payment_target_validate
{
typedef void result_type;
void operator()( const account_id_type& id )const { }
void operator()( const market_buyback& t )const
{
FC_ASSERT( t.asset_to_buy == t.limit_price.quote.asset_id );
t.limit_price.validate();
}
};
struct splitter_create_operation : public base_operation
{
/// TODO: charge fee based upon size
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
account_id_type payer;
account_id_type owner;
vector<payment_target> targets;
asset min_payment;
share_type max_payment; ///< same asset_id as min_payment
share_type payout_threshold; ///< same asset_id as min_payment
void validate()const
{
FC_ASSERT( fee.amount >= 0 );
FC_ASSERT( min_payment.amount > 0 );
FC_ASSERT( min_payment.amount <= max_payment );
FC_ASSERT( payout_threshold >= 0 );
for( const auto& t : targets )
{
FC_ASSERT( t.weight > 0 );
t.target.visit( payment_target_validate() );
}
}
account_id_type fee_payer()const { return payer; }
};
struct splitter_update_operation : public base_operation
{
/// TODO: charge fee based upon size
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
splitter_id_type splitter_id;
account_id_type owner; ///< must match splitter_id->owner
account_id_type new_owner;
vector<payment_target> targets;
asset min_payment;
share_type max_payment; ///< same asset_id as min_payment
share_type payout_threshold; ///< same asset_id as min_payment
void validate()const
{
FC_ASSERT( fee.amount >= 0 );
FC_ASSERT( min_payment.amount > 0 );
FC_ASSERT( min_payment.amount <= max_payment );
FC_ASSERT( payout_threshold >= 0 );
for( const auto& t : targets ) FC_ASSERT( t.weight > 0 );
}
account_id_type fee_payer()const { return owner; }
};
struct splitter_pay_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
splitter_id_type splitter_id;
account_id_type paying_account; ///< also fee payer
asset payment;
void validate()const
{
FC_ASSERT( payment.amount > 0 );
FC_ASSERT( fee.amount >= 0 );
}
account_id_type fee_payer()const { return paying_account; }
};
struct splitter_payout_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
splitter_id_type splitter_id;
account_id_type owner; ///< must match splitter_id->owner
void validate()const { FC_ASSERT( fee.amount >= 0 ); }
account_id_type fee_payer()const { return owner; }
};
struct splitter_delete_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
splitter_id_type splitter_id;
account_id_type owner; ///< must match splitter_id->owner
void validate()const { FC_ASSERT( fee.amount >= 0 ); }
account_id_type fee_payer()const { return owner; }
};
} }
FC_REFLECT( graphene::chain::market_buyback, (asset_to_buy)(limit_price) )
FC_REFLECT( graphene::chain::payment_target, (weight)(target) )
FC_REFLECT( graphene::chain::splitter_create_operation, (fee)(payer)(owner)(targets)(min_payment)(max_payment)(payout_threshold) )
FC_REFLECT( graphene::chain::splitter_update_operation, (fee)(owner)(new_owner)(targets)(min_payment)(max_payment)(payout_threshold) )
FC_REFLECT( graphene::chain::splitter_pay_operation, (fee)(splitter_id)(paying_account)(payment) )
FC_REFLECT( graphene::chain::splitter_payout_operation, (fee)(splitter_id)(owner) )
FC_REFLECT( graphene::chain::splitter_delete_operation, (fee)(splitter_id)(owner) )
FC_REFLECT( graphene::chain::splitter_create_operation::fee_parameters_type, (fee) );
FC_REFLECT( graphene::chain::splitter_update_operation::fee_parameters_type, (fee) );
FC_REFLECT( graphene::chain::splitter_pay_operation::fee_parameters_type, (fee) );
FC_REFLECT( graphene::chain::splitter_payout_operation::fee_parameters_type, (fee) );
FC_REFLECT( graphene::chain::splitter_delete_operation::fee_parameters_type, (fee) );

View file

@ -123,6 +123,7 @@ namespace graphene { namespace chain {
vesting_balance_object_type,
worker_object_type,
balance_object_type,
splitter_object_type,
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
};
@ -169,11 +170,12 @@ namespace graphene { namespace chain {
class witness_schedule_object;
class worker_object;
class balance_object;
class splitter_object;
typedef object_id< protocol_ids, account_object_type, account_object> account_id_type;
typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type;
typedef object_id< protocol_ids, force_settlement_object_type, force_settlement_object> force_settlement_id_type;
typedef object_id< protocol_ids, committee_member_object_type, committee_member_object> committee_member_id_type;
typedef object_id< protocol_ids, committee_member_object_type, committee_member_object> committee_member_id_type;
typedef object_id< protocol_ids, witness_object_type, witness_object> witness_id_type;
typedef object_id< protocol_ids, limit_order_object_type, limit_order_object> limit_order_id_type;
typedef object_id< protocol_ids, call_order_object_type, call_order_object> call_order_id_type;
@ -184,6 +186,7 @@ namespace graphene { namespace chain {
typedef object_id< protocol_ids, vesting_balance_object_type, vesting_balance_object> vesting_balance_id_type;
typedef object_id< protocol_ids, worker_object_type, worker_object> worker_id_type;
typedef object_id< protocol_ids, balance_object_type, balance_object> balance_id_type;
typedef object_id< protocol_ids, splitter_object_type, splitter_object> splitter_id_type;
// implementation types
class global_property_object;
@ -376,6 +379,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
(vesting_balance_object_type)
(worker_object_type)
(balance_object_type)
(splitter_object_type)
(OBJECT_TYPE_COUNT)
)
FC_REFLECT_ENUM( graphene::chain::impl_object_type,

View file

@ -0,0 +1,232 @@
/* Copyright (c) 2015, Cryptonomex, Inc. */
#pragma once
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/database.hpp>
namespace graphene { namespace chain {
class splitter_object : public abstract_object<splitter_object>
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = splitter_object_type;
account_id_type owner;
asset balance;
vector<payment_target> targets;
asset min_payment;
share_type max_payment; ///< same asset_id as min_payment
share_type payout_threshold; ///< same asset_id as min_payment
struct payout_visitor
{
typedef void result_type;
database& db;
asset amount;
payout_visitor( database& d, asset a ):db(d),amount(a){}
void operator()( const account_id_type& id )const
{
db.adjust_balance( id(db), amount );
}
void operator()( const market_buyback& t )const
{
const auto& new_order_object = db.create<limit_order_object>([&](limit_order_object& obj){
obj.seller = GRAPHENE_NULL_ACCOUNT;
obj.for_sale = amount.amount;
obj.sell_price = t.limit_price;
assert( amount.asset_id == t.limit_price.base.asset_id );
});
db.apply_order(new_order_object);
}
};
void payout( database& db )const
{
uint64_t total_weight = 0;
for( auto& t : targets ) total_weight += t.weight;
asset remaining = balance;
for( uint32_t i = 0 ; i < targets.size(); ++i )
{
const auto& t = targets[i];
fc::uint128 tmp( balance.amount.value );
tmp *= t.weight;
tmp /= total_weight;
asset payout_amount( tmp.to_uint64(), balance.asset_id );
if( payout_amount > remaining || (i == (targets.size() - 1)) )
payout_amount = remaining;
if( payout_amount.amount > 0 )
{
t.target.visit( payout_visitor( db, payout_amount ) );
}
remaining -= payout_amount;
}
}
};
struct by_account;
typedef multi_index_container<
splitter_object,
indexed_by<
hashed_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_non_unique< tag<by_account>, member< splitter_object, account_id_type, &splitter_object::owner > >
>
> splitter_multi_index_type;
typedef generic_index<splitter_object, splitter_multi_index_type> splitter_index;
struct target_evalautor
{
typedef void result_type;
const database& db;
target_evalautor( database& d ):db(d){}
void operator()( const account_id_type& id )const { id(db); }
void operator()( const market_buyback& t )const
{
/// dereference these objects to verify they exist
t.asset_to_buy(db);
t.limit_price.base.asset_id(db);
}
};
class splitter_create_evaluator : public evaluator<splitter_create_evaluator>
{
public:
typedef splitter_create_operation operation_type;
void_result do_evaluate( const splitter_create_operation& o )
{
o.owner(db()); // dereference to prove it exists
for( auto& t : o.targets )
t.target.visit( target_evalautor(db()) );
return void_result();
}
object_id_type do_apply( const splitter_create_operation& o )
{
const auto& new_splitter_object = db().create<splitter_object>( [&]( splitter_object& obj ){
obj.owner = o.owner;
obj.targets = o.targets;
obj.min_payment = o.min_payment;
obj.max_payment = o.max_payment;
obj.payout_threshold = o.payout_threshold;
obj.balance.asset_id = o.min_payment.asset_id;
});
return new_splitter_object.id;
}
};
class splitter_update_evaluator : public evaluator<splitter_update_evaluator>
{
public:
typedef splitter_update_operation operation_type;
void_result do_evaluate( const splitter_update_operation& o )
{
const auto& sp = o.splitter_id(db()); // dereference to prove it exists
FC_ASSERT( sp.balance.amount == 0 );
FC_ASSERT( sp.owner == o.owner );
for( auto& t : o.targets )
t.target.visit( target_evalautor(db()) );
return void_result();
}
void_result do_apply( const splitter_update_operation& o )
{
const auto& sp = o.splitter_id(db()); // dereference to prove it exists
db().modify( sp, [&]( splitter_object& obj ){
obj.targets = o.targets;
obj.owner = o.new_owner;
obj.min_payment = o.min_payment;
obj.max_payment = o.max_payment;
obj.payout_threshold = o.payout_threshold;
obj.balance.asset_id = o.min_payment.asset_id;
});
return void_result();
}
};
class splitter_pay_evaluator : public evaluator<splitter_pay_evaluator>
{
public:
typedef splitter_pay_operation operation_type;
void_result do_evaluate( const splitter_pay_operation& o )
{
const auto& sp = o.splitter_id(db()); // dereference to prove it exists
FC_ASSERT( o.payment.asset_id == sp.min_payment.asset_id );
FC_ASSERT( o.payment >= sp.min_payment );
FC_ASSERT( o.payment.amount <= sp.max_payment );
return void_result();
}
void_result do_apply( const splitter_pay_operation& o )
{
db().adjust_balance( o.paying_account, -o.payment );
const auto& sp = o.splitter_id(db()); // dereference to prove it exists
db().modify( sp, [&]( splitter_object& obj ){
obj.balance += o.payment;
});
if( sp.balance.amount > sp.payout_threshold )
sp.payout(db());
return void_result();
}
};
class splitter_payout_evaluator : public evaluator<splitter_payout_evaluator>
{
public:
typedef splitter_payout_operation operation_type;
void_result do_evaluate( const splitter_payout_operation& o )
{
const auto& sp = o.splitter_id(db()); // dereference to prove it exists
FC_ASSERT( sp.owner == o.owner );
FC_ASSERT( sp.balance.amount > 0 );
return void_result();
}
void_result do_apply( const splitter_payout_operation& o )
{
const auto& sp = o.splitter_id(db()); // dereference to prove it exists
sp.payout(db());
return void_result();
}
};
class splitter_delete_evaluator : public evaluator<splitter_delete_evaluator>
{
public:
typedef splitter_delete_operation operation_type;
void_result do_evaluate( const splitter_delete_operation& o )
{
const auto& sp = o.splitter_id(db()); // dereference to prove it exists
FC_ASSERT( sp.owner == o.owner );
FC_ASSERT( sp.balance.amount == 0 );
return void_result();
}
void_result do_apply( const splitter_delete_operation& o )
{
db().remove( o.splitter_id(db()) );
return void_result();
}
};
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::splitter_object,
(graphene::db::object),
(owner)(balance)(targets)(min_payment)(max_payment)(payout_threshold)
)

View file

@ -24,6 +24,7 @@
#include <graphene/chain/market_evaluator.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/balance_object.hpp>
#include <graphene/chain/splitter_evaluator.hpp>
#include <iostream>
using namespace graphene::chain;