Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTLC API Additions #1742

Merged
merged 5 commits into from Apr 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions libraries/app/application.cpp
Expand Up @@ -340,6 +340,9 @@ void application_impl::set_api_limit() {
if(_options->count("api-limit-get-key-references")){
_app_options.api_limit_get_key_references = _options->at("api-limit-get-key-references").as<uint64_t>();
}
if(_options->count("api-limit-get-htlc-by")) {
_app_options.api_limit_get_htlc_by = _options->at("api-limit-get-htlc-by").as<uint64_t>();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have liked a more descriptive name for this, and have it cover both "to" and "from", but I could not think of one.

}
}

void application_impl::startup()
Expand Down
86 changes: 85 additions & 1 deletion libraries/app/database_api.cpp
Expand Up @@ -168,6 +168,11 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
vector<withdraw_permission_object> get_withdraw_permissions_by_giver(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const;
vector<withdraw_permission_object> get_withdraw_permissions_by_recipient(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const;

// HTLC
optional<htlc_object> get_htlc(htlc_id_type id) const;
vector<htlc_object> get_htlc_by_from(const std::string account_id_or_name, htlc_id_type start, uint32_t limit) const;
vector<htlc_object> get_htlc_by_to(const std::string account_id_or_name, htlc_id_type start, uint32_t limit) const;

//private:
static string price_to_string( const price& _price, const asset_object& _base, const asset_object& _quote );

Expand Down Expand Up @@ -969,7 +974,18 @@ std::map<std::string, full_account> database_api_impl::get_full_accounts( const
acnt.withdraws.emplace_back(withdraw);
});


// get htlcs
auto htlc_from_range = _db.get_index_type<htlc_index>().indices().get<by_from_id>().equal_range(account->id);
std::for_each(htlc_from_range.first, htlc_from_range.second,
[&acnt] (const htlc_object& htlc) {
acnt.htlcs.emplace_back(htlc);
});
auto htlc_to_range = _db.get_index_type<htlc_index>().indices().get<by_to_id>().equal_range(account->id);
std::for_each(htlc_to_range.first, htlc_to_range.second,
[&acnt] (const htlc_object& htlc) {
if ( std::find(acnt.htlcs.begin(), acnt.htlcs.end(), htlc) == acnt.htlcs.end() )
acnt.htlcs.emplace_back(htlc);
});
results[account_name_or_id] = acnt;
}
return results;
Expand Down Expand Up @@ -2382,6 +2398,74 @@ vector<withdraw_permission_object> database_api_impl::get_withdraw_permissions_b
return result;
}

//////////////////////////////////////////////////////////////////////
// //
// HTLC //
// //
//////////////////////////////////////////////////////////////////////

optional<htlc_object> database_api::get_htlc(htlc_id_type id)const
{
return my->get_htlc(id);
}

fc::optional<htlc_object> database_api_impl::get_htlc(htlc_id_type id) const
{
auto obj = get_objects( { id }).front();
if ( !obj.is_null() )
{
return fc::optional<htlc_object>(obj.template as<htlc_object>(GRAPHENE_MAX_NESTED_OBJECTS));
}
return fc::optional<htlc_object>();
}

vector<htlc_object> database_api::get_htlc_by_from(const std::string account_id_or_name, htlc_id_type start, uint32_t limit)const
{
return my->get_htlc_by_from(account_id_or_name, start, limit);
}

vector<htlc_object> database_api_impl::get_htlc_by_from(const std::string account_id_or_name, htlc_id_type start, uint32_t limit) const
{
FC_ASSERT( limit <= _app_options->api_limit_get_htlc_by );
vector<htlc_object> result;

const auto& htlc_idx = _db.get_index_type< htlc_index >().indices().get< by_from_id >();
auto htlc_index_end = htlc_idx.end();
const account_id_type account = get_account_from_string(account_id_or_name)->id;
auto htlc_itr = htlc_idx.lower_bound(boost::make_tuple(account, start));

while(htlc_itr != htlc_index_end && htlc_itr->transfer.from == account && result.size() < limit)
{
result.push_back(*htlc_itr);
++htlc_itr;
}
return result;
}

vector<htlc_object> database_api::get_htlc_by_to(const std::string account_id_or_name, htlc_id_type start, uint32_t limit)const
{
return my->get_htlc_by_to(account_id_or_name, start, limit);
}

vector<htlc_object> database_api_impl::get_htlc_by_to(const std::string account_id_or_name, htlc_id_type start, uint32_t limit) const
{

FC_ASSERT( limit <= _app_options->api_limit_get_htlc_by );
vector<htlc_object> result;

const auto& htlc_idx = _db.get_index_type< htlc_index >().indices().get< by_to_id >();
auto htlc_index_end = htlc_idx.end();
const account_id_type account = get_account_from_string(account_id_or_name)->id;
auto htlc_itr = htlc_idx.lower_bound(boost::make_tuple(account, start));

while(htlc_itr != htlc_index_end && htlc_itr->transfer.to == account && result.size() < limit)
{
result.push_back(*htlc_itr);
++htlc_itr;
}
return result;
}

//////////////////////////////////////////////////////////////////////
// //
// Private methods //
Expand Down
1 change: 1 addition & 0 deletions libraries/app/include/graphene/app/application.hpp
Expand Up @@ -47,6 +47,7 @@ namespace graphene { namespace app {
uint64_t api_limit_get_account_history_by_operations = 100;
uint64_t api_limit_get_asset_holders = 100;
uint64_t api_limit_get_key_references = 100;
uint64_t api_limit_get_htlc_by = 100;
};

class application
Expand Down
36 changes: 35 additions & 1 deletion libraries/app/include/graphene/app/database_api.hpp
Expand Up @@ -40,6 +40,7 @@
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/worker_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/htlc_object.hpp>

#include <graphene/market_history/market_history_plugin.hpp>

Expand Down Expand Up @@ -743,7 +744,36 @@ class database_api
*/
vector<withdraw_permission_object> get_withdraw_permissions_by_recipient(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const;

private:
//////////
// HTLC //
//////////

/**
* @brief Get HTLC object
* @param id HTLC contract id
* @return HTLC object for the id
*/
optional<htlc_object> get_htlc(htlc_id_type id) const;

/**
* @brief Get non expired HTLC objects using the sender account
* @param account_id_or_name Account ID or name to get objects from
* @param start htlc objects before this ID will be skipped in results. Pagination purposes.
* @param limit Maximum number of objects to retrieve
* @return HTLC objects for the account
*/
vector<htlc_object> get_htlc_by_from(const std::string account_id_or_name, htlc_id_type start, uint32_t limit) const;

/**
* @brief Get non expired HTLC objects using the receiver account
* @param account_id_or_name Account ID or name to get objects from
* @param start htlc objects before this ID will be skipped in results. Pagination purposes.
* @param limit Maximum number of objects to retrieve
* @return HTLC objects for the account
*/
vector<htlc_object> get_htlc_by_to(const std::string account_id_or_name, htlc_id_type start, uint32_t limit) const;

private:
std::shared_ptr< database_api_impl > my;
};

Expand Down Expand Up @@ -865,4 +895,8 @@ FC_API(graphene::app::database_api,
(get_withdraw_permissions_by_giver)
(get_withdraw_permissions_by_recipient)

// HTLC
(get_htlc)
(get_htlc_by_from)
(get_htlc_by_to)
)
2 changes: 2 additions & 0 deletions libraries/app/include/graphene/app/full_account.hpp
Expand Up @@ -48,6 +48,7 @@ namespace graphene { namespace app {
vector<proposal_object> proposals;
vector<asset_id_type> assets;
vector<withdraw_permission_object> withdraws;
vector<htlc_object> htlcs;
};

} }
Expand All @@ -68,4 +69,5 @@ FC_REFLECT( graphene::app::full_account,
(proposals)
(assets)
(withdraws)
(htlcs)
)
18 changes: 17 additions & 1 deletion libraries/chain/include/graphene/chain/htlc_object.hpp
Expand Up @@ -77,10 +77,21 @@ namespace graphene { namespace chain {
const result_type& operator()(const htlc_object& o)const { return o.transfer.from; }
};

/*****
* Index helper for to
*/
struct to_extractor {
typedef account_id_type result_type;
const result_type& operator()(const htlc_object& o)const { return o.transfer.to; }
};

bool operator==(const htlc_object& in) { return this->id == in.id; }

};

struct by_from_id;
struct by_expiration;
struct by_to_id;
typedef multi_index_container<
htlc_object,
indexed_by<
Expand All @@ -94,8 +105,13 @@ namespace graphene { namespace chain {
ordered_unique< tag< by_from_id >,
composite_key< htlc_object,
htlc_object::from_extractor,
member< object, object_id_type, &object::id > > >,

ordered_unique< tag< by_to_id >,
composite_key< htlc_object,
htlc_object::to_extractor,
member< object, object_id_type, &object::id > > >
>
>

> htlc_object_index_type;

Expand Down
154 changes: 154 additions & 0 deletions tests/tests/htlc_tests.cpp
Expand Up @@ -832,4 +832,158 @@ try {
} FC_LOG_AND_RETHROW()
}

BOOST_AUTO_TEST_CASE(htlc_database_api) {
try {

ACTORS((alice)(bob)(carl)(dan));

int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION);

transfer( committee_account, alice_id, graphene::chain::asset(init_balance) );

generate_blocks(HARDFORK_CORE_1468_TIME);
set_expiration( db, trx );

set_committee_parameters(this);

uint16_t preimage_size = 256;
std::vector<char> pre_image(256);
std::independent_bits_engine<std::default_random_engine, CHAR_BIT, unsigned char> rbe;
std::generate(begin(pre_image), end(pre_image), std::ref(rbe));
graphene::chain::htlc_id_type alice_htlc_id_bob;
graphene::chain::htlc_id_type alice_htlc_id_carl;
graphene::chain::htlc_id_type alice_htlc_id_dan;

generate_block();
set_expiration( db, trx );
trx.clear();
// alice puts a htlc contract to bob
{
graphene::chain::htlc_create_operation create_operation;
BOOST_TEST_MESSAGE("Alice, who has 100 coins, is transferring 3 coins to Bob");
create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION );
create_operation.to = bob_id;
create_operation.claim_period_seconds = 60;
create_operation.preimage_hash = hash_it<fc::sha256>( pre_image );
create_operation.preimage_size = preimage_size;
create_operation.from = alice_id;
create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation);
trx.operations.push_back(create_operation);
sign(trx, alice_private_key);
PUSH_TX(db, trx, ~0);
trx.clear();
set_expiration( db, trx );
graphene::chain::signed_block blk = generate_block();
processed_transaction alice_trx = blk.transactions[0];
alice_htlc_id_bob = alice_trx.operation_results[0].get<object_id_type>();
generate_block();
set_expiration( db, trx );
}

trx.clear();
// alice puts a htlc contract to carl
{
graphene::chain::htlc_create_operation create_operation;
BOOST_TEST_MESSAGE("Alice, who has 100 coins, is transferring 3 coins to Carl");
create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION );
create_operation.to = carl_id;
create_operation.claim_period_seconds = 60;
create_operation.preimage_hash = hash_it<fc::sha256>( pre_image );
create_operation.preimage_size = preimage_size;
create_operation.from = alice_id;
create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation);
trx.operations.push_back(create_operation);
sign(trx, alice_private_key);
PUSH_TX(db, trx, ~0);
trx.clear();
set_expiration( db, trx );
graphene::chain::signed_block blk = generate_block();
processed_transaction alice_trx = blk.transactions[0];
alice_htlc_id_carl = alice_trx.operation_results[0].get<object_id_type>();
generate_block();
set_expiration( db, trx );
}

trx.clear();
// alice puts a htlc contract to dan
{
graphene::chain::htlc_create_operation create_operation;
BOOST_TEST_MESSAGE("Alice, who has 100 coins, is transferring 3 coins to Dan");
create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION );
create_operation.to = dan_id;
create_operation.claim_period_seconds = 60;
create_operation.preimage_hash = hash_it<fc::sha256>( pre_image );
create_operation.preimage_size = preimage_size;
create_operation.from = alice_id;
create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation);
trx.operations.push_back(create_operation);
sign(trx, alice_private_key);
PUSH_TX(db, trx, ~0);
trx.clear();
set_expiration( db, trx );
graphene::chain::signed_block blk = generate_block();
processed_transaction alice_trx = blk.transactions[0];
alice_htlc_id_dan = alice_trx.operation_results[0].get<object_id_type>();
generate_block();
set_expiration( db, trx );
}

graphene::app::database_api db_api(db, &(this->app.get_options()) ) ;

auto htlc = db_api.get_htlc(alice_htlc_id_bob);
BOOST_CHECK_EQUAL( htlc->id.instance(), 0);
BOOST_CHECK_EQUAL( htlc->transfer.from.instance.value, 16 );
BOOST_CHECK_EQUAL( htlc->transfer.to.instance.value, 17 );

htlc = db_api.get_htlc(alice_htlc_id_carl);
BOOST_CHECK_EQUAL( htlc->id.instance(), 1);
BOOST_CHECK_EQUAL( htlc->transfer.from.instance.value, 16 );
BOOST_CHECK_EQUAL( htlc->transfer.to.instance.value, 18 );

htlc = db_api.get_htlc(alice_htlc_id_dan);
BOOST_CHECK_EQUAL( htlc->id.instance(), 2);
BOOST_CHECK_EQUAL( htlc->transfer.from.instance.value, 16 );
BOOST_CHECK_EQUAL( htlc->transfer.to.instance.value, 19 );

auto htlcs_alice = db_api.get_htlc_by_from(alice.name, graphene::chain::htlc_id_type(0), 100);
BOOST_CHECK_EQUAL( htlcs_alice.size(), 3 );
BOOST_CHECK_EQUAL( htlcs_alice[0].id.instance(), 0 );
BOOST_CHECK_EQUAL( htlcs_alice[1].id.instance(), 1 );
BOOST_CHECK_EQUAL( htlcs_alice[2].id.instance(), 2 );

htlcs_alice = db_api.get_htlc_by_from(alice.name, graphene::chain::htlc_id_type(1), 1);
BOOST_CHECK_EQUAL( htlcs_alice.size(), 1 );
BOOST_CHECK_EQUAL( htlcs_alice[0].id.instance(), 1 );

htlcs_alice = db_api.get_htlc_by_from(alice.name, graphene::chain::htlc_id_type(1), 2);
BOOST_CHECK_EQUAL( htlcs_alice.size(), 2 );
BOOST_CHECK_EQUAL( htlcs_alice[0].id.instance(), 1 );
BOOST_CHECK_EQUAL( htlcs_alice[1].id.instance(), 2 );

auto htlcs_bob = db_api.get_htlc_by_to(bob.name, graphene::chain::htlc_id_type(0), 100);
BOOST_CHECK_EQUAL( htlcs_bob.size(), 1 );
BOOST_CHECK_EQUAL( htlcs_bob[0].id.instance(), 0 );

auto htlcs_carl = db_api.get_htlc_by_to(carl.name, graphene::chain::htlc_id_type(0), 100);
BOOST_CHECK_EQUAL( htlcs_carl.size(), 1 );
BOOST_CHECK_EQUAL( htlcs_carl[0].id.instance(), 1 );

auto htlcs_dan = db_api.get_htlc_by_to(dan.name, graphene::chain::htlc_id_type(0), 100);
BOOST_CHECK_EQUAL( htlcs_dan.size(), 1 );
BOOST_CHECK_EQUAL( htlcs_dan[0].id.instance(), 2 );

auto full = db_api.get_full_accounts({alice.name}, false);
BOOST_CHECK_EQUAL( full[alice.name].htlcs.size(), 3 );

full = db_api.get_full_accounts({bob.name}, false);
BOOST_CHECK_EQUAL( full[bob.name].htlcs.size(), 1 );

} catch (fc::exception &e) {
edump((e.to_detail_string()));
throw;
}
}



BOOST_AUTO_TEST_SUITE_END()