mirror of
				https://git.wownero.com/wownero/wownero.git
				synced 2024-08-15 01:03:23 +00:00 
			
		
		
		
	Merge pull request #6354
67ade8005 Add randomized delay when forwarding txes from i2p/tor -> ipv4/6 (Lee Clagett)
			
			
This commit is contained in:
		
						commit
						c108c5e2f0
					
				
					 9 changed files with 368 additions and 40 deletions
				
			
		| 
						 | 
				
			
			@ -63,6 +63,7 @@ bool matches_category(relay_method method, relay_category category) noexcept
 | 
			
		|||
  {
 | 
			
		||||
    default:
 | 
			
		||||
    case relay_method::local:
 | 
			
		||||
    case relay_method::forward:
 | 
			
		||||
    case relay_method::stem:
 | 
			
		||||
      return false;
 | 
			
		||||
    case relay_method::block:
 | 
			
		||||
| 
						 | 
				
			
			@ -79,6 +80,7 @@ void txpool_tx_meta_t::set_relay_method(relay_method method) noexcept
 | 
			
		|||
  kept_by_block = 0;
 | 
			
		||||
  do_not_relay = 0;
 | 
			
		||||
  is_local = 0;
 | 
			
		||||
  is_forwarding = 0;
 | 
			
		||||
  dandelionpp_stem = 0;
 | 
			
		||||
 | 
			
		||||
  switch (method)
 | 
			
		||||
| 
						 | 
				
			
			@ -89,8 +91,8 @@ void txpool_tx_meta_t::set_relay_method(relay_method method) noexcept
 | 
			
		|||
    case relay_method::local:
 | 
			
		||||
      is_local = 1;
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
    case relay_method::fluff:
 | 
			
		||||
    case relay_method::forward:
 | 
			
		||||
      is_forwarding = 1;
 | 
			
		||||
      break;
 | 
			
		||||
    case relay_method::stem:
 | 
			
		||||
      dandelionpp_stem = 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -98,26 +100,45 @@ void txpool_tx_meta_t::set_relay_method(relay_method method) noexcept
 | 
			
		|||
    case relay_method::block:
 | 
			
		||||
      kept_by_block = 1;
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
    case relay_method::fluff:
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
relay_method txpool_tx_meta_t::get_relay_method() const noexcept
 | 
			
		||||
{
 | 
			
		||||
  if (kept_by_block)
 | 
			
		||||
    return relay_method::block;
 | 
			
		||||
  if (do_not_relay)
 | 
			
		||||
    return relay_method::none;
 | 
			
		||||
  if (is_local)
 | 
			
		||||
    return relay_method::local;
 | 
			
		||||
  if (dandelionpp_stem)
 | 
			
		||||
    return relay_method::stem;
 | 
			
		||||
  const uint8_t state =
 | 
			
		||||
    uint8_t(kept_by_block) +
 | 
			
		||||
    (uint8_t(do_not_relay) << 1) +
 | 
			
		||||
    (uint8_t(is_local) << 2) +
 | 
			
		||||
    (uint8_t(is_forwarding) << 3) +
 | 
			
		||||
    (uint8_t(dandelionpp_stem) << 4);
 | 
			
		||||
 | 
			
		||||
  switch (state)
 | 
			
		||||
  {
 | 
			
		||||
    default: // error case
 | 
			
		||||
    case 0:
 | 
			
		||||
      break;
 | 
			
		||||
    case 1:
 | 
			
		||||
      return relay_method::block;
 | 
			
		||||
    case 2:
 | 
			
		||||
      return relay_method::none;
 | 
			
		||||
    case 4:
 | 
			
		||||
      return relay_method::local;
 | 
			
		||||
    case 8:
 | 
			
		||||
      return relay_method::forward;
 | 
			
		||||
    case 16:
 | 
			
		||||
      return relay_method::stem;
 | 
			
		||||
  };
 | 
			
		||||
  return relay_method::fluff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool txpool_tx_meta_t::upgrade_relay_method(relay_method method) noexcept
 | 
			
		||||
{
 | 
			
		||||
  static_assert(relay_method::none < relay_method::local, "bad relay_method value");
 | 
			
		||||
  static_assert(relay_method::local < relay_method::stem, "bad relay_method value");
 | 
			
		||||
  static_assert(relay_method::local < relay_method::forward, "bad relay_method value");
 | 
			
		||||
  static_assert(relay_method::forward < relay_method::stem, "bad relay_method value");
 | 
			
		||||
  static_assert(relay_method::stem < relay_method::fluff, "bad relay_method value");
 | 
			
		||||
  static_assert(relay_method::fluff < relay_method::block, "bad relay_method value");
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -160,7 +160,7 @@ struct txpool_tx_meta_t
 | 
			
		|||
  uint64_t max_used_block_height;
 | 
			
		||||
  uint64_t last_failed_height;
 | 
			
		||||
  uint64_t receive_time;
 | 
			
		||||
  uint64_t last_relayed_time; //!< If Dandelion++ stem, randomized embargo timestamp. Otherwise, last relayed timestmap.
 | 
			
		||||
  uint64_t last_relayed_time; //!< If received over i2p/tor, randomized forward time. If Dandelion++stem, randomized embargo time. Otherwise, last relayed timestamp
 | 
			
		||||
  // 112 bytes
 | 
			
		||||
  uint8_t kept_by_block;
 | 
			
		||||
  uint8_t relayed;
 | 
			
		||||
| 
						 | 
				
			
			@ -169,7 +169,8 @@ struct txpool_tx_meta_t
 | 
			
		|||
  uint8_t pruned: 1;
 | 
			
		||||
  uint8_t is_local: 1;
 | 
			
		||||
  uint8_t dandelionpp_stem : 1;
 | 
			
		||||
  uint8_t bf_padding: 4;
 | 
			
		||||
  uint8_t is_forwarding: 1;
 | 
			
		||||
  uint8_t bf_padding: 3;
 | 
			
		||||
 | 
			
		||||
  uint8_t padding[76]; // till 192 bytes
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -117,6 +117,11 @@
 | 
			
		|||
#define CRYPTONOTE_NOISE_BYTES                          3*1024 // 3 KiB
 | 
			
		||||
#define CRYPTONOTE_NOISE_CHANNELS                       2      // Max outgoing connections per zone used for noise/covert sending
 | 
			
		||||
 | 
			
		||||
// Both below are in seconds. The idea is to delay forwarding from i2p/tor
 | 
			
		||||
// to ipv4/6, such that 2+ incoming connections _could_ have sent the tx
 | 
			
		||||
#define CRYPTONOTE_FORWARD_DELAY_BASE (CRYPTONOTE_NOISE_MIN_DELAY + CRYPTONOTE_NOISE_DELAY_RANGE)
 | 
			
		||||
#define CRYPTONOTE_FORWARD_DELAY_AVERAGE (CRYPTONOTE_FORWARD_DELAY_BASE + (CRYPTONOTE_FORWARD_DELAY_BASE / 2))
 | 
			
		||||
 | 
			
		||||
#define CRYPTONOTE_MAX_FRAGMENTS                        20 // ~20 * NOISE_BYTES max payload size for covert/noise send
 | 
			
		||||
 | 
			
		||||
#define COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT           1000
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1303,6 +1303,7 @@ namespace cryptonote
 | 
			
		|||
    {
 | 
			
		||||
      NOTIFY_NEW_TRANSACTIONS::request public_req{};
 | 
			
		||||
      NOTIFY_NEW_TRANSACTIONS::request private_req{};
 | 
			
		||||
      NOTIFY_NEW_TRANSACTIONS::request stem_req{};
 | 
			
		||||
      for (auto& tx : txs)
 | 
			
		||||
      {
 | 
			
		||||
        switch (std::get<2>(tx))
 | 
			
		||||
| 
						 | 
				
			
			@ -1313,6 +1314,9 @@ namespace cryptonote
 | 
			
		|||
          case relay_method::local:
 | 
			
		||||
            private_req.txs.push_back(std::move(std::get<1>(tx)));
 | 
			
		||||
            break;
 | 
			
		||||
          case relay_method::forward:
 | 
			
		||||
            stem_req.txs.push_back(std::move(std::get<1>(tx)));
 | 
			
		||||
            break;
 | 
			
		||||
          case relay_method::block:
 | 
			
		||||
          case relay_method::fluff:
 | 
			
		||||
          case relay_method::stem:
 | 
			
		||||
| 
						 | 
				
			
			@ -1330,6 +1334,8 @@ namespace cryptonote
 | 
			
		|||
        get_protocol()->relay_transactions(public_req, source, epee::net_utils::zone::public_, relay_method::fluff);
 | 
			
		||||
      if (!private_req.txs.empty())
 | 
			
		||||
        get_protocol()->relay_transactions(private_req, source, epee::net_utils::zone::invalid, relay_method::local);
 | 
			
		||||
      if (!stem_req.txs.empty())
 | 
			
		||||
        get_protocol()->relay_transactions(stem_req, source, epee::net_utils::zone::public_, relay_method::stem);
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -91,6 +91,8 @@ namespace cryptonote
 | 
			
		|||
    time_t const MAX_RELAY_TIME = (60 * 60 * 4); // at most that many seconds between resends
 | 
			
		||||
    float const ACCEPT_THRESHOLD = 1.0f;
 | 
			
		||||
 | 
			
		||||
    constexpr const std::chrono::seconds forward_delay_average{CRYPTONOTE_FORWARD_DELAY_AVERAGE};
 | 
			
		||||
 | 
			
		||||
    // a kind of increasing backoff within min/max bounds
 | 
			
		||||
    uint64_t get_relay_delay(time_t now, time_t received)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -309,8 +311,14 @@ namespace cryptonote
 | 
			
		|||
 | 
			
		||||
        if (meta.upgrade_relay_method(tx_relay) || !existing_tx) // synchronize with embargo timer or stem/fluff out-of-order messages
 | 
			
		||||
        {
 | 
			
		||||
          using clock = std::chrono::system_clock;
 | 
			
		||||
          auto last_relayed_time = std::numeric_limits<decltype(meta.last_relayed_time)>::max();
 | 
			
		||||
          if (tx_relay == relay_method::forward)
 | 
			
		||||
            last_relayed_time = clock::to_time_t(clock::now() + crypto::random_poisson_seconds{forward_delay_average}());
 | 
			
		||||
          // else the `set_relayed` function will adjust the time accordingly later
 | 
			
		||||
 | 
			
		||||
          //update transactions container
 | 
			
		||||
          meta.last_relayed_time = std::numeric_limits<decltype(meta.last_relayed_time)>::max();
 | 
			
		||||
          meta.last_relayed_time = last_relayed_time;
 | 
			
		||||
          meta.receive_time = receive_time;
 | 
			
		||||
          meta.weight = tx_weight;
 | 
			
		||||
          meta.fee = fee;
 | 
			
		||||
| 
						 | 
				
			
			@ -341,7 +349,7 @@ namespace cryptonote
 | 
			
		|||
      tvc.m_added_to_pool = true;
 | 
			
		||||
 | 
			
		||||
      static_assert(unsigned(relay_method::none) == 0, "expected relay_method::none value to be zero");
 | 
			
		||||
      if(meta.fee > 0)
 | 
			
		||||
      if(meta.fee > 0 && tx_relay != relay_method::forward)
 | 
			
		||||
        tvc.m_relay = tx_relay;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -722,28 +730,46 @@ namespace cryptonote
 | 
			
		|||
  //TODO: investigate whether boolean return is appropriate
 | 
			
		||||
  bool tx_memory_pool::get_relayable_transactions(std::vector<std::tuple<crypto::hash, cryptonote::blobdata, relay_method>> &txs) const
 | 
			
		||||
  {
 | 
			
		||||
    std::vector<std::pair<crypto::hash, txpool_tx_meta_t>> change_timestamps;
 | 
			
		||||
    const uint64_t now = time(NULL);
 | 
			
		||||
 | 
			
		||||
    CRITICAL_REGION_LOCAL(m_transactions_lock);
 | 
			
		||||
    CRITICAL_REGION_LOCAL1(m_blockchain);
 | 
			
		||||
    const uint64_t now = time(NULL);
 | 
			
		||||
    LockedTXN lock(m_blockchain.get_db());
 | 
			
		||||
    txs.reserve(m_blockchain.get_txpool_tx_count());
 | 
			
		||||
    m_blockchain.for_all_txpool_txes([this, now, &txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *){
 | 
			
		||||
    m_blockchain.for_all_txpool_txes([this, now, &txs, &change_timestamps](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *){
 | 
			
		||||
      // 0 fee transactions are never relayed
 | 
			
		||||
      if(!meta.pruned && meta.fee > 0 && !meta.do_not_relay)
 | 
			
		||||
      {
 | 
			
		||||
        if (!meta.dandelionpp_stem && now - meta.last_relayed_time <= get_relay_delay(now, meta.receive_time))
 | 
			
		||||
          return true;
 | 
			
		||||
        if (meta.dandelionpp_stem && meta.last_relayed_time < now) // for dandelion++ stem, this value is the embargo timeout
 | 
			
		||||
          return true;
 | 
			
		||||
        const relay_method tx_relay = meta.get_relay_method();
 | 
			
		||||
        switch (tx_relay)
 | 
			
		||||
        {
 | 
			
		||||
          case relay_method::stem:
 | 
			
		||||
          case relay_method::forward:
 | 
			
		||||
            if (meta.last_relayed_time > now)
 | 
			
		||||
              return true; // continue to next tx
 | 
			
		||||
            change_timestamps.emplace_back(txid, meta);
 | 
			
		||||
            break;
 | 
			
		||||
          default:
 | 
			
		||||
          case relay_method::none:
 | 
			
		||||
            return true;
 | 
			
		||||
          case relay_method::local:
 | 
			
		||||
          case relay_method::fluff:
 | 
			
		||||
          case relay_method::block:
 | 
			
		||||
            if (now - meta.last_relayed_time <= get_relay_delay(now, meta.receive_time))
 | 
			
		||||
              return true; // continue to next tx
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // if the tx is older than half the max lifetime, we don't re-relay it, to avoid a problem
 | 
			
		||||
        // mentioned by smooth where nodes would flush txes at slightly different times, causing
 | 
			
		||||
        // flushed txes to be re-added when received from a node which was just about to flush it
 | 
			
		||||
        uint64_t max_age = meta.kept_by_block ? CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME : CRYPTONOTE_MEMPOOL_TX_LIVETIME;
 | 
			
		||||
        uint64_t max_age = (tx_relay == relay_method::block) ? CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME : CRYPTONOTE_MEMPOOL_TX_LIVETIME;
 | 
			
		||||
        if (now - meta.receive_time <= max_age / 2)
 | 
			
		||||
        {
 | 
			
		||||
          try
 | 
			
		||||
          {
 | 
			
		||||
            txs.emplace_back(txid, m_blockchain.get_txpool_tx_blob(txid, relay_category::all), meta.get_relay_method());
 | 
			
		||||
            txs.emplace_back(txid, m_blockchain.get_txpool_tx_blob(txid, relay_category::all), tx_relay);
 | 
			
		||||
          }
 | 
			
		||||
          catch (const std::exception &e)
 | 
			
		||||
          {
 | 
			
		||||
| 
						 | 
				
			
			@ -754,6 +780,18 @@ namespace cryptonote
 | 
			
		|||
      }
 | 
			
		||||
      return true;
 | 
			
		||||
    }, false, relay_category::relayable);
 | 
			
		||||
 | 
			
		||||
    for (auto& elem : change_timestamps)
 | 
			
		||||
    {
 | 
			
		||||
      /* These transactions are still in forward or stem state, so the field
 | 
			
		||||
         represents the next time a relay should be attempted. Will be
 | 
			
		||||
         overwritten when the state is upgraded to stem, fluff or block. This
 | 
			
		||||
         function is only called every ~2 minutes, so this resetting should be
 | 
			
		||||
         unnecessary, but is primarily a precaution against potential changes
 | 
			
		||||
	 to the callback routines. */
 | 
			
		||||
      elem.second.last_relayed_time = now + get_relay_delay(now, elem.second.receive_time);
 | 
			
		||||
      m_blockchain.update_txpool_tx(elem.first, elem.second);
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  //---------------------------------------------------------------------------------
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -935,7 +935,19 @@ namespace cryptonote
 | 
			
		|||
      return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    relay_method tx_relay;
 | 
			
		||||
    /* If the txes were received over i2p/tor, the default is to "forward"
 | 
			
		||||
       with a randomized delay to further enhance the "white noise" behavior,
 | 
			
		||||
       potentially making it harder for ISP-level spies to determine which
 | 
			
		||||
       inbound link sent the tx. If the sender disabled "white noise" over
 | 
			
		||||
       i2p/tor, then the sender is "fluffing" (to only outbound) i2p/tor
 | 
			
		||||
       connections with the `dandelionpp_fluff` flag set. The receiver (hidden
 | 
			
		||||
       service) will immediately fluff in that scenario (i.e. this assumes that a
 | 
			
		||||
       sybil spy will be unable to link an IP to an i2p/tor connection). */
 | 
			
		||||
 | 
			
		||||
    const epee::net_utils::zone zone = context.m_remote_address.get_zone();
 | 
			
		||||
    relay_method tx_relay = zone == epee::net_utils::zone::public_ ?
 | 
			
		||||
      relay_method::stem : relay_method::forward;
 | 
			
		||||
 | 
			
		||||
    std::vector<blobdata> stem_txs{};
 | 
			
		||||
    std::vector<blobdata> fluff_txs{};
 | 
			
		||||
    if (arg.dandelionpp_fluff)
 | 
			
		||||
| 
						 | 
				
			
			@ -944,10 +956,7 @@ namespace cryptonote
 | 
			
		|||
      fluff_txs.reserve(arg.txs.size());
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
      tx_relay = relay_method::stem;
 | 
			
		||||
      stem_txs.reserve(arg.txs.size());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (auto& tx : arg.txs)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -970,6 +979,7 @@ namespace cryptonote
 | 
			
		|||
          fluff_txs.push_back(std::move(tx));
 | 
			
		||||
          break;
 | 
			
		||||
        default:
 | 
			
		||||
        case relay_method::forward: // not supposed to happen here
 | 
			
		||||
        case relay_method::none:
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,6 +37,7 @@ namespace cryptonote
 | 
			
		|||
  {
 | 
			
		||||
    none = 0, //!< Received via RPC with `do_not_relay` set
 | 
			
		||||
    local,    //!< Received via RPC; trying to send over i2p/tor, etc.
 | 
			
		||||
    forward,  //!< Received over i2p/tor; timer delayed before ipv4/6 public broadcast
 | 
			
		||||
    stem,     //!< Received/send over network using Dandelion++ stem
 | 
			
		||||
    fluff,    //!< Received/sent over network using Dandelion++ fluff
 | 
			
		||||
    block     //!< Received in block, takes precedence over others
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -357,11 +357,15 @@ namespace levin
 | 
			
		|||
          return true;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Always send txs in stem mode over i2p/tor, see comments in `send_txs` below.
 | 
			
		||||
        /* Always send with `fluff` flag, even over i2p/tor. The hidden service
 | 
			
		||||
	   will disable the forwarding delay and immediately fluff. The i2p/tor
 | 
			
		||||
	   network is therefore replacing the sybil protection of Dandelion++.
 | 
			
		||||
	   Dandelion++ stem phase over i2p/tor is also worth investigating
 | 
			
		||||
	   (with/without "noise"?). */
 | 
			
		||||
        for (auto& connection : connections)
 | 
			
		||||
        {
 | 
			
		||||
          std::sort(connection.first.begin(), connection.first.end()); // don't leak receive order
 | 
			
		||||
          make_payload_send_txs(*zone_->p2p, std::move(connection.first), connection.second, zone_->pad_txs, zone_->is_public);
 | 
			
		||||
          make_payload_send_txs(*zone_->p2p, std::move(connection.first), connection.second, zone_->pad_txs, true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (next_flush != std::chrono::steady_clock::time_point::max())
 | 
			
		||||
| 
						 | 
				
			
			@ -811,12 +815,11 @@ namespace levin
 | 
			
		|||
        case relay_method::block:
 | 
			
		||||
          return false;
 | 
			
		||||
        case relay_method::stem:
 | 
			
		||||
	  tx_relay = relay_method::fluff; // don't set stempool embargo when skipping to fluff
 | 
			
		||||
	  /* fallthrough */
 | 
			
		||||
        case relay_method::forward:
 | 
			
		||||
        case relay_method::local:
 | 
			
		||||
          if (zone_->is_public)
 | 
			
		||||
          {
 | 
			
		||||
	    // this will change a local tx to stem or fluff ...
 | 
			
		||||
            // this will change a local/forward tx to stem or fluff ...
 | 
			
		||||
            zone_->strand.dispatch(
 | 
			
		||||
              dandelionpp_notify{zone_, std::addressof(core), std::move(txs), source}
 | 
			
		||||
            );
 | 
			
		||||
| 
						 | 
				
			
			@ -824,6 +827,11 @@ namespace levin
 | 
			
		|||
          }
 | 
			
		||||
          /* fallthrough */
 | 
			
		||||
        case relay_method::fluff:
 | 
			
		||||
          /* If sending stem/forward/local txes over non public networks,
 | 
			
		||||
             continue to claim that relay mode even though it used the "fluff"
 | 
			
		||||
             routine. A "fluff" over i2p/tor is not the same as a "fluff" over
 | 
			
		||||
             ipv4/6. Marking it as "fluff" here will make the tx immediately
 | 
			
		||||
             visible externally from this node, which is not desired. */
 | 
			
		||||
          core.on_transactions_relayed(epee::to_span(txs), tx_relay);
 | 
			
		||||
          zone_->strand.dispatch(fluff_notify{zone_, std::move(txs), source});
 | 
			
		||||
          break;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -680,6 +680,76 @@ TEST_F(levin_notify, local_without_padding)
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(levin_notify, forward_without_padding)
 | 
			
		||||
{
 | 
			
		||||
    cryptonote::levin::notify notifier = make_notifier(0, true, false);
 | 
			
		||||
 | 
			
		||||
    for (unsigned count = 0; count < 10; ++count)
 | 
			
		||||
        add_connection(count % 2 == 0);
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        const auto status = notifier.get_status();
 | 
			
		||||
        EXPECT_FALSE(status.has_noise);
 | 
			
		||||
        EXPECT_FALSE(status.connections_filled);
 | 
			
		||||
    }
 | 
			
		||||
    notifier.new_out_connection();
 | 
			
		||||
    io_service_.poll();
 | 
			
		||||
 | 
			
		||||
    std::vector<cryptonote::blobdata> txs(2);
 | 
			
		||||
    txs[0].resize(100, 'f');
 | 
			
		||||
    txs[1].resize(200, 'e');
 | 
			
		||||
 | 
			
		||||
    std::vector<cryptonote::blobdata> sorted_txs = txs;
 | 
			
		||||
    std::sort(sorted_txs.begin(), sorted_txs.end());
 | 
			
		||||
 | 
			
		||||
    ASSERT_EQ(10u, contexts_.size());
 | 
			
		||||
    bool has_stemmed = false;
 | 
			
		||||
    bool has_fluffed = false;
 | 
			
		||||
    while (!has_stemmed || !has_fluffed)
 | 
			
		||||
    {
 | 
			
		||||
        auto context = contexts_.begin();
 | 
			
		||||
        EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::forward));
 | 
			
		||||
 | 
			
		||||
        io_service_.reset();
 | 
			
		||||
        ASSERT_LT(0u, io_service_.poll());
 | 
			
		||||
        const bool is_stem = events_.has_stem_txes();
 | 
			
		||||
        EXPECT_EQ(txs, events_.take_relayed(is_stem ? cryptonote::relay_method::stem : cryptonote::relay_method::fluff));
 | 
			
		||||
 | 
			
		||||
        if (!is_stem)
 | 
			
		||||
        {
 | 
			
		||||
            notifier.run_fluff();
 | 
			
		||||
            ASSERT_LT(0u, io_service_.poll());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::size_t send_count = 0;
 | 
			
		||||
        EXPECT_EQ(0u, context->process_send_queue());
 | 
			
		||||
        for (++context; context != contexts_.end(); ++context)
 | 
			
		||||
        {
 | 
			
		||||
            const std::size_t sent = context->process_send_queue();
 | 
			
		||||
            if (sent && is_stem)
 | 
			
		||||
                EXPECT_EQ(1u, (context - contexts_.begin()) % 2);
 | 
			
		||||
            send_count += sent;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        EXPECT_EQ(is_stem ? 1u : 9u, send_count);
 | 
			
		||||
        ASSERT_EQ(is_stem ? 1u : 9u, receiver_.notified_size());
 | 
			
		||||
        for (unsigned count = 0; count < (is_stem ? 1u : 9u); ++count)
 | 
			
		||||
        {
 | 
			
		||||
            auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
 | 
			
		||||
	    if (is_stem)
 | 
			
		||||
	      EXPECT_EQ(txs, notification.txs);
 | 
			
		||||
	    else
 | 
			
		||||
	      EXPECT_EQ(sorted_txs, notification.txs);
 | 
			
		||||
            EXPECT_TRUE(notification._.empty());
 | 
			
		||||
            EXPECT_EQ(!is_stem, notification.dandelionpp_fluff);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        has_stemmed |= is_stem;
 | 
			
		||||
        has_fluffed |= !is_stem;
 | 
			
		||||
        notifier.run_epoch();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(levin_notify, block_without_padding)
 | 
			
		||||
{
 | 
			
		||||
    cryptonote::levin::notify notifier = make_notifier(0, true, false);
 | 
			
		||||
| 
						 | 
				
			
			@ -918,6 +988,73 @@ TEST_F(levin_notify, local_with_padding)
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(levin_notify, forward_with_padding)
 | 
			
		||||
{
 | 
			
		||||
    cryptonote::levin::notify notifier = make_notifier(0, true, true);
 | 
			
		||||
 | 
			
		||||
    for (unsigned count = 0; count < 10; ++count)
 | 
			
		||||
        add_connection(count % 2 == 0);
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        const auto status = notifier.get_status();
 | 
			
		||||
        EXPECT_FALSE(status.has_noise);
 | 
			
		||||
        EXPECT_FALSE(status.connections_filled);
 | 
			
		||||
    }
 | 
			
		||||
    notifier.new_out_connection();
 | 
			
		||||
    io_service_.poll();
 | 
			
		||||
 | 
			
		||||
    std::vector<cryptonote::blobdata> txs(2);
 | 
			
		||||
    txs[0].resize(100, 'e');
 | 
			
		||||
    txs[1].resize(200, 'f');
 | 
			
		||||
 | 
			
		||||
    ASSERT_EQ(10u, contexts_.size());
 | 
			
		||||
    bool has_stemmed = false;
 | 
			
		||||
    bool has_fluffed = false;
 | 
			
		||||
    while (!has_stemmed || !has_fluffed)
 | 
			
		||||
    {
 | 
			
		||||
        auto context = contexts_.begin();
 | 
			
		||||
        EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::forward));
 | 
			
		||||
 | 
			
		||||
        io_service_.reset();
 | 
			
		||||
        ASSERT_LT(0u, io_service_.poll());
 | 
			
		||||
        const bool is_stem = events_.has_stem_txes();
 | 
			
		||||
        EXPECT_EQ(txs, events_.take_relayed(is_stem ? cryptonote::relay_method::stem : cryptonote::relay_method::fluff));
 | 
			
		||||
 | 
			
		||||
        if (!is_stem)
 | 
			
		||||
        {
 | 
			
		||||
            notifier.run_fluff();
 | 
			
		||||
            ASSERT_LT(0u, io_service_.poll());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::size_t send_count = 0;
 | 
			
		||||
        EXPECT_EQ(0u, context->process_send_queue());
 | 
			
		||||
        for (++context; context != contexts_.end(); ++context)
 | 
			
		||||
        {
 | 
			
		||||
            const std::size_t sent = context->process_send_queue();
 | 
			
		||||
            if (sent && is_stem)
 | 
			
		||||
            {
 | 
			
		||||
                EXPECT_EQ(1u, (context - contexts_.begin()) % 2);
 | 
			
		||||
                EXPECT_FALSE(context->is_incoming());
 | 
			
		||||
            }
 | 
			
		||||
            send_count += sent;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        EXPECT_EQ(is_stem ? 1u : 9u, send_count);
 | 
			
		||||
        ASSERT_EQ(is_stem ? 1u : 9u, receiver_.notified_size());
 | 
			
		||||
        for (unsigned count = 0; count < (is_stem ? 1u : 9u); ++count)
 | 
			
		||||
        {
 | 
			
		||||
            auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
 | 
			
		||||
            EXPECT_EQ(txs, notification.txs);
 | 
			
		||||
            EXPECT_FALSE(notification._.empty());
 | 
			
		||||
            EXPECT_EQ(!is_stem, notification.dandelionpp_fluff);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        has_stemmed |= is_stem;
 | 
			
		||||
        has_fluffed |= !is_stem;
 | 
			
		||||
        notifier.run_epoch();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(levin_notify, block_with_padding)
 | 
			
		||||
{
 | 
			
		||||
    cryptonote::levin::notify notifier = make_notifier(0, true, true);
 | 
			
		||||
| 
						 | 
				
			
			@ -1021,7 +1158,7 @@ TEST_F(levin_notify, private_fluff_without_padding)
 | 
			
		|||
            auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
 | 
			
		||||
            EXPECT_EQ(txs, notification.txs);
 | 
			
		||||
            EXPECT_TRUE(notification._.empty());
 | 
			
		||||
            EXPECT_FALSE(notification.dandelionpp_fluff);
 | 
			
		||||
            EXPECT_TRUE(notification.dandelionpp_fluff);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1057,7 +1194,7 @@ TEST_F(levin_notify, private_stem_without_padding)
 | 
			
		|||
        io_service_.reset();
 | 
			
		||||
        ASSERT_LT(0u, io_service_.poll());
 | 
			
		||||
 | 
			
		||||
        EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::fluff));
 | 
			
		||||
        EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::stem));
 | 
			
		||||
 | 
			
		||||
        EXPECT_EQ(0u, context->process_send_queue());
 | 
			
		||||
        for (++context; context != contexts_.end(); ++context)
 | 
			
		||||
| 
						 | 
				
			
			@ -1072,7 +1209,7 @@ TEST_F(levin_notify, private_stem_without_padding)
 | 
			
		|||
            auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
 | 
			
		||||
            EXPECT_EQ(txs, notification.txs);
 | 
			
		||||
            EXPECT_TRUE(notification._.empty());
 | 
			
		||||
            EXPECT_FALSE(notification.dandelionpp_fluff);
 | 
			
		||||
            EXPECT_TRUE(notification.dandelionpp_fluff);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1123,7 +1260,58 @@ TEST_F(levin_notify, private_local_without_padding)
 | 
			
		|||
            auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
 | 
			
		||||
            EXPECT_EQ(txs, notification.txs);
 | 
			
		||||
            EXPECT_TRUE(notification._.empty());
 | 
			
		||||
            EXPECT_FALSE(notification.dandelionpp_fluff);
 | 
			
		||||
            EXPECT_TRUE(notification.dandelionpp_fluff);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(levin_notify, private_forward_without_padding)
 | 
			
		||||
{
 | 
			
		||||
    // private mode always uses fluff but marked as stem
 | 
			
		||||
    cryptonote::levin::notify notifier = make_notifier(0, false, false);
 | 
			
		||||
 | 
			
		||||
    for (unsigned count = 0; count < 10; ++count)
 | 
			
		||||
        add_connection(count % 2 == 0);
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        const auto status = notifier.get_status();
 | 
			
		||||
        EXPECT_FALSE(status.has_noise);
 | 
			
		||||
        EXPECT_FALSE(status.connections_filled);
 | 
			
		||||
    }
 | 
			
		||||
    notifier.new_out_connection();
 | 
			
		||||
    io_service_.poll();
 | 
			
		||||
 | 
			
		||||
    std::vector<cryptonote::blobdata> txs(2);
 | 
			
		||||
    txs[0].resize(100, 'e');
 | 
			
		||||
    txs[1].resize(200, 'f');
 | 
			
		||||
 | 
			
		||||
    ASSERT_EQ(10u, contexts_.size());
 | 
			
		||||
    {
 | 
			
		||||
        auto context = contexts_.begin();
 | 
			
		||||
        EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::forward));
 | 
			
		||||
 | 
			
		||||
        io_service_.reset();
 | 
			
		||||
        ASSERT_LT(0u, io_service_.poll());
 | 
			
		||||
        notifier.run_fluff();
 | 
			
		||||
        io_service_.reset();
 | 
			
		||||
        ASSERT_LT(0u, io_service_.poll());
 | 
			
		||||
 | 
			
		||||
        EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::forward));
 | 
			
		||||
 | 
			
		||||
        EXPECT_EQ(0u, context->process_send_queue());
 | 
			
		||||
        for (++context; context != contexts_.end(); ++context)
 | 
			
		||||
        {
 | 
			
		||||
            const bool is_incoming = ((context - contexts_.begin()) % 2 == 0);
 | 
			
		||||
            EXPECT_EQ(is_incoming ? 0u : 1u, context->process_send_queue());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ASSERT_EQ(5u, receiver_.notified_size());
 | 
			
		||||
        for (unsigned count = 0; count < 5; ++count)
 | 
			
		||||
        {
 | 
			
		||||
            auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
 | 
			
		||||
            EXPECT_EQ(txs, notification.txs);
 | 
			
		||||
            EXPECT_TRUE(notification._.empty());
 | 
			
		||||
            EXPECT_TRUE(notification.dandelionpp_fluff);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1233,7 +1421,7 @@ TEST_F(levin_notify, private_fluff_with_padding)
 | 
			
		|||
            auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
 | 
			
		||||
            EXPECT_EQ(txs, notification.txs);
 | 
			
		||||
            EXPECT_FALSE(notification._.empty());
 | 
			
		||||
            EXPECT_FALSE(notification.dandelionpp_fluff);
 | 
			
		||||
            EXPECT_TRUE(notification.dandelionpp_fluff);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1268,7 +1456,7 @@ TEST_F(levin_notify, private_stem_with_padding)
 | 
			
		|||
        io_service_.reset();
 | 
			
		||||
        ASSERT_LT(0u, io_service_.poll());
 | 
			
		||||
 | 
			
		||||
        EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::fluff));
 | 
			
		||||
        EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::stem));
 | 
			
		||||
 | 
			
		||||
        EXPECT_EQ(0u, context->process_send_queue());
 | 
			
		||||
        for (++context; context != contexts_.end(); ++context)
 | 
			
		||||
| 
						 | 
				
			
			@ -1283,7 +1471,7 @@ TEST_F(levin_notify, private_stem_with_padding)
 | 
			
		|||
            auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
 | 
			
		||||
            EXPECT_EQ(txs, notification.txs);
 | 
			
		||||
            EXPECT_FALSE(notification._.empty());
 | 
			
		||||
            EXPECT_FALSE(notification.dandelionpp_fluff);
 | 
			
		||||
            EXPECT_TRUE(notification.dandelionpp_fluff);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1333,7 +1521,57 @@ TEST_F(levin_notify, private_local_with_padding)
 | 
			
		|||
            auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
 | 
			
		||||
            EXPECT_EQ(txs, notification.txs);
 | 
			
		||||
            EXPECT_FALSE(notification._.empty());
 | 
			
		||||
            EXPECT_FALSE(notification.dandelionpp_fluff);
 | 
			
		||||
            EXPECT_TRUE(notification.dandelionpp_fluff);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(levin_notify, private_forward_with_padding)
 | 
			
		||||
{
 | 
			
		||||
    cryptonote::levin::notify notifier = make_notifier(0, false, true);
 | 
			
		||||
 | 
			
		||||
    for (unsigned count = 0; count < 10; ++count)
 | 
			
		||||
        add_connection(count % 2 == 0);
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        const auto status = notifier.get_status();
 | 
			
		||||
        EXPECT_FALSE(status.has_noise);
 | 
			
		||||
        EXPECT_FALSE(status.connections_filled);
 | 
			
		||||
    }
 | 
			
		||||
    notifier.new_out_connection();
 | 
			
		||||
    io_service_.poll();
 | 
			
		||||
 | 
			
		||||
    std::vector<cryptonote::blobdata> txs(2);
 | 
			
		||||
    txs[0].resize(100, 'e');
 | 
			
		||||
    txs[1].resize(200, 'f');
 | 
			
		||||
 | 
			
		||||
    ASSERT_EQ(10u, contexts_.size());
 | 
			
		||||
    {
 | 
			
		||||
        auto context = contexts_.begin();
 | 
			
		||||
        EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::forward));
 | 
			
		||||
 | 
			
		||||
        io_service_.reset();
 | 
			
		||||
        ASSERT_LT(0u, io_service_.poll());
 | 
			
		||||
        notifier.run_fluff();
 | 
			
		||||
        io_service_.reset();
 | 
			
		||||
        ASSERT_LT(0u, io_service_.poll());
 | 
			
		||||
 | 
			
		||||
        EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::forward));
 | 
			
		||||
 | 
			
		||||
        EXPECT_EQ(0u, context->process_send_queue());
 | 
			
		||||
        for (++context; context != contexts_.end(); ++context)
 | 
			
		||||
        {
 | 
			
		||||
            const bool is_incoming = ((context - contexts_.begin()) % 2 == 0);
 | 
			
		||||
            EXPECT_EQ(is_incoming ? 0u : 1u, context->process_send_queue());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ASSERT_EQ(5u, receiver_.notified_size());
 | 
			
		||||
        for (unsigned count = 0; count < 5; ++count)
 | 
			
		||||
        {
 | 
			
		||||
            auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
 | 
			
		||||
            EXPECT_EQ(txs, notification.txs);
 | 
			
		||||
            EXPECT_FALSE(notification._.empty());
 | 
			
		||||
            EXPECT_TRUE(notification.dandelionpp_fluff);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue