vector<operation_history_object> history_api::get_account_history(account_id_type account, operation_history_id_type stop, unsigned limit, operation_history_id_type start) const { FC_ASSERT(_app.chain_database()); const auto& db = *_app.chain_database(); FC_ASSERT(limit <= 100); vector<operation_history_object> result; const auto& stats = account(db).statistics(db); if(stats.most_recent_op == account_transaction_history_id_type()) return result; const account_transaction_history_object* node = &stats.most_recent_op(db); if(start == operation_history_id_type()) start = node->id; while(node && node->operation_id.instance.value > stop.instance.value && result.size() < limit) { if(node->id.instance() <= start.instance.value) result.push_back(node->operation_id(db)); if(node->next == account_transaction_history_id_type()) node = nullptr; else node = db.find(node->next); } return result; }
void elasticsearch_plugin_impl::add_elasticsearch( const account_id_type account_id, const optional <operation_history_object>& oho, const signed_block& b) { graphene::chain::database& db = database(); const auto &stats_obj = account_id(db).statistics(db); // add new entry const auto &ath = db.create<account_transaction_history_object>([&](account_transaction_history_object &obj) { obj.operation_id = oho->id; obj.account = account_id; obj.sequence = stats_obj.total_ops + 1; obj.next = stats_obj.most_recent_op; }); // keep stats growing as no op will be removed db.modify(stats_obj, [&](account_statistics_object &obj) { obj.most_recent_op = ath.id; obj.total_ops = ath.sequence; }); // operation_type int op_type = -1; if (!oho->id.is_null()) op_type = oho->op.which(); // operation history data operation_history_struct os; os.trx_in_block = oho->trx_in_block; os.op_in_trx = oho->op_in_trx; os.operation_result = fc::json::to_string(oho->result); os.virtual_op = oho->virtual_op; os.op = fc::json::to_string(oho->op); // visitor data visitor_struct vs; if(_elasticsearch_visitor) { operation_visitor o_v; oho->op.visit(o_v); vs.fee_data.asset = o_v.fee_asset; vs.fee_data.amount = o_v.fee_amount; vs.transfer_data.asset = o_v.transfer_asset_id; vs.transfer_data.amount = o_v.transfer_amount; vs.transfer_data.from = o_v.transfer_from; vs.transfer_data.to = o_v.transfer_to; } // block data std::string trx_id = ""; if(!b.transactions.empty() && oho->trx_in_block < b.transactions.size()) { trx_id = b.transactions[oho->trx_in_block].id().str(); } block_struct bs; bs.block_num = b.block_num(); bs.block_time = b.timestamp; bs.trx_id = trx_id; // check if we are in replay or in sync and change number of bulk documents accordingly uint32_t limit_documents = 0; if((fc::time_point::now() - b.timestamp) < fc::seconds(30)) limit_documents = _elasticsearch_bulk_sync; else limit_documents = _elasticsearch_bulk_replay; createBulkLine(ath, os, op_type, bs, vs); // we have everything, creating bulk line if (curl && bulk.size() >= limit_documents) { // we are in bulk time, ready to add data to elasticsearech sendBulk(_elasticsearch_node_url, _elasticsearch_logs); } // remove everything except current object from ath const auto &his_idx = db.get_index_type<account_transaction_history_index>(); const auto &by_seq_idx = his_idx.indices().get<by_seq>(); auto itr = by_seq_idx.lower_bound(boost::make_tuple(account_id, 0)); if (itr != by_seq_idx.end() && itr->account == account_id && itr->id != ath.id) { // if found, remove the entry const auto remove_op_id = itr->operation_id; const auto itr_remove = itr; ++itr; db.remove( *itr_remove ); // modify previous node's next pointer // this should be always true, but just have a check here if( itr != by_seq_idx.end() && itr->account == account_id ) { db.modify( *itr, [&]( account_transaction_history_object& obj ){ obj.next = account_transaction_history_id_type(); }); } // do the same on oho const auto &by_opid_idx = his_idx.indices().get<by_opid>(); if (by_opid_idx.find(remove_op_id) == by_opid_idx.end()) { db.remove(remove_op_id(db)); } } }