bool login_api::login(const string& user, const string& password) { idump((user)(password)); optional< api_access_info > acc = _app.get_api_access_info( user ); if( !acc.valid() ) return false; if( acc->password_hash_b64 != "*" ) { std::string password_salt = fc::base64_decode( acc->password_salt_b64 ); std::string acc_password_hash = fc::base64_decode( acc->password_hash_b64 ); fc::sha256 hash_obj = fc::sha256::hash( password + password_salt ); if( hash_obj.data_size() != acc_password_hash.length() ) return false; if( memcmp( hash_obj.data(), acc_password_hash.c_str(), hash_obj.data_size() ) != 0 ) return false; } idump((acc->allowed_apis)); for( const std::string& api_name : acc->allowed_apis ) { auto it = _api_map.find( api_name ); if( it != _api_map.end() ) { wlog( "known api: ${api}", ("api",api_name) ); continue; } idump((api_name)); _api_map[ api_name ] = _app.create_api_by_name( api_name ); } return true; }
int main(int c, char *v[]) { int w, h, pd; uint8_t *x = iio_read_image_uint8_vec("-", &w, &h, &pd); if (pd != 3) return 1; idump(x, w, h); return 0; }
/* * This tty interrupt routine checks to see if the uart receiver * actually caused the interrupt. If so it adds the character to * the tty input queue, echoing and processing backspace and carriage * return. If the queue contains a full line, it wakes up anything * waiting on it. If it is totally full, it beeps at the user. */ int tty_int(void) { char c; int found; char oc; found = 0; again: if ((in(0x72) & 0x81) != 0x81) return (found); c = (in(0x73) & 0x7f); if (c == 0x1a) /* ^Z */ idump(); /* For debugging. */ if (c == '\003') /* ^C */ sendsig(NULL, SIGINT); else if (c == '\017') /* ^O */ flshflag = !flshflag; else if (c == '\023') /* ^S */ stopflag = 1; else if (c == '\021') { /* ^Q */ stopflag = 0; wakeup(&stopflag); } else if (c == '\b') { if (uninsq(&ttyinq, &oc)) { if (oc == '\n') { /* Don't erase past newline. */ insq(&ttyinq, oc); } else { _putc('\b'); _putc(' '); _putc('\b'); } } } else { if ((c == '\r') || (c == '\n')) { c = '\n'; _putc('\r'); } if (insq(&ttyinq, c)) _putc(c); else _putc('\007'); /* Beep if no more room. */ } if ((c == '\n') || (c == '\004')) /* ^D */ wakeup(&ttyinq); found = 1; goto again; /* Loop until the uart has no data ready. */ }
void operator()( const delete_comment_operation& op )const { const auto& idx = _db.get_index_type<tag_index>().indices().get<by_author_comment>(); const auto& auth = _db.get_account(op.author); auto itr = idx.lower_bound( boost::make_tuple( auth.get_id() ) ); while( itr != idx.end() && itr->author == auth.get_id() ) { const auto& tobj = *itr; const auto* obj = _db.find_object( itr->comment ); ++itr; if( !obj ) { idump((tobj)); _db.remove( tobj ); } } }
void apply(database &db, const signed_block &b, const options_type &opts) { auto undo_session = db.start_undo_session(!(opts & skip_undo_block)); db.pre_apply_block(b); if (!(opts & skip_validation)) { FC_ASSERT(b.timestamp.sec_since_epoch() % 3 == 0); if (b.block_num() > 1) { idump((b.block_num())); const auto &head = db.head_block(); FC_ASSERT(b.block_num() == head.block_num + 1); FC_ASSERT(b.timestamp >= head.timestamp + fc::seconds(3)); } } db.create<block_object>([&](block_object &obj) { obj.block_num = b.block_num(); obj.block_id = b.id(); obj.ref_prefix = obj.block_id._hash[1]; obj.previous = b.previous; obj.timestamp = b.timestamp; obj.witness = b.witness; obj.transaction_merkle_root = b.transaction_merkle_root; obj.witness_signature = b.witness_signature; obj.transactions.reserve(b.transactions.size()); for (const auto &t : b.transactions) { obj.transactions.emplace_back(t.id()); } }); for (const auto &trx : b.transactions) { apply(db, trx, opts); } db.post_apply_block(b); undo_session.push(); }
read_only::get_actions_result read_only::get_actions( const read_only::get_actions_params& params )const { edump((params)); auto& chain = history->chain_plug->chain(); const auto& db = chain.db(); const auto& idx = db.get_index<account_history_index, by_account_action_seq>(); int32_t start = 0; int32_t pos = params.pos ? *params.pos : -1; int32_t end = 0; int32_t offset = params.offset ? *params.offset : -20; auto n = params.account_name; idump((pos)); if( pos == -1 ) { auto itr = idx.lower_bound( boost::make_tuple( name(n.value+1), 0 ) ); if( itr == idx.begin() ) { if( itr->account == n ) pos = itr->account_sequence_num+1; } else if( itr != idx.begin() ) --itr; if( itr->account == n ) pos = itr->account_sequence_num + 1; } if( pos== -1 ) pos = 0xfffffff; if( offset > 0 ) { start = pos; end = start + offset; } else { start = pos + offset; if( start > pos ) start = 0; end = pos; } FC_ASSERT( end >= start ); idump((start)(end)); auto start_itr = idx.lower_bound( boost::make_tuple( n, start ) ); auto end_itr = idx.lower_bound( boost::make_tuple( n, end+1) ); auto start_time = fc::time_point::now(); auto end_time = start_time; get_actions_result result; result.last_irreversible_block = chain.last_irreversible_block_num(); while( start_itr != end_itr ) { const auto& a = db.get<action_history_object, by_action_sequence_num>( start_itr->action_sequence_num ); fc::datastream<const char*> ds( a.packed_action_trace.data(), a.packed_action_trace.size() ); action_trace t; fc::raw::unpack( ds, t ); result.actions.emplace_back( ordered_action_result{ start_itr->action_sequence_num, start_itr->account_sequence_num, a.block_num, a.block_time, chain.to_variant_with_abi(t) }); end_time = fc::time_point::now(); if( end_time - start_time > fc::microseconds(100000) ) { result.time_limit_exceeded_error = true; break; } ++start_itr; } return result; }
int tty_inproc(uint8_t minor, unsigned char c) { unsigned char oc; struct termios *td; struct s_queue *q = &ttyinq[minor]; int canon; uint16_t pgrp = tty_pgrp[minor]; uint8_t wr; td = &ttydata[minor]; canon = td->c_lflag & ICANON; if (td->c_iflag & ISTRIP) c &= 0x7f; /* Strip off parity */ if (canon && !c) return 1; /* Simply quit if Null character */ #ifdef CONFIG_IDUMP if (c == 0x1a) /* ^Z */ idump(); /* (For debugging) */ #endif #ifdef CONFIG_MONITOR if (c == 0x01) /* ^A */ trap_monitor(); #endif if (c == '\r' && (td->c_iflag & ICRNL)) c = '\n'; if (c == '\n' && (td->c_iflag & INLCR)) c = '\r'; if (td->c_lflag & ISIG) { if (c == td->c_cc[VINTR]) { /* ^C */ sgrpsig(pgrp, SIGINT); clrq(q); stopflag[minor] = flshflag[minor] = false; return 1; } else if (c == td->c_cc[VQUIT]) { /* ^\ */ sgrpsig(pgrp, SIGQUIT); clrq(q); stopflag[minor] = flshflag[minor] = false; return 1; } } if (c == td->c_cc[VDISCARD]) { /* ^O */ flshflag[minor] = !flshflag[minor]; return 1; } if (td->c_iflag & IXON) { if (c == td->c_cc[VSTOP]) { /* ^S */ stopflag[minor] = true; return 1; } if (c == td->c_cc[VSTART]) { /* ^Q */ stopflag[minor] = false; wakeup(&stopflag[minor]); return 1; } } if (canon) { if (c == td->c_cc[VERASE]) { if (uninsq(q, &oc)) { if (oc == '\n' || oc == td->c_cc[VEOL]) insq(q, oc); /* Don't erase past nl */ else if (td->c_lflag & ECHOE) tty_erase(minor); return 1; } else if (c == td->c_cc[VKILL]) { while (uninsq(q, &oc)) { if (oc == '\n' || oc == td->c_cc[VEOL]) { insq(q, oc); /* Don't erase past nl */ break; } if (td->c_lflag & ECHOK) tty_erase(minor); } return 1; } } } /* All modes come here */ if (c == '\n') { if ((td->c_oflag & OPOST | ONLCR) == OPOST | ONLCR) tty_echo(minor, '\r'); } wr = insq(q, c); if (wr) tty_echo(minor, c); else if (minor < PTY_OFFSET) tty_putc_wait(minor, '\007'); /* Beep if no more room */ if (!canon || c == td->c_cc[VEOL] || c == '\n' || c == td->c_cc[VEOF]) wakeup(q); return wr; }
/** * Starting with the least collateralized orders, fill them if their * call price is above the max(lowest bid,call_limit). * * This method will return true if it filled a short or limit * * @param mia - the market issued asset that should be called. * @param enable_black_swan - when adjusting collateral, triggering a black swan is invalid and will throw * if enable_black_swan is not set to true. * * @return true if a margin call was executed. */ bool database::check_call_orders(const asset_object& mia, bool enable_black_swan) { try { if( !mia.is_market_issued() ) return false; if( check_for_blackswan( mia, enable_black_swan ) ) return false; const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this); if( bitasset.is_prediction_market ) return false; if( bitasset.current_feed.settlement_price.is_null() ) return false; const call_order_index& call_index = get_index_type<call_order_index>(); const auto& call_price_index = call_index.indices().get<by_price>(); const limit_order_index& limit_index = get_index_type<limit_order_index>(); const auto& limit_price_index = limit_index.indices().get<by_price>(); // looking for limit orders selling the most USD for the least CORE auto max_price = price::max( mia.id, bitasset.options.short_backing_asset ); // stop when limit orders are selling too little USD for too much CORE auto min_price = bitasset.current_feed.max_short_squeeze_price(); assert( max_price.base.asset_id == min_price.base.asset_id ); // NOTE limit_price_index is sorted from greatest to least auto limit_itr = limit_price_index.lower_bound( max_price ); auto limit_end = limit_price_index.upper_bound( min_price ); if( limit_itr == limit_end ) return false; auto call_min = price::min( bitasset.options.short_backing_asset, mia.id ); auto call_max = price::max( bitasset.options.short_backing_asset, mia.id ); auto call_itr = call_price_index.lower_bound( call_min ); auto call_end = call_price_index.upper_bound( call_max ); bool filled_limit = false; bool margin_called = false; while( !check_for_blackswan( mia, enable_black_swan ) && call_itr != call_end ) { bool filled_call = false; price match_price; asset usd_for_sale; if( limit_itr != limit_end ) { assert( limit_itr != limit_price_index.end() ); match_price = limit_itr->sell_price; usd_for_sale = limit_itr->amount_for_sale(); } else return margin_called; match_price.validate(); // would be margin called, but there is no matching order #436 bool feed_protected = ( bitasset.current_feed.settlement_price > ~call_itr->call_price ); if( feed_protected && (head_block_time() > HARDFORK_436_TIME) ) return margin_called; // would be margin called, but there is no matching order if( match_price > ~call_itr->call_price ) return margin_called; if( feed_protected ) { ilog( "Feed protected margin call executing (HARDFORK_436_TIME not here yet)" ); idump( (*call_itr) ); idump( (*limit_itr) ); } // idump((*call_itr)); // idump((*limit_itr)); // ilog( "match_price <= ~call_itr->call_price performing a margin call" ); margin_called = true; auto usd_to_buy = call_itr->get_debt(); if( usd_to_buy * match_price > call_itr->get_collateral() ) { elog( "black swan detected" ); edump((enable_black_swan)); FC_ASSERT( enable_black_swan ); globally_settle_asset(mia, bitasset.current_feed.settlement_price ); return true; } asset call_pays, call_receives, order_pays, order_receives; if( usd_to_buy >= usd_for_sale ) { // fill order call_receives = usd_for_sale; order_receives = usd_for_sale * match_price; call_pays = order_receives; order_pays = usd_for_sale; filled_limit = true; filled_call = (usd_to_buy == usd_for_sale); } else { // fill call call_receives = usd_to_buy; order_receives = usd_to_buy * match_price; call_pays = order_receives; order_pays = usd_to_buy; filled_call = true; } FC_ASSERT( filled_call || filled_limit ); auto old_call_itr = call_itr; if( filled_call ) ++call_itr; fill_order(*old_call_itr, call_pays, call_receives); auto old_limit_itr = filled_limit ? limit_itr++ : limit_itr; fill_order(*old_limit_itr, order_pays, order_receives, true); } // whlie call_itr != call_end return margin_called; } FC_CAPTURE_AND_RETHROW() }
/* This routine processes a character in response to an interrupt. It * adds the character to the tty input queue, echoing and processing * backspace and carriage return. If the queue contains a full line, * it wakes up anything waiting on it. If it is totally full, it beeps * at the user. * UZI180 - This routine is called from the raw Hardware read routine, * either interrupt or polled, to process the input character. HFB */ int tty_inproc(uint8_t minor, unsigned char c) { unsigned char oc; int canon; uint8_t wr; struct tty *t = &ttydata[minor]; struct s_queue *q = &ttyinq[minor]; canon = t->termios.c_lflag & ICANON; if (t->termios.c_iflag & ISTRIP) c &= 0x7f; /* Strip off parity */ if (canon && !c) return 1; /* Simply quit if Null character */ #ifdef CONFIG_IDUMP if (c == 0x1a) /* ^Z */ idump(); /* (For debugging) */ #endif #ifdef CONFIG_MONITOR if (c == 0x01) /* ^A */ trap_monitor(); #endif if (c == '\r' && (t->termios.c_iflag & ICRNL)) c = '\n'; if (c == '\n' && (t->termios.c_iflag & INLCR)) c = '\r'; if (t->termios.c_lflag & ISIG) { if (c == t->termios.c_cc[VINTR]) { /* ^C */ wr = SIGINT; goto sigout; } else if (c == t->termios.c_cc[VQUIT]) { /* ^\ */ wr = SIGQUIT; sigout: sgrpsig(t->pgrp, wr); clrq(q); t->flag &= ~(TTYF_STOP | TTYF_DISCARD); return 1; } } if (c == t->termios.c_cc[VDISCARD]) { /* ^O */ t->flag ^= TTYF_DISCARD; return 1; } if (t->termios.c_iflag & IXON) { if (c == t->termios.c_cc[VSTOP]) { /* ^S */ t->flag |= TTYF_STOP; return 1; } if (c == t->termios.c_cc[VSTART]) { /* ^Q */ t->flag &= ~TTYF_STOP; wakeup(&t->flag); return 1; } } if (canon) { if (c == t->termios.c_cc[VERASE]) { wr = ECHOE; goto eraseout; } else if (c == t->termios.c_cc[VKILL]) { wr = ECHOK; goto eraseout; } } /* All modes come here */ if (c == '\n') { if ((t->termios.c_oflag & (OPOST | ONLCR)) == (OPOST | ONLCR)) tty_echo(minor, '\r'); } wr = insq(q, c); if (wr) tty_echo(minor, c); else tty_putc(minor, '\007'); /* Beep if no more room */ if (!canon || c == t->termios.c_cc[VEOL] || c == '\n' || c == t->termios.c_cc[VEOF]) wakeup(q); return wr; eraseout: while (uninsq(q, &oc)) { if (oc == '\n' || oc == t->termios.c_cc[VEOL]) { insq(q, oc); /* Don't erase past nl */ break; } if (t->termios.c_lflag & wr) tty_erase(minor); if (wr == ECHOE) break; } return 1; }
void execute( asset_id_type quote_id, asset_id_type base_id, const fc::time_point_sec& timestamp ) { try { _quote_id = quote_id; _base_id = base_id; auto quote_asset = _pending_state->get_asset_record( _quote_id ); // DISABLE MARKET ISSUED ASSETS if( quote_asset->is_market_issued() ) return; // don't execute anything. // the order book is soreted from low to high price, so to get the last item (highest bid), we need to go to the first item in the // next market class and then back up one auto next_pair = base_id+1 == quote_id ? price( 0, quote_id+1, 0) : price( 0, quote_id, base_id+1 ); _bid_itr = _db_impl._bid_db.lower_bound( market_index_key( next_pair ) ); _ask_itr = _db_impl._ask_db.lower_bound( market_index_key( price( 0, quote_id, base_id) ) ); _short_itr = _db_impl._short_db.lower_bound( market_index_key( next_pair ) ); _collateral_itr = _db_impl._collateral_db.lower_bound( market_index_key( next_pair ) ); if( !_ask_itr.valid() ) { wlog( "ask iter invalid..." ); _ask_itr = _db_impl._ask_db.begin(); } if( _short_itr.valid() ) --_short_itr; else _short_itr = _db_impl._short_db.last(); if( _bid_itr.valid() ) --_bid_itr; else _bid_itr = _db_impl._bid_db.last(); if( _collateral_itr.valid() ) --_collateral_itr; else _collateral_itr = _db_impl._collateral_db.last(); asset consumed_bid_depth(0,base_id); asset consumed_ask_depth(0,base_id); asset usd_fees_collected(0,quote_id); asset trading_volume(0, base_id); omarket_status market_stat = _pending_state->get_market_status( _quote_id, _base_id ); if( !market_stat.valid() ) { if( quote_asset->is_market_issued() ) FC_CAPTURE_AND_THROW( insufficient_depth, (market_stat) ); FC_ASSERT( market_stat.valid() ); } while( get_next_bid() && get_next_ask() ) { idump( (_current_bid)(_current_ask) ); price ask_price = _current_ask->get_price(); // this works for bids, asks, and shorts.... but in the case of a cover // the current ask can go lower than the call price in order to match // the bid.... if( _current_ask->type == cover_order ) { ask_price = std::min( _current_bid->get_price(), _current_ask->get_highest_cover_price() ); } if( _current_bid->get_price() < ask_price ) break; if( quote_asset->is_market_issued() ) { if( !market_stat || market_stat->ask_depth < BTS_BLOCKCHAIN_MARKET_DEPTH_REQUIREMENT/2 || market_stat->bid_depth < BTS_BLOCKCHAIN_MARKET_DEPTH_REQUIREMENT/2 ) FC_CAPTURE_AND_THROW( insufficient_depth, (market_stat) ); } auto quantity = std::min( _current_bid->get_quantity(), _current_ask->get_quantity() ); auto usd_paid_by_bid = quantity * _current_bid->get_price(); auto usd_received_by_ask = quantity * _current_ask->get_price(); auto xts_paid_by_ask = quantity; auto xts_received_by_bid = quantity; consumed_bid_depth += quantity; consumed_ask_depth += quantity; if( _current_bid->type == short_order ) { usd_paid_by_bid = usd_received_by_ask; } if( _current_ask->type == cover_order ) { usd_received_by_ask = usd_paid_by_bid; } FC_ASSERT( usd_paid_by_bid.amount >= 0 ); FC_ASSERT( xts_paid_by_ask.amount >= 0 ); FC_ASSERT( usd_received_by_ask.amount >= 0 ); FC_ASSERT( xts_received_by_bid.amount >= 0 ); FC_ASSERT( usd_paid_by_bid >= usd_received_by_ask ); FC_ASSERT( xts_paid_by_ask >= xts_received_by_bid ); // sanity check to keep supply from growing without bound FC_ASSERT( usd_paid_by_bid < asset(quote_asset->maximum_share_supply,quote_id), "", ("usd_paid_by_bid",usd_paid_by_bid)("asset",quote_asset) ); usd_fees_collected += usd_paid_by_bid - usd_received_by_ask; idump( (usd_fees_collected)(xts_paid_by_ask)(xts_received_by_bid)(quantity) ); market_transaction mtrx; mtrx.bid_owner = _current_bid->get_owner(); mtrx.ask_owner = _current_ask->get_owner(); mtrx.bid_price = _current_bid->get_price(); mtrx.ask_price = ask_price; mtrx.bid_paid = usd_paid_by_bid; mtrx.bid_received = xts_received_by_bid; mtrx.ask_paid = xts_paid_by_ask; mtrx.ask_received = usd_received_by_ask; mtrx.bid_type = _current_bid->type; mtrx.fees_collected = xts_paid_by_ask - xts_received_by_bid; _market_transactions.push_back(mtrx); trading_volume += mtrx.bid_received; market_stat->ask_depth -= xts_paid_by_ask.amount; if( _current_ask->type == ask_order ) { /* rounding errors on price cause this not to go to 0 in some cases */ if( quantity == _current_ask->get_quantity() ) _current_ask->state.balance = 0; else _current_ask->state.balance -= xts_paid_by_ask.amount; FC_ASSERT( _current_ask->state.balance >= 0 ); auto ask_balance_address = withdraw_condition( withdraw_with_signature(_current_ask->get_owner()), quote_id ).get_address(); auto ask_payout = _pending_state->get_balance_record( ask_balance_address ); if( !ask_payout ) ask_payout = balance_record( _current_ask->get_owner(), asset(0,quote_id), 0 ); ask_payout->balance += usd_received_by_ask.amount; ask_payout->last_update = _pending_state->now(); _pending_state->store_balance_record( *ask_payout ); _pending_state->store_ask_record( _current_ask->market_index, _current_ask->state ); } else if( _current_ask->type == cover_order ) { elog( "MATCHING COVER ORDER recv_usd: ${usd} paid_collat: ${c}", ("usd",usd_received_by_ask)("c",xts_paid_by_ask) ); wlog( "current ask: ${c}", ("c",_current_ask) ); // we are in the margin call range... _current_ask->state.balance -= usd_received_by_ask.amount; *(_current_ask->collateral) -= xts_paid_by_ask.amount; FC_ASSERT( _current_ask->state.balance >= 0 ); FC_ASSERT( *_current_ask->collateral >= 0 ); if( _current_ask->state.balance == 0 ) // no more USD left { // send collateral home to mommy & daddy wlog( " collateral balance is now 0!" ); auto ask_balance_address = withdraw_condition( withdraw_with_signature(_current_ask->get_owner()), base_id ).get_address(); auto ask_payout = _pending_state->get_balance_record( ask_balance_address ); if( !ask_payout ) ask_payout = balance_record( _current_ask->get_owner(), asset(0,base_id), 0 ); ask_payout->balance += (*_current_ask->collateral); ask_payout->last_update = _pending_state->now(); _pending_state->store_balance_record( *ask_payout ); _current_ask->collateral = 0; } wlog( "storing collateral ${c}", ("c",_current_ask) ); _pending_state->store_collateral_record( _current_ask->market_index, collateral_record( *_current_ask->collateral, _current_ask->state.balance ) ); } if( _current_bid->type == bid_order ) { _current_bid->state.balance -= usd_paid_by_bid.amount; FC_ASSERT( _current_bid->state.balance >= 0 ); auto bid_payout = _pending_state->get_balance_record( withdraw_condition( withdraw_with_signature(_current_bid->get_owner()), base_id ).get_address() ); if( !bid_payout ) bid_payout = balance_record( _current_bid->get_owner(), asset(0,base_id), 0 ); bid_payout->balance += xts_received_by_bid.amount; bid_payout->last_update = _pending_state->now(); _pending_state->store_balance_record( *bid_payout ); _pending_state->store_bid_record( _current_bid->market_index, _current_bid->state ); } else if( _current_bid->type == short_order ) { market_stat->bid_depth -= xts_received_by_bid.amount; // TODO: what if the amount paid is 0 for bid and ask due to rounding errors, // make sure this doesn't put us in an infinite loop. if( quantity == _current_bid->get_quantity() ) _current_bid->state.balance = 0; else _current_bid->state.balance -= xts_received_by_bid.amount; FC_ASSERT( _current_bid->state.balance >= 0 ); auto collateral = (xts_paid_by_ask + xts_received_by_bid).amount; auto cover_price = usd_received_by_ask / asset( (3*collateral)/4, base_id ); market_index_key cover_index( cover_price, _current_ask->get_owner() ); auto ocover_record = _pending_state->get_collateral_record( cover_index ); if( NOT ocover_record ) ocover_record = collateral_record(); ocover_record->collateral_balance += collateral; ocover_record->payoff_balance += usd_received_by_ask.amount; FC_ASSERT( ocover_record->payoff_balance >= 0 ); FC_ASSERT( ocover_record->collateral_balance >= 0 ); _pending_state->store_collateral_record( cover_index, *ocover_record ); _pending_state->store_short_record( _current_bid->market_index, _current_bid->state ); } } // while bid && ask if( quote_asset->is_market_issued() ) { if( !market_stat || market_stat->ask_depth < BTS_BLOCKCHAIN_MARKET_DEPTH_REQUIREMENT/2 || market_stat->bid_depth < BTS_BLOCKCHAIN_MARKET_DEPTH_REQUIREMENT/2 ) FC_CAPTURE_AND_THROW( insufficient_depth, (market_stat) ); } _pending_state->store_market_status( *market_stat ); if( trading_volume.amount > 0 && get_next_bid() && get_next_ask() ) { market_history_key key(quote_id, base_id, market_history_key::each_block, _db_impl._head_block_header.timestamp); market_history_record new_record(_current_bid->get_price(), _current_ask->get_price(), trading_volume.amount); //LevelDB iterators are dumb and don't support proper past-the-end semantics. auto last_key_itr = _db_impl._market_history_db.lower_bound(key); if( !last_key_itr.valid() ) last_key_itr = _db_impl._market_history_db.last(); else --last_key_itr; key.timestamp = timestamp; //Unless the previous record for this market is the same as ours... if( (!(last_key_itr.valid() && last_key_itr.key().quote_id == quote_id && last_key_itr.key().base_id == base_id && last_key_itr.key().granularity == market_history_key::each_block && last_key_itr.value() == new_record)) ) { //...add a new entry to the history table. _pending_state->market_history[key] = new_record; } fc::time_point_sec start_of_this_hour = timestamp - (timestamp.sec_since_epoch() % (60*60)); market_history_key old_key(quote_id, base_id, market_history_key::each_hour, start_of_this_hour); if( auto opt = _db_impl._market_history_db.fetch_optional(old_key) ) { auto old_record = *opt; old_record.volume += new_record.volume; if( new_record.highest_bid > old_record.highest_bid || new_record.lowest_ask < old_record.lowest_ask ) { old_record.highest_bid = std::max(new_record.highest_bid, old_record.highest_bid); old_record.lowest_ask = std::min(new_record.lowest_ask, old_record.lowest_ask); _pending_state->market_history[old_key] = old_record; } } else _pending_state->market_history[old_key] = new_record; fc::time_point_sec start_of_this_day = timestamp - (timestamp.sec_since_epoch() % (60*60*24)); old_key = market_history_key(quote_id, base_id, market_history_key::each_day, start_of_this_day); if( auto opt = _db_impl._market_history_db.fetch_optional(old_key) ) { auto old_record = *opt; old_record.volume += new_record.volume; if( new_record.highest_bid > old_record.highest_bid || new_record.lowest_ask < old_record.lowest_ask ) { old_record.highest_bid = std::max(new_record.highest_bid, old_record.highest_bid); old_record.lowest_ask = std::min(new_record.lowest_ask, old_record.lowest_ask); _pending_state->market_history[old_key] = old_record; } } else _pending_state->market_history[old_key] = new_record; } auto market_state = _pending_state->get_market_status( quote_id, base_id ); if( !market_state ) market_state = market_status( quote_id, base_id, 0, 0 ); market_state->last_error.reset(); _pending_state->store_market_status( *market_state ); wlog( "done matching orders" ); _pending_state->apply_changes(); } catch( const fc::exception& e ) { wlog( "error executing market ${quote} / ${base}\n ${e}", ("quote",quote_id)("base",base_id)("e",e.to_detail_string()) ); auto market_state = _prior_state->get_market_status( quote_id, base_id ); if( !market_state ) market_state = market_status( quote_id, base_id, 0, 0 ); market_state->last_error = e; _prior_state->store_market_status( *market_state ); } } // execute(...)
void execute( asset_id_type quote_id, asset_id_type base_id, const fc::time_point_sec& timestamp ) { try { const uint32_t pending_block_num = _pending_state->get_head_block_num(); _quote_id = quote_id; _base_id = base_id; oasset_record quote_asset = _pending_state->get_asset_record( _quote_id ); oasset_record base_asset = _pending_state->get_asset_record( _base_id ); FC_ASSERT( quote_asset.valid() && base_asset.valid() ); // The order book is sorted from low to high price. So to get the last item (highest bid), // we need to go to the first item in the next market class and then back up one const price next_pair = (base_id+1 == quote_id) ? price( 0, quote_id+1, 0 ) : price( 0, quote_id, base_id+1 ); _bid_itr = _db_impl._bid_db.lower_bound( market_index_key( next_pair ) ); _ask_itr = _db_impl._ask_db.lower_bound( market_index_key( price( 0, quote_id, base_id) ) ); _short_itr = _db_impl._short_db.lower_bound( market_index_key( price( 0, quote_id, base_id) ) ); _collateral_itr = _db_impl._collateral_db.lower_bound( market_index_key( next_pair ) ); if( !_ask_itr.valid() ) { wlog( "ask iter invalid..." ); _ask_itr = _db_impl._ask_db.begin(); } if( _bid_itr.valid() ) --_bid_itr; else _bid_itr = _db_impl._bid_db.last(); if( _collateral_itr.valid() ) --_collateral_itr; else _collateral_itr = _db_impl._collateral_db.last(); asset trading_volume(0, base_id); // Set initial market status { omarket_status market_stat = _pending_state->get_market_status( _quote_id, _base_id ); if( !market_stat ) market_stat = market_status( quote_id, base_id, 0, 0 ); _market_stat = *market_stat; } price min_cover_ask; price opening_price; price closing_price; const oprice median_feed_price = _db_impl.self->get_median_delegate_price( quote_id, base_id ); if( quote_asset->is_market_issued() ) { // If bootstrapping market for the very first time if( _market_stat.center_price.ratio == fc::uint128_t() ) { if( median_feed_price.valid() ) _market_stat.center_price = *median_feed_price; else FC_CAPTURE_AND_THROW( insufficient_feeds, (quote_id) ); } min_cover_ask = _market_stat.minimum_ask(); } int last_orders_filled = -1; bool order_did_execute = false; // prime the pump, to make sure that margin calls (asks) have a bid to check against. get_next_bid(); get_next_ask(); idump( (_current_bid)(_current_ask) ); while( get_next_bid() && get_next_ask() ) { idump( (_current_bid)(_current_ask) ); // Make sure that at least one order was matched every time we enter the loop FC_ASSERT( _orders_filled != last_orders_filled, "We appear caught in an order matching loop" ); last_orders_filled = _orders_filled; const asset bid_quantity_xts = _current_bid->get_quantity(); const asset ask_quantity_xts = _current_ask->get_quantity(); asset current_bid_balance = _current_bid->get_balance(); // Initialize the market transaction market_transaction mtrx; mtrx.bid_owner = _current_bid->get_owner(); mtrx.ask_owner = _current_ask->get_owner(); // Always execute shorts at the center price mtrx.bid_price = (_current_bid->type != short_order) ? _current_bid->get_price() : _market_stat.center_price; mtrx.ask_price = _current_ask->get_price(); mtrx.bid_type = _current_bid->type; mtrx.ask_type = _current_ask->type; if( _current_ask->type == cover_order && _current_bid->type == short_order ) { FC_ASSERT( quote_asset->is_market_issued() ); /** don't allow new shorts to execute unless there is a feed, all other * trades are still valid. (we shouldn't stop the market) */ if( !median_feed_price.valid() ) { _current_bid.reset(); continue; } if( mtrx.ask_price < mtrx.bid_price ) // The call price has not been reached break; if( _current_bid->state.short_price_limit.valid() ) { if( *_current_bid->state.short_price_limit < mtrx.ask_price ) { _current_bid.reset(); continue; // skip shorts that are over the price limit. } mtrx.bid_price = *_current_bid->state.short_price_limit; } mtrx.ask_price = mtrx.bid_price; // Bound collateral ratio price collateral_rate = _current_bid->get_price(); if( collateral_rate > _market_stat.center_price ) collateral_rate = _market_stat.center_price; const asset ask_quantity_usd = _current_ask->get_quote_quantity(); const asset short_quantity_usd = _current_bid->get_balance() / collateral_rate; const asset trade_quantity_usd = std::min( short_quantity_usd, ask_quantity_usd ); mtrx.ask_received = trade_quantity_usd; mtrx.bid_paid = mtrx.ask_received; mtrx.ask_paid = mtrx.ask_received * mtrx.ask_price; mtrx.bid_received = mtrx.ask_paid; mtrx.bid_collateral = mtrx.bid_paid / collateral_rate; // Handle rounding errors if( (*mtrx.bid_collateral - _current_bid->get_balance()).amount < BTS_BLOCKCHAIN_PRECISION ) mtrx.bid_collateral = _current_bid->get_balance(); // If too little collateral at this price if( *mtrx.bid_collateral < mtrx.ask_paid ) { edump( (mtrx) ); _current_bid.reset(); continue; } pay_current_short( mtrx, *quote_asset ); pay_current_cover( mtrx, *quote_asset ); _market_stat.bid_depth -= mtrx.bid_collateral->amount; _market_stat.ask_depth += mtrx.bid_collateral->amount; order_did_execute = true; } else if( _current_ask->type == cover_order && _current_bid->type == bid_order ) { FC_ASSERT( quote_asset->is_market_issued() ); if( mtrx.ask_price < mtrx.bid_price ) // The call price has not been reached break; /** * Don't allow margin calls to be executed too far below * the minimum ask, this could lead to an attack where someone * walks the whole book to steal the collateral. */ if( mtrx.bid_price < _market_stat.minimum_ask() ) { _current_ask.reset(); continue; } mtrx.ask_price = mtrx.bid_price; const asset max_usd_purchase = asset( *_current_ask->collateral, _base_id ) * mtrx.bid_price; asset usd_exchanged = std::min( current_bid_balance, max_usd_purchase ); // Bound quote asset amount exchanged const asset required_usd_purchase = _current_ask->get_balance(); if( required_usd_purchase < usd_exchanged ) usd_exchanged = required_usd_purchase; mtrx.ask_received = usd_exchanged; mtrx.bid_paid = mtrx.ask_received; // Handle rounding errors if( usd_exchanged == max_usd_purchase ) mtrx.ask_paid = asset(*_current_ask->collateral,_base_id); else mtrx.ask_paid = usd_exchanged * mtrx.bid_price; mtrx.bid_received = mtrx.ask_paid; pay_current_bid( mtrx, *quote_asset ); pay_current_cover( mtrx, *quote_asset ); // TODO: Do we need to decrease bid depth as well? _market_stat.ask_depth -= mtrx.ask_paid.amount; order_did_execute = true; } else if( _current_ask->type == ask_order && _current_bid->type == short_order ) { FC_ASSERT( quote_asset->is_market_issued() ); /** don't allow new shorts to execute unless there is a feed, all other * trades are still valid. */ if( !median_feed_price.valid() ) { _current_bid.reset(); continue; } if( mtrx.bid_price < mtrx.ask_price ) // The ask price hasn't been reached break; if( _current_bid->state.short_price_limit.valid() ) { if( *_current_bid->state.short_price_limit < mtrx.ask_price ) { elog( "short price limit < bid price" ); _current_bid.reset(); continue; // skip shorts that are over the price limit. } mtrx.bid_price = *_current_bid->state.short_price_limit; } // Bound collateral ratio price collateral_rate = _current_bid->get_price(); if( collateral_rate > _market_stat.center_price ) collateral_rate = _market_stat.center_price; const asset ask_quantity_usd = _current_ask->get_quote_quantity(); const asset short_quantity_usd = _current_bid->get_balance() / collateral_rate; const asset trade_quantity_usd = std::min( short_quantity_usd, ask_quantity_usd ); mtrx.ask_received = trade_quantity_usd; mtrx.bid_paid = mtrx.ask_received; mtrx.ask_paid = mtrx.ask_received * mtrx.ask_price; mtrx.bid_received = mtrx.ask_paid; mtrx.bid_collateral = mtrx.bid_paid / collateral_rate; // Handle rounding errors if( (*mtrx.bid_collateral - _current_bid->get_balance()).amount < BTS_BLOCKCHAIN_PRECISION ) mtrx.bid_collateral = _current_bid->get_balance(); // If too little collateral at this price if( *mtrx.bid_collateral < mtrx.ask_paid ) { edump( (mtrx) ); _current_bid.reset(); continue; } pay_current_short( mtrx, *quote_asset ); pay_current_ask( mtrx, *quote_asset ); _market_stat.bid_depth -= mtrx.bid_collateral->amount; _market_stat.ask_depth += mtrx.bid_collateral->amount; order_did_execute = true; } else if( _current_ask->type == ask_order && _current_bid->type == bid_order ) { if( mtrx.bid_price < mtrx.ask_price ) // The ask price hasn't been reached break; const asset quantity_xts = std::min( bid_quantity_xts, ask_quantity_xts ); // Everyone gets the price they asked for mtrx.ask_received = quantity_xts * mtrx.ask_price; mtrx.bid_paid = quantity_xts * mtrx.bid_price; mtrx.ask_paid = quantity_xts; mtrx.bid_received = quantity_xts; // Handle rounding errors if( quantity_xts == bid_quantity_xts ) mtrx.bid_paid = current_bid_balance; mtrx.fees_collected = mtrx.bid_paid - mtrx.ask_received; pay_current_bid( mtrx, *quote_asset ); pay_current_ask( mtrx, *base_asset ); // TODO: Do we need to decrease bid depth as well? _market_stat.ask_depth -= mtrx.ask_paid.amount; order_did_execute = true; } push_market_transaction( mtrx ); if( mtrx.ask_received.asset_id == 0 ) trading_volume += mtrx.ask_received; else if( mtrx.bid_received.asset_id == 0 ) trading_volume += mtrx.bid_received; if( opening_price == price() ) opening_price = mtrx.bid_price; closing_price = mtrx.bid_price; if( mtrx.fees_collected.asset_id == base_asset->id ) base_asset->collected_fees += mtrx.fees_collected.amount; else if( mtrx.fees_collected.asset_id == quote_asset->id ) quote_asset->collected_fees += mtrx.fees_collected.amount; } // while( next bid && next ask ) // update any fees collected _pending_state->store_asset_record( *quote_asset ); _pending_state->store_asset_record( *base_asset ); _market_stat.last_error.reset(); // Force at least one center price update every 6 blocks order_did_execute |= (pending_block_num % 6) == 0; if( _current_bid && _current_ask && order_did_execute ) { if( median_feed_price.valid() ) { _market_stat.center_price = *median_feed_price; } else if( _current_bid->type != short_order ) // we cannot use short prices for this { _market_stat.center_price.ratio *= (BTS_BLOCKCHAIN_BLOCKS_PER_HOUR-1); const price max_bid = _market_stat.maximum_bid(); // limit the maximum movement rate of the price. if( _current_bid->get_price() < min_cover_ask ) _market_stat.center_price.ratio += min_cover_ask.ratio; else if( _current_bid->get_price() > max_bid ) _market_stat.center_price.ratio += max_bid.ratio; else _market_stat.center_price.ratio += _current_bid->get_price().ratio; if( _current_ask->get_price() < min_cover_ask ) _market_stat.center_price.ratio += min_cover_ask.ratio; else if( _current_ask->get_price() > max_bid ) _market_stat.center_price.ratio += max_bid.ratio; else _market_stat.center_price.ratio += _current_ask->get_price().ratio; _market_stat.center_price.ratio /= (BTS_BLOCKCHAIN_BLOCKS_PER_HOUR+1); } } // Update market status and market history _pending_state->store_market_status( _market_stat ); update_market_history( trading_volume, opening_price, closing_price, _market_stat, timestamp ); wlog( "done matching orders" ); idump( (_current_bid)(_current_ask) ); _pending_state->apply_changes(); } catch( const fc::exception& e ) { wlog( "error executing market ${quote} / ${base}\n ${e}", ("quote",quote_id)("base",base_id)("e",e.to_detail_string()) ); auto market_state = _prior_state->get_market_status( quote_id, base_id ); if( !market_state ) market_state = market_status( quote_id, base_id, 0, 0 ); market_state->last_error = e; _prior_state->store_market_status( *market_state ); } } // execute(...)
/** * Returns a synopsis of the blockchain used for syncing. This consists of a list of * block hashes at intervals exponentially increasing towards the genesis block. * When syncing to a peer, the peer uses this data to determine if we're on the same * fork as they are, and if not, what blocks they need to send us to get us on their * fork. * * In the over-simplified case, this is a straighforward synopsis of our current * preferred blockchain; when we first connect up to a peer, this is what we will be sending. * It looks like this: * If the blockchain is empty, it will return the empty list. * If the blockchain has one block, it will return a list containing just that block. * If it contains more than one block: * the first element in the list will be the hash of the highest numbered block that * we cannot undo * the second element will be the hash of an item at the half way point in the undoable * segment of the blockchain * the third will be ~3/4 of the way through the undoable segment of the block chain * the fourth will be at ~7/8... * &c. * the last item in the list will be the hash of the most recent block on our preferred chain * so if the blockchain had 26 blocks labeled a - z, the synopsis would be: * a n u x z * the idea being that by sending a small (<30) number of block ids, we can summarize a huge * blockchain. The block ids are more dense near the end of the chain where because we are * more likely to be almost in sync when we first connect, and forks are likely to be short. * If the peer we're syncing with in our example is on a fork that started at block 'v', * then they will reply to our synopsis with a list of all blocks starting from block 'u', * the last block they know that we had in common. * * In the real code, there are several complications. * * First, as an optimization, we don't usually send a synopsis of the entire blockchain, we * send a synopsis of only the segment of the blockchain that we have undo data for. If their * fork doesn't build off of something in our undo history, we would be unable to switch, so there's * no reason to fetch the blocks. * * Second, when a peer replies to our initial synopsis and gives us a list of the blocks they think * we are missing, they only send a chunk of a few thousand blocks at once. After we get those * block ids, we need to request more blocks by sending another synopsis (we can't just say "send me * the next 2000 ids" because they may have switched forks themselves and they don't track what * they've sent us). For faster performance, we want to get a fairly long list of block ids first, * then start downloading the blocks. * The peer doesn't handle these follow-up block id requests any different from the initial request; * it treats the synopsis we send as our blockchain and bases its response entirely off that. So to * get the response we want (the next chunk of block ids following the last one they sent us, or, * failing that, the shortest fork off of the last list of block ids they sent), we need to construct * a synopsis as if our blockchain was made up of: * 1. the blocks in our block chain up to the fork point (if there is a fork) or the head block (if no fork) * 2. the blocks we've already pushed from their fork (if there's a fork) * 3. the block ids they've previously sent us * Segment 3 is handled in the p2p code, it just tells us the number of blocks it has (in * number_of_blocks_after_reference_point) so we can leave space in the synopsis for them. * We're responsible for constructing the synopsis of Segments 1 and 2 from our active blockchain and * fork database. The reference_point parameter is the last block from that peer that has been * successfully pushed to the blockchain, so that tells us whether the peer is on a fork or on * the main chain. */ virtual std::vector<item_hash_t> get_blockchain_synopsis(const item_hash_t& reference_point, uint32_t number_of_blocks_after_reference_point) override { try { std::vector<item_hash_t> synopsis; synopsis.reserve(30); uint32_t high_block_num; uint32_t non_fork_high_block_num; uint32_t low_block_num = _chain_db->last_non_undoable_block_num(); std::vector<block_id_type> fork_history; if (reference_point != item_hash_t()) { // the node is asking for a summary of the block chain up to a specified // block, which may or may not be on a fork // for now, assume it's not on a fork if (is_included_block(reference_point)) { // reference_point is a block we know about and is on the main chain uint32_t reference_point_block_num = block_header::num_from_id(reference_point); assert(reference_point_block_num > 0); high_block_num = reference_point_block_num; non_fork_high_block_num = high_block_num; if (reference_point_block_num < low_block_num) { // we're on the same fork (at least as far as reference_point) but we've passed // reference point and could no longer undo that far if we diverged after that // block. This should probably only happen due to a race condition where // the network thread calls this function, and then immediately pushes a bunch of blocks, // then the main thread finally processes this function. // with the current framework, there's not much we can do to tell the network // thread what our current head block is, so we'll just pretend that // our head is actually the reference point. // this *may* enable us to fetch blocks that we're unable to push, but that should // be a rare case (and correctly handled) low_block_num = reference_point_block_num; } } else { // block is a block we know about, but it is on a fork try { fork_history = _chain_db->get_block_ids_on_fork(reference_point); // returns a vector where the last element is the common ancestor with the preferred chain, // and the first element is the reference point you passed in assert(fork_history.size() >= 2); if( fork_history.front() != reference_point ) { edump( (fork_history)(reference_point) ); assert(fork_history.front() == reference_point); } block_id_type last_non_fork_block = fork_history.back(); fork_history.pop_back(); // remove the common ancestor boost::reverse(fork_history); if (last_non_fork_block == block_id_type()) // if the fork goes all the way back to genesis (does graphene's fork db allow this?) non_fork_high_block_num = 0; else non_fork_high_block_num = block_header::num_from_id(last_non_fork_block); high_block_num = non_fork_high_block_num + fork_history.size(); assert(high_block_num == block_header::num_from_id(fork_history.back())); } catch (const fc::exception& e) { // unable to get fork history for some reason. maybe not linked? // we can't return a synopsis of its chain elog("Unable to construct a blockchain synopsis for reference hash ${hash}: ${exception}", ("hash", reference_point)("exception", e)); throw; } if (non_fork_high_block_num < low_block_num) { wlog("Unable to generate a usable synopsis because the peer we're generating it for forked too long ago " "(our chains diverge after block #${non_fork_high_block_num} but only undoable to block #${low_block_num})", ("low_block_num", low_block_num) ("non_fork_high_block_num", non_fork_high_block_num)); FC_THROW_EXCEPTION(graphene::net::block_older_than_undo_history, "Peer is are on a fork I'm unable to switch to"); } } } else { // no reference point specified, summarize the whole block chain high_block_num = _chain_db->head_block_num(); non_fork_high_block_num = high_block_num; if (high_block_num == 0) return synopsis; // we have no blocks } // at this point: // low_block_num is the block before the first block we can undo, // non_fork_high_block_num is the block before the fork (if the peer is on a fork, or otherwise it is the same as high_block_num) // high_block_num is the block number of the reference block, or the end of the chain if no reference provided // true_high_block_num is the ending block number after the network code appends any item ids it // knows about that we don't uint32_t true_high_block_num = high_block_num + number_of_blocks_after_reference_point; do { // for each block in the synopsis, figure out where to pull the block id from. // if it's <= non_fork_high_block_num, we grab it from the main blockchain; // if it's not, we pull it from the fork history if (low_block_num <= non_fork_high_block_num) synopsis.push_back(_chain_db->get_block_id_for_num(low_block_num)); else synopsis.push_back(fork_history[low_block_num - non_fork_high_block_num - 1]); low_block_num += (true_high_block_num - low_block_num + 2) / 2; } while (low_block_num <= high_block_num); idump((synopsis)); return synopsis; } FC_CAPTURE_AND_RETHROW() }
int main( int argc, char** argv, char** envp ) { try { //steemit::chain::database db; steemit::chain::block_log log; fc::temp_directory temp_dir( "." ); //db.open( temp_dir ); log.open( temp_dir.path() / "log" ); idump( (log.head() ) ); steemit::protocol::signed_block b1; b1.witness = "alice"; b1.previous = steemit::protocol::block_id_type(); log.append( b1 ); log.flush(); idump( (b1) ); idump( ( log.head() ) ); idump( (fc::raw::pack_size(b1)) ); steemit::protocol::signed_block b2; b2.witness = "bob"; b2.previous = b1.id(); log.append( b2 ); log.flush(); idump( (b2) ); idump( (log.head() ) ); idump( (fc::raw::pack_size(b2)) ); auto r1 = log.read_block( 0 ); idump( (r1) ); idump( (fc::raw::pack_size(r1.first)) ); auto r2 = log.read_block( r1.second ); idump( (r2) ); idump( (fc::raw::pack_size(r2.first)) ); idump( (log.read_head()) ); idump( (fc::raw::pack_size(log.read_head()))); auto r3 = log.read_block( r2.second ); idump( (r3) ); } catch ( const std::exception& e ) { edump( ( std::string( e.what() ) ) ); } return 0; }