Commit 46b68340 authored by Howo's avatar Howo Committed by Bartek Wrona
Browse files

Fix drift problem include tests for the drift, Increase the RC cost for the...

Fix drift problem include tests for the drift, Increase the RC cost for the operation, add proper state size
parent d651a904
......@@ -2352,6 +2352,92 @@ void database::process_delayed_voting( const block_notification& note )
}
}
/**
* Iterates over all recurrent transfers with a due date date before
* the head block time and then executes the transfers
*/
void database::process_recurrent_transfers()
{
if( has_hardfork( HIVE_HARDFORK_1_25 ) ) {
auto now = head_block_time();
const auto& recurrent_transfers_by_date = get_index< recurrent_transfer_index >().indices().get< by_trigger_date >();
auto itr = recurrent_transfers_by_date.begin();
// uint16_t is okay because we stop at 1000, if the limit changes, make sure to check if it fits in the integer.
uint16_t processed_transfers = 0;
while( itr != recurrent_transfers_by_date.end() && itr->get_trigger_date() <= now ) {
// Since this is an intensive process, we don't want to process too many recurrent transfers in a single block
if (processed_transfers >= HIVE_MAX_RECURRENT_TRANSFERS_PER_BLOCK) {
ilog("Reached max processed recurrent transfers this block");
return;
}
auto &current_recurrent_transfer = *itr;
++itr;
const auto &from_account = get_account(current_recurrent_transfer.from_id);
const auto &to_account = get_account(current_recurrent_transfer.to_id);
asset available = get_balance(from_account, current_recurrent_transfer.amount.symbol);
FC_ASSERT(current_recurrent_transfer.remaining_executions > 0);
const auto remaining_executions = current_recurrent_transfer.remaining_executions -1;
bool remove_recurrent_transfer = false;
// If we have enough money, we proceed with the transfer
if (available >= current_recurrent_transfer.amount) {
adjust_balance(from_account, -current_recurrent_transfer.amount);
adjust_balance(to_account, current_recurrent_transfer.amount);
// No need to update the object if we know that we will remove it
if (remaining_executions == 0) {
remove_recurrent_transfer = true;
} else {
modify(current_recurrent_transfer, [&](recurrent_transfer_object &rt) {
rt.consecutive_failures = 0; // reset the consecutive failures counter
rt.update_next_trigger_date();
rt.remaining_executions = remaining_executions;
});
}
push_virtual_operation(fill_recurrent_transfer_operation(from_account.name, to_account.name, current_recurrent_transfer.amount, to_string(current_recurrent_transfer.memo), remaining_executions));
} else {
uint8_t consecutive_failures = current_recurrent_transfer.consecutive_failures + 1;
if (consecutive_failures < HIVE_MAX_CONSECUTIVE_RECURRENT_TRANSFER_FAILURES) {
// No need to update the object if we know that we will remove it
if (remaining_executions == 0) {
remove_recurrent_transfer = true;
} else {
modify(current_recurrent_transfer, [&](recurrent_transfer_object &rt) {
++rt.consecutive_failures;
rt.update_next_trigger_date();
rt.remaining_executions = remaining_executions;
});
}
// false means the recurrent transfer was not deleted
push_virtual_operation(failed_recurrent_transfer_operation(from_account.name, to_account.name, current_recurrent_transfer.amount, consecutive_failures, to_string(current_recurrent_transfer.memo), remaining_executions, false));
} else {
// if we had too many consecutive failures, remove the recurrent payment object
remove_recurrent_transfer = true;
// true means the recurrent transfer was deleted
push_virtual_operation(failed_recurrent_transfer_operation(from_account.name, to_account.name, current_recurrent_transfer.amount, consecutive_failures, to_string(current_recurrent_transfer.memo), remaining_executions, true));
}
}
if (remove_recurrent_transfer) {
remove( current_recurrent_transfer );
modify(from_account, [&](account_object& a )
{
FC_ASSERT( a.open_recurrent_transfers > 0 );
a.open_recurrent_transfers--;
});
}
processed_transfers++;
}
}
}
/**
* This method updates total_reward_shares2 on DGPO, and children_rshares2 on comments, when a comment's rshares2 changes
* from old_rshares2 to new_rshares2. Maintaining invariants that children_rshares2 is the sum of all descendants' rshares2,
......@@ -3529,6 +3615,7 @@ void database::initialize_evaluators()
_my->_evaluator_registry.register_evaluator< update_proposal_evaluator >();
_my->_evaluator_registry.register_evaluator< update_proposal_votes_evaluator >();
_my->_evaluator_registry.register_evaluator< remove_proposal_evaluator >();
_my->_evaluator_registry.register_evaluator< recurrent_transfer_evaluator >();
#ifdef IS_TEST_NET
......@@ -4064,6 +4151,8 @@ void database::_apply_block( const signed_block& next_block )
process_delayed_voting( note );
remove_expired_governance_votes();
process_recurrent_transfers();
generate_required_actions();
generate_optional_actions();
......
......@@ -653,7 +653,7 @@ void delete_comment_evaluator::do_apply( const delete_comment_operation& o )
_db.remove(cur_vote);
}
/// this loop can be skiped for validate-only nodes as it is merely gathering stats for indicies
/// this loop can be skiped for validate-only nodes as it is merely gathering stats for indices
if( _db.has_hardfork( HIVE_HARDFORK_0_6__80 ) && !comment.is_root() )
{
const comment_cashout_object* parent = _db.find_comment_cashout( _db.get_comment( comment.get_parent_id() ) );
......@@ -3337,4 +3337,51 @@ void delegate_vesting_shares_evaluator::do_apply( const delegate_vesting_shares_
}
}
void recurrent_transfer_evaluator::do_apply( const recurrent_transfer_operation& op )
{
FC_ASSERT( _db.has_hardfork( HIVE_HARDFORK_1_25 ), "Recurrent transfers are not enabled until hardfork ${hf}", ("hf", HIVE_HARDFORK_1_25) );
const auto& from_account = _db.get_account(op.from );
const auto& to_account = _db.get_account( op.to );
FC_ASSERT( from_account.open_recurrent_transfers < HIVE_MAX_OPEN_RECURRENT_TRANSFERS, "Account can't have more than ${rt} recurrent transfers", ("rt", HIVE_MAX_OPEN_RECURRENT_TRANSFERS) );
asset available = _db.get_balance( from_account, op.amount.symbol );
FC_ASSERT( available >= op.amount, "Account does not have enough tokens for the first transfer, has ${has} needs ${needs}", ("has", available)("needs", op.amount) );
const auto& rt_idx = _db.get_index< recurrent_transfer_index >().indices().get< by_from_to_id >();
auto itr = rt_idx.find(boost::make_tuple(from_account.get_id(), to_account.get_id()));
if( itr == rt_idx.end() )
{
// If the recurrent transfer is not found and the amount is 0 it means the user wants to delete a transfer that doesnt exists
FC_ASSERT( op.amount.amount != 0, "Cannot create a recurrent transfer with 0 amount");
_db.create< recurrent_transfer_object >(_db.head_block_time(), from_account.get_id(), to_account.get_id(), op.amount, op.memo, op.recurrence, op.executions);
_db.modify(from_account, [](account_object& a )
{
a.open_recurrent_transfers++;
});
} else if( op.amount.amount == 0 )
{
_db.remove( *itr );
_db.modify(from_account, [&](account_object& a )
{
FC_ASSERT( a.open_recurrent_transfers > 0 );
a.open_recurrent_transfers--;
});
} else
{
_db.modify( *itr, [&]( recurrent_transfer_object& rt )
{
rt.amount = op.amount;
from_string( rt.memo, op.memo );
rt.set_recurrence_trigger_date(_db.head_block_time(), op.recurrence);
rt.remaining_executions = op.executions;
});
}
}
} } // hive::chain
......@@ -150,6 +150,7 @@ namespace hive { namespace chain {
share_type withdrawn = 0; /// Track how many shares have been withdrawn
share_type to_withdraw = 0; /// Might be able to look this up with operation history.
share_type pending_claimed_accounts = 0;
uint16_t open_recurrent_transfers = 0; //for now max is 255, but it might change
/*
Total sum of VESTS from `delayed_votes` collection.
......@@ -613,6 +614,7 @@ FC_REFLECT( hive::chain::account_object,
(delayed_votes)
(sum_delayed_votes)
(governance_vote_expiration_ts)
(open_recurrent_transfers)
)
CHAINBASE_SET_INDEX_TYPE( hive::chain::account_object, hive::chain::account_index )
......
......@@ -671,6 +671,8 @@ namespace chain {
void process_delayed_voting(const block_notification& note );
void process_recurrent_transfers();
void update_global_dynamic_data( const signed_block& b );
void update_signing_witness(const witness_object& signing_witness, const signed_block& new_block);
void update_last_irreversible_block();
......
......@@ -175,6 +175,11 @@ namespace hive { namespace chain {
// objects can accumulate over time but need to be removed in single operation f.e. proposal votes)
int16_t current_remove_threshold = HIVE_GLOBAL_REMOVE_THRESHOLD; //negative means no limit
uint8_t max_consecutive_recurrent_transfer_failures = HIVE_MAX_CONSECUTIVE_RECURRENT_TRANSFER_FAILURES;
uint16_t max_recurrent_transfer_end_date = HIVE_MAX_RECURRENT_TRANSFER_END_DATE;
uint8_t min_recurrent_transfers_recurrence = HIVE_MIN_RECURRENT_TRANSFERS_RECURRENCE;
uint16_t max_open_recurrent_transfers = HIVE_MAX_OPEN_RECURRENT_TRANSFERS;
#ifdef HIVE_ENABLE_SMT
asset smt_creation_fee = asset( 1000, HBD_SYMBOL ); //< TODO: replace with HBD_asset
#endif
......@@ -235,6 +240,10 @@ FC_REFLECT( hive::chain::dynamic_global_property_object,
(sps_interval_ledger)
(downvote_pool_percent)
(current_remove_threshold)
(max_consecutive_recurrent_transfer_failures)
(max_recurrent_transfer_end_date)
(max_open_recurrent_transfers)
(min_recurrent_transfers_recurrence)
#ifdef HIVE_ENABLE_SMT
(smt_creation_fee)
#endif
......
......@@ -68,5 +68,6 @@ HIVE_DEFINE_EVALUATOR( create_proposal )
HIVE_DEFINE_EVALUATOR(update_proposal)
HIVE_DEFINE_EVALUATOR( update_proposal_votes )
HIVE_DEFINE_EVALUATOR( remove_proposal )
HIVE_DEFINE_EVALUATOR( recurrent_transfer )
} } // hive::chain
......@@ -80,6 +80,7 @@ enum object_type
proposal_object_type,
proposal_vote_object_type,
comment_cashout_object_type,
recurrent_transfer_object_type,
#ifdef HIVE_ENABLE_SMT
// SMT objects
smt_token_object_type,
......@@ -125,6 +126,7 @@ class vesting_delegation_expiration_object;
class pending_required_action_object;
class pending_optional_action_object;
class comment_cashout_object;
class recurrent_transfer_object;
#ifdef HIVE_ENABLE_SMT
class smt_token_object;
......@@ -172,6 +174,7 @@ typedef oid_ref< vesting_delegation_expiration_object > vesting_delegation_exp
typedef oid_ref< pending_required_action_object > pending_required_action_id_type;
typedef oid_ref< pending_optional_action_object > pending_optional_action_id_type;
typedef oid_ref< comment_cashout_object > comment_cashout_id_type;
typedef oid_ref< recurrent_transfer_object > recurrent_transfer_id_type;
#ifdef HIVE_ENABLE_SMT
typedef oid_ref< smt_token_object > smt_token_id_type;
......@@ -189,7 +192,7 @@ typedef oid_ref< proposal_vote_object > proposal_vote_id_type;
enum bandwidth_type
{
post, ///< Rate limiting posting reward eligibility over time
forum, ///< Rate limiting for all forum related actins
forum, ///< Rate limiting for all forum related actions
market ///< Rate limiting for all other actions
};
......@@ -338,6 +341,7 @@ FC_REFLECT_ENUM( hive::chain::object_type,
(proposal_object_type)
(proposal_vote_object_type)
(comment_cashout_object_type)
(recurrent_transfer_object_type)
#ifdef HIVE_ENABLE_SMT
(smt_token_object_type)
......
......@@ -333,6 +333,57 @@ namespace hive { namespace chain {
CHAINBASE_UNPACK_CONSTRUCTOR(reward_fund_object);
};
class recurrent_transfer_object : public object< recurrent_transfer_object_type, recurrent_transfer_object >
{
CHAINBASE_OBJECT( recurrent_transfer_object );
public:
template< typename Allocator >
recurrent_transfer_object(allocator< Allocator > a, uint64_t _id,
const time_point_sec& _trigger_date, const account_id_type& _from_id,
const account_id_type& _to_id, const asset& _amount, const string& _memo, const uint16_t _recurrence, const uint16_t _remaining_executions)
: id( _id ), trigger_date( _trigger_date ), from_id( _from_id ), to_id( _to_id ),
amount( _amount ), memo( a ), recurrence( _recurrence ), remaining_executions( _remaining_executions )
{
from_string( memo, _memo );
}
void update_next_trigger_date()
{
trigger_date += fc::hours(recurrence);
}
time_point_sec get_trigger_date() const
{
return trigger_date;
}
// if the recurrence changed, we must update the trigger_date
void set_recurrence_trigger_date( const time_point_sec& _head_block_time, uint16_t _recurrence )
{
if ( _recurrence != recurrence )
trigger_date = _head_block_time + fc::hours( _recurrence );
recurrence = _recurrence;
}
private:
time_point_sec trigger_date;
public:
account_id_type from_id;
account_id_type to_id;
asset amount;
/// The memo is plain-text, any encryption on the memo is up to a higher level protocol.
shared_string memo;
/// How often will the payment be triggered, unit: hours
uint16_t recurrence = 0;
/// How many payment have failed in a row, at HIVE_MAX_CONSECUTIVE_RECURRENT_TRANSFER_FAILURES the object is deleted
uint8_t consecutive_failures = 0;
/// How many executions are remaining
uint16_t remaining_executions = 0;
CHAINBASE_UNPACK_CONSTRUCTOR(recurrent_transfer_object, (memo));
};
struct by_price;
struct by_expiration;
struct by_account;
......@@ -553,6 +604,37 @@ namespace hive { namespace chain {
allocator< reward_fund_object >
> reward_fund_index;
struct by_from_to_id;
struct by_from_id;
struct by_trigger_date;
typedef multi_index_container<
recurrent_transfer_object,
indexed_by<
ordered_unique< tag< by_id >,
const_mem_fun< recurrent_transfer_object, recurrent_transfer_object::id_type, &recurrent_transfer_object::get_id > >,
ordered_unique< tag< by_trigger_date >,
composite_key< recurrent_transfer_object,
const_mem_fun< recurrent_transfer_object, time_point_sec, &recurrent_transfer_object::get_trigger_date >,
const_mem_fun< recurrent_transfer_object, recurrent_transfer_object::id_type, &recurrent_transfer_object::get_id >
>
>,
ordered_unique< tag< by_from_id >,
composite_key< recurrent_transfer_object,
member< recurrent_transfer_object, account_id_type, &recurrent_transfer_object::from_id >,
const_mem_fun< recurrent_transfer_object, recurrent_transfer_object::id_type, &recurrent_transfer_object::get_id >
>
>,
ordered_unique< tag< by_from_to_id >,
composite_key< recurrent_transfer_object,
member< recurrent_transfer_object, account_id_type, &recurrent_transfer_object::from_id >,
member< recurrent_transfer_object, account_id_type, &recurrent_transfer_object::to_id >
>,
composite_key_compare< std::less< account_id_type >, std::less< account_id_type > >
>
>,
allocator< recurrent_transfer_object >
> recurrent_transfer_index;
} } // hive::chain
#include <hive/chain/comment_object.hpp>
......@@ -610,3 +692,6 @@ FC_REFLECT( hive::chain::reward_fund_object,
(curation_reward_curve)
)
CHAINBASE_SET_INDEX_TYPE( hive::chain::reward_fund_object, hive::chain::reward_fund_index )
FC_REFLECT(hive::chain::recurrent_transfer_object, (id)(trigger_date)(from_id)(to_id)(amount)(memo)(recurrence)(consecutive_failures)(remaining_executions) )
CHAINBASE_SET_INDEX_TYPE( hive::chain::recurrent_transfer_object, hive::chain::recurrent_transfer_index )
......@@ -60,6 +60,7 @@ void initialize_core_indexes( database& db )
HIVE_ADD_CORE_INDEX(db, proposal_index);
HIVE_ADD_CORE_INDEX(db, proposal_vote_index);
HIVE_ADD_CORE_INDEX(db, comment_cashout_index);
HIVE_ADD_CORE_INDEX(db, recurrent_transfer_index);
}
index_info::index_info() {}
......
......@@ -193,6 +193,11 @@ struct get_impacted_account_visitor
_impacted.insert( op.new_account_name );
}
void operator()( const recurrent_transfer_operation& op )
{
_impacted.insert( op.from );
_impacted.insert( op.to );
}
// vops
......@@ -389,6 +394,19 @@ struct get_impacted_account_visitor
_impacted.insert( HIVE_INIT_MINER_NAME );
}
void operator()( const fill_recurrent_transfer_operation& op )
{
_impacted.insert( op.from );
_impacted.insert( op.to );
}
void operator()( const failed_recurrent_transfer_operation& op )
{
_impacted.insert( op.from );
_impacted.insert( op.to );
}
//void operator()( const operation& op ){}
};
......
......@@ -285,7 +285,8 @@ struct filtering_visitor
(consolidate_treasury_balance_operation)(effective_comment_vote_operation)(ineffective_delete_comment_operation)
(sps_convert_operation)(expired_account_notification_operation)(changed_recovery_account_operation)
(transfer_to_vesting_completed_operation)(pow_reward_operation)(vesting_shares_split_operation)
(account_created_operation)(fill_collateralized_convert_request_operation)(system_warning_operation) )
(account_created_operation)(fill_collateralized_convert_request_operation)(system_warning_operation)
(fill_recurrent_transfer_operation)(failed_recurrent_transfer_operation) )
private:
uint64_t _filter = 0;
......
......@@ -125,7 +125,9 @@ enum enum_vops_filter : uint64_t
vesting_shares_split_operation = 0x0'20000000ull,
account_created_operation = 0x0'40000000ull,
fill_collateralized_convert_request_operation = 0x0'80000000ull,
system_warning_operation = 0x1'00000000ull
system_warning_operation = 0x1'00000000ull,
fill_recurrent_transfer_operation = 0x2'00000000ull,
failed_recurrent_transfer_operation = 0x4'00000000ull,
};
/** Allows to specify range of blocks to retrieve virtual operations for.
......
......@@ -139,6 +139,7 @@ namespace detail
(list_proposals)
(find_proposals)
(list_proposal_votes)
(find_recurrent_transfers)
)
void on_post_apply_block( const signed_block& b );
......@@ -1201,6 +1202,14 @@ namespace detail
return _database_api->list_proposal_votes( list_args ).proposal_votes;
}
DEFINE_API_IMPL( condenser_api_impl, find_recurrent_transfers )
{
CHECK_ARG_SIZE( 1 )
return _database_api->find_recurrent_transfers( { args[0].as< account_name_type >() } ).recurrent_transfers;
}
void condenser_api_impl::on_post_apply_block( const signed_block& b )
{ try {
boost::lock_guard< boost::mutex > guard( _mtx );
......@@ -1444,6 +1453,7 @@ DEFINE_READ_APIS( condenser_api,
(list_proposals)
(list_proposal_votes)
(find_proposals)
(find_recurrent_transfers)
)
} } } // hive::plugins::condenser_api
......@@ -151,6 +151,7 @@ struct api_account_object
last_vote_time( a.last_vote_time ),
post_bandwidth( a.post_bandwidth ),
pending_claimed_accounts( a.pending_claimed_accounts ),
open_recurrent_transfers( a.open_recurrent_transfers ),
governance_vote_expiration_ts( a.governance_vote_expiration_ts )
{
voting_power = _compute_voting_power(a);
......@@ -238,6 +239,8 @@ struct api_account_object
share_type pending_claimed_accounts = 0;
uint16_t open_recurrent_transfers = 0;
fc::optional< vector< delayed_votes_data > > delayed_votes;
time_point_sec governance_vote_expiration_ts;
......@@ -312,7 +315,11 @@ struct extended_dynamic_global_properties
downvote_pool_percent( o.downvote_pool_percent ),
current_remove_threshold( o.current_remove_threshold ),
early_voting_seconds( o.early_voting_seconds ),
mid_voting_seconds( o.mid_voting_seconds )
mid_voting_seconds( o.mid_voting_seconds ),
max_consecutive_recurrent_transfer_failures( o.max_consecutive_recurrent_transfer_failures ),
max_recurrent_transfer_end_date( o.max_recurrent_transfer_end_date ),
min_recurrent_transfers_recurrence( o.min_recurrent_transfers_recurrence ),
max_open_recurrent_transfers( o.max_open_recurrent_transfers )
{}
uint32_t head_block_number = 0;
......@@ -373,6 +380,11 @@ struct extended_dynamic_global_properties
uint64_t early_voting_seconds = 0;
uint64_t mid_voting_seconds = 0;
uint8_t max_consecutive_recurrent_transfer_failures = HIVE_MAX_CONSECUTIVE_RECURRENT_TRANSFER_FAILURES;
uint16_t max_recurrent_transfer_end_date = HIVE_MAX_RECURRENT_TRANSFER_END_DATE;
uint8_t min_recurrent_transfers_recurrence = HIVE_MIN_RECURRENT_TRANSFERS_RECURRENCE;
uint16_t max_open_recurrent_transfers = HIVE_MAX_OPEN_RECURRENT_TRANSFERS;
};
struct api_witness_object
......@@ -947,6 +959,7 @@ DEFINE_API_ARGS( is_known_transaction, vector< variant >, bo
DEFINE_API_ARGS( list_proposals, vector< variant >, vector< api_proposal_object > )
DEFINE_API_ARGS( find_proposals, vector< variant >, vector< api_proposal_object > )
DEFINE_API_ARGS( list_proposal_votes, vector< variant >, vector< database_api::api_proposal_vote_object > )
DEFINE_API_ARGS( find_recurrent_transfers, vector< variant >, vector< database_api::api_recurrent_transfer_object > )
#undef DEFINE_API_ARGS
......@@ -1046,6 +1059,7 @@ public:
(list_proposals)
(find_proposals)
(list_proposal_votes)
(find_recurrent_transfers)
)
private:
......@@ -1089,8 +1103,8 @@ FC_REFLECT( hive::plugins::condenser_api::api_account_object,
(proxied_vsf_votes)(witnesses_voted_for)
(last_post)(last_root_post)(last_vote_time)
(post_bandwidth)(pending_claimed_accounts)
(delayed_votes)
(governance_vote_expiration_ts)
(delayed_votes)(open_recurrent_transfers)
)
FC_REFLECT_DERIVED( hive::plugins::condenser_api::extended_account, (hive::plugins::condenser_api::api_account_object),
......@@ -1107,6 +1121,8 @@ FC_REFLECT( hive::plugins::condenser_api::extended_dynamic_global_properties,
(vote_power_reserve_rate)(delegation_return_period)(reverse_auction_seconds)(available_account_subsidies)(hbd_stop_percent)(hbd_start_percent)
(next_maintenance_time)(last_budget_time)(next_daily_maintenance_time)(content_reward_percent)(vesting_reward_percent)(sps_fund_percent)(sps_interval_ledger)
(downvote_pool_percent)(current_remove_threshold)(early_voting_seconds)(mid_voting_seconds)
(max_consecutive_recurrent_transfer_failures)(max_recurrent_transfer_end_date)(min_recurrent_transfers_recurrence)
(max_open_recurrent_transfers)
)
FC_REFLECT( hive::plugins::condenser_api::api_witness_object,
......
......@@ -84,6 +84,8 @@ namespace hive { namespace plugins { namespace condenser_api {
typedef changed_recovery_account_operation legacy_changed_recovery_account_operation;
typedef system_warning_operation legacy_system_warning_operation;
typedef ineffective_delete_comment_operation legacy_ineffective_delete_comment_operation;
typedef fill_recurrent_transfer_operation legacy_fill_recurrent_transfer_operation;
typedef failed_recurrent_transfer_operation legacy_failed_recurrent_transfer_operation;
struct legacy_price
{
......@@ -1373,6 +1375,38 @@ namespace hive { namespace plugins { namespace condenser_api {
legacy_asset pending_payout;
};
struct legacy_recurrent_transfer_operation
{
legacy_recurrent_transfer_operation() {}
legacy_recurrent_transfer_operation( const recurrent_transfer_operation& op ) :
from( op.from ),
to( op.to ),
amount( legacy_asset::from_asset( op.amount ) ),
memo( op.memo ),
recurrence( op.recurrence ),
executions( op.executions )
{}
operator recurrent_transfer_operation()const
{
recurrent_transfer_operation op;
op.from = from;
op.to = to;
op.amount = amount;
op.memo = memo;
op.recurrence = recurrence;
op.executions = executions;
return op;
}
account_name_type from;
account_name_type to;
legacy_asset amount;
string memo;
uint16_t recurrence = 0;
uint16_t executions = 0;
};
typedef fc::static_variant<
legacy_vote_operation,
legacy_comment_operation,
......@@ -1455,7 +1489,10 @@ namespace hive { namespace plugins { namespace condenser_api {
legacy_fill_collateralized_convert_request_operation,