Compare commits

...

16 Commits

Author SHA1 Message Date
luigi1111 17ec003c06 Merge pull request #6376
6177e49 Added logging for dropped local txes with no i2p/tor connections (vtnerd)
2020-03-09 13:34:24 -04:00
luigi1111 7ca6b550ae Merge pull request #6374
d2c1cb7 depends: set several missing build tags (moneromooo-monero)
2020-03-09 13:33:35 -04:00
luigi1111 137a11b39c Merge pull request #6373
62ab7fd p2p: plug tor to clearnet association vector (moneromooo-monero)
2020-03-09 13:32:22 -04:00
luigi1111 7416bb7fc1 Merge pull request #6368
ac1a34c build: prepare v0.15.0.5 update (selsta)
2020-03-09 13:30:51 -04:00
luigi1111 de0d9be0ec Merge pull request #6367
f427933 depends: update libsodium to 1.0.18 (selsta)
2020-03-09 13:29:41 -04:00
luigi1111 7352e8f616 Merge pull request #6365
d438e6a wallet: fix exceptions getting the hash of a pruned tx (moneromooo-monero)
2020-03-09 13:28:40 -04:00
luigi1111 a4ec085dd4 Merge pull request #6362
535286a Fixed bug in ZMQ JSON-RPC method field (vtnerd)
2020-03-09 13:27:36 -04:00
luigi1111 26107dc4cd Merge pull request #6360
533d85d Fixed string_ref usage bug in epee::from_hex::vector (vtnerd)
2020-03-09 13:26:26 -04:00
moneromooo-monero d2c1cb72e3 depends: set several missing build tags 2020-03-07 22:00:13 +00:00
Aaron Hook 62ab7fdf04 p2p: plug tor to clearnet association vector
During the handshake for an incoming connection, the peer id is checked against the local node's peer id only for the specific zone of the incoming peer, in order to avoid linking public addresses to tor addresses:
https://github.com/monero-project/monero/blob/5d7ae2d2791c0244a221872a7ac62627abb81896/src/p2p/net_node.inl#L2343

However, on handshakes for outgoing connections, all zones are checked:
https://github.com/monero-project/monero/blob/5d7ae2d2791c0244a221872a7ac62627abb81896/src/p2p/net_node.inl#L1064

If an attacker wanted to link a specific tor node to a public node, they could potentially connect to as many public nodes as possible, get themselves added to the peer whitelist, maybe stuff some more attacker-owned addresses into the greylist, then disconnect, and for any future incoming connections, respond with the tor node's id in an attempt to link the public/tor addresses.
2020-03-07 18:14:06 +00:00
Lee Clagett 6177e4910d Added logging for dropped local txes with no i2p/tor connections 2020-03-07 00:02:31 +00:00
selsta ac1a34c5d0 build: prepare v0.15.0.5 update 2020-03-03 23:17:18 +01:00
TheCharlatan f427933029 depends: update libsodium to 1.0.18 2020-03-02 23:45:22 +01:00
moneromooo-monero d438e6a28c wallet: fix exceptions getting the hash of a pruned tx 2020-03-01 22:44:59 +00:00
Lee Clagett 533d85d44b Fixed string_ref usage bug in epee::from_hex::vector 2020-02-28 17:42:57 -05:00
Lee Clagett 535286a28b Fixed bug in ZMQ JSON-RPC method field 2019-11-18 13:00:25 +00:00
20 changed files with 136 additions and 46 deletions
+4 -4
View File
@@ -153,7 +153,7 @@ Dates are provided in the format YYYY-MM-DD.
| 1686275 | 2018-10-19 | v9 | v0.13.0.0 | v0.13.0.4 | bulletproofs required
| 1788000 | 2019-03-09 | v10 | v0.14.0.0 | v0.14.1.2 | New PoW based on Cryptonight-R, new block weight algorithm, slightly more efficient RingCT format
| 1788720 | 2019-03-10 | v11 | v0.14.0.0 | v0.14.1.2 | forbid old RingCT transaction format
| 1978433 | 2019-11-30* | v12 | v0.15.0.0 | v0.15.0.1 | New PoW based on RandomX, only allow >= 2 outputs, change to the block median used to calculate penalty, v1 coinbases are forbidden, rct sigs in coinbase forbidden, 10 block lock time for incoming transactions
| 1978433 | 2019-11-30* | v12 | v0.15.0.0 | v0.15.0.5 | New PoW based on RandomX, only allow >= 2 outputs, change to the block median used to calculate penalty, v1 coinbases are forbidden, rct sigs in coinbase forbidden, 10 block lock time for incoming transactions
| XXXXXXX | XXX-XX-XX | XXX | vX.XX.X.X | vX.XX.X.X | XXX |
X's indicate that these details have not been determined as of commit date.
@@ -315,7 +315,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch (
```bash
git clone https://github.com/monero-project/monero.git
cd monero
git checkout tags/v0.15.0.1
git checkout tags/v0.15.0.5
```
* Build:
@@ -432,10 +432,10 @@ application.
cd monero
```
* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.15.0.1'. If you don't care about the version and just want binaries from master, skip this step:
* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.15.0.5'. If you don't care about the version and just want binaries from master, skip this step:
```bash
git checkout v0.15.0.1
git checkout v0.15.0.5
```
* If you are on a 64-bit system, run:
+2 -2
View File
@@ -1,8 +1,8 @@
package=sodium
$(package)_version=1.0.16
$(package)_version=1.0.18
$(package)_download_path=https://download.libsodium.org/libsodium/releases/
$(package)_file_name=libsodium-$($(package)_version).tar.gz
$(package)_sha256_hash=eeadc7e1e1bcef09680fb4837d448fbdf57224978f865ac1c16745868fbd0533
$(package)_sha256_hash=6f504490b342a4f8a4c4a02fc9b866cbef8622d5df4e5452b46be121e46636c1
$(package)_patches=fix-whitespace.patch
define $(package)_set_vars
@@ -5,8 +5,8 @@ index b29f769..ca008ae 100755
@@ -591,7 +591,7 @@ MAKEFLAGS=
PACKAGE_NAME='libsodium'
PACKAGE_TARNAME='libsodium'
PACKAGE_VERSION='1.0.16'
-PACKAGE_STRING='libsodium 1.0.16'
PACKAGE_VERSION='1.0.18'
-PACKAGE_STRING='libsodium 1.0.18'
+PACKAGE_STRING='libsodium'
PACKAGE_BUGREPORT='https://github.com/jedisct1/libsodium/issues'
PACKAGE_URL='https://github.com/jedisct1/libsodium'
+10
View File
@@ -136,11 +136,21 @@ endif()
if(ARCHITECTURE STREQUAL "i686")
SET(ARCH_ID "i386")
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(BUILD_TAG "linux-x86")
SET(LINUX_32 ON)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
set(BUILD_TAG "win-x32")
endif()
endif()
if(ARCHITECTURE STREQUAL "x86_64")
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(BUILD_TAG "linux-x64")
elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
set(BUILD_TAG "freebsd-x64")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
set(BUILD_TAG "win-x64")
endif()
SET(ARCH_ID "x86_64")
endif()
+4 -4
View File
@@ -84,7 +84,7 @@ namespace epee
return write_hex(out, src);
}
std::vector<uint8_t> from_hex::vector(boost::string_ref src)
std::vector<uint8_t> from_hex::vector(const boost::string_ref src)
{
// should we include a specific character
auto include = [](char input) {
@@ -104,7 +104,7 @@ namespace epee
result.reserve(count / 2);
// the data to work with (std::string is always null-terminated)
auto data = src.data();
auto data = src.begin();
// convert a single hex character to an unsigned integer
auto char_to_int = [](const char *input) {
@@ -130,9 +130,9 @@ namespace epee
};
// keep going until we reach the end
while (data[0] != '\0') {
while (data != src.end()) {
// skip unwanted characters
if (!include(data[0])) {
if (!include(*data)) {
++data;
continue;
}
+3 -3
View File
@@ -126,7 +126,7 @@ Setup for LXC:
```bash
GH_USER=fluffypony
VERSION=v0.15.0.1
VERSION=v0.15.0.5
./gitian-build.py --setup $GH_USER $VERSION
```
@@ -167,7 +167,7 @@ If all went well, this produces a number of (uncommitted) `.assert` files in the
Checking your work
------------------
Take a look in the assert files and note the SHA256 checksums listed there. eg for `v0.15.0.1` you should get this checksum:
Take a look in the assert files and note the SHA256 checksums listed there. eg for `v0.15.0.5` you should get this checksum:
```
2b95118f53d98d542a85f8732b84ba13b3cd20517ccb40332b0edd0ddf4f8c62 monero-x86_64-linux-gnu.tar.gz
@@ -183,7 +183,7 @@ If you chose to do detached signing using `--detach-sign` above (recommended), y
```bash
GH_USER=fluffypony
VERSION=v0.15.0.1
VERSION=v0.15.0.5
gpg --detach-sign ${VERSION}-linux/${GH_USER}/monero-linux-*-build.assert
gpg --detach-sign ${VERSION}-win/${GH_USER}/monero-win-*-build.assert
Binary file not shown.
+1
View File
@@ -211,6 +211,7 @@ namespace cryptonote
ADD_CHECKPOINT(1775600, "1c6e01c661dc22cab939e79ec6a5272190624ce8356d2f7b958e4f9a57fdb05e");
ADD_CHECKPOINT(1856000, "9b57f17f29c71a3acd8a7904b93c41fa6eb8d2b7c73936ce4f1702d14880ba29");
ADD_CHECKPOINT(1958000, "98a5d6e51afdf3146e0eefb10a66e8648d8d4d5c2742be8835e976ba217c9bb2");
ADD_CHECKPOINT(2046000, "5e867f0b8baefed9244a681df97fc885d8ab36c3dfcd24c7a3abf3b8ac8b8314");
return true;
}
+1 -1
View File
@@ -5029,7 +5029,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
static const char expected_block_hashes_hash[] = "bd0028887df452e58655a68015f74b923f29e9829b0b2c030d64feee67a2f537";
static const char expected_block_hashes_hash[] = "d2ecd6b7a1937dcabd2b1108d3060a85bfdd523b2baa65f84430f674744750d5";
void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
{
if (get_checkpoints == nullptr || !m_fast_sync)
+16 -4
View File
@@ -43,6 +43,9 @@
#include "net/dandelionpp.h"
#include "p2p/net_node.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "net.p2p.tx"
namespace cryptonote
{
namespace levin
@@ -242,6 +245,8 @@ namespace levin
if (!channel.connection.is_nil())
channel.queue.push_back(std::move(message_));
else if (destination_ == 0 && zone_->connection_count == 0)
MWARNING("Unable to send transaction(s) over anonymity network - no available outbound connections");
}
};
@@ -286,8 +291,12 @@ namespace levin
return true;
});
bool sent = false;
for (const boost::uuids::uuid& connection : connections)
zone_->p2p->send(message_.clone(), connection);
sent |= zone_->p2p->send(message_.clone(), connection);
if (!sent)
MWARNING("Unable to send transaction(s), no available connections");
}
};
@@ -441,9 +450,12 @@ namespace levin
{
channel.active = nullptr;
channel.connection = boost::uuids::nil_uuid();
zone_->strand.post(
update_channels{zone_, get_out_connections(*zone_->p2p)}
);
auto connections = get_out_connections(*zone_->p2p);
if (connections.empty())
MWARNING("Lost all outbound connections to anonymity network - currently unable to send transaction(s)");
zone_->strand.post(update_channels{zone_, std::move(connections)});
}
}
+6 -8
View File
@@ -1058,17 +1058,15 @@ namespace nodetool
pi = context.peer_id = rsp.node_data.peer_id;
context.m_rpc_port = rsp.node_data.rpc_port;
context.m_rpc_credits_per_hash = rsp.node_data.rpc_credits_per_hash;
m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.set_peer_just_seen(rsp.node_data.peer_id, context.m_remote_address, context.m_pruning_seed, context.m_rpc_port, context.m_rpc_credits_per_hash);
network_zone& zone = m_network_zones.at(context.m_remote_address.get_zone());
zone.m_peerlist.set_peer_just_seen(rsp.node_data.peer_id, context.m_remote_address, context.m_pruning_seed, context.m_rpc_port, context.m_rpc_credits_per_hash);
// move
for (auto const& zone : m_network_zones)
if(rsp.node_data.peer_id == zone.m_config.m_peer_id)
{
if(rsp.node_data.peer_id == zone.second.m_config.m_peer_id)
{
LOG_DEBUG_CC(context, "Connection to self detected, dropping connection");
hsh_result = false;
return;
}
LOG_DEBUG_CC(context, "Connection to self detected, dropping connection");
hsh_result = false;
return;
}
LOG_INFO_CC(context, "New connection handshaked, pruning seed " << epee::string_tools::to_string_hex(context.m_pruning_seed));
LOG_DEBUG_CC(context, " COMMAND_HANDSHAKE INVOKED OK");
+12 -3
View File
@@ -52,6 +52,16 @@ constexpr const char id_field[] = "id";
constexpr const char method_field[] = "method";
constexpr const char params_field[] = "params";
constexpr const char result_field[] = "result";
const rapidjson::Value& get_method_field(const rapidjson::Value& src)
{
const auto member = src.FindMember(method_field);
if (member == src.MemberEnd())
throw cryptonote::json::MISSING_KEY{method_field};
if (!member->value.IsString())
throw cryptonote::json::WRONG_TYPE{"Expected string"};
return member->value;
}
}
rapidjson::Value Message::toJson(rapidjson::Document& doc) const
@@ -120,7 +130,7 @@ FullMessage::FullMessage(const std::string& json_string, bool request)
if (request)
{
OBJECT_HAS_MEMBER_OR_THROW(doc, method_field)
get_method_field(doc); // throws on errors
OBJECT_HAS_MEMBER_OR_THROW(doc, params_field)
}
else
@@ -151,8 +161,7 @@ std::string FullMessage::getJson()
std::string FullMessage::getRequestType() const
{
OBJECT_HAS_MEMBER_OR_THROW(doc, method_field)
return doc[method_field].GetString();
return get_method_field(doc).GetString();
}
rapidjson::Value& FullMessage::getMessage()
+2 -2
View File
@@ -8356,7 +8356,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec
m_in_manual_refresh.store(true, std::memory_order_relaxed);
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
std::vector<std::pair<cryptonote::transaction, bool>> process_txs;
std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_txs;
m_wallet->update_pool_state(process_txs);
if (!process_txs.empty())
m_wallet->process_pool_state(process_txs);
@@ -10025,7 +10025,7 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args)
try
{
std::vector<std::pair<cryptonote::transaction, bool>> process_txs;
std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_txs;
m_wallet->update_pool_state(process_txs);
if (!process_txs.empty())
m_wallet->process_pool_state(process_txs);
+1 -1
View File
@@ -1,5 +1,5 @@
#define DEF_MONERO_VERSION_TAG "@VERSIONTAG@"
#define DEF_MONERO_VERSION "0.15.0.1"
#define DEF_MONERO_VERSION "0.15.0.5"
#define DEF_MONERO_RELEASE_NAME "Carbon Chamaeleon"
#define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG
#define DEF_MONERO_VERSION_IS_RELEASE @VERSION_IS_RELEASE@
+7 -7
View File
@@ -2829,7 +2829,7 @@ void wallet2::remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashe
}
//----------------------------------------------------------------------------------------------------
void wallet2::update_pool_state(std::vector<std::pair<cryptonote::transaction, bool>> &process_txs, bool refreshed)
void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed)
{
MTRACE("update_pool_state start");
@@ -3019,7 +3019,7 @@ void wallet2::update_pool_state(std::vector<std::pair<cryptonote::transaction, b
[tx_hash](const std::pair<crypto::hash, bool> &e) { return e.first == tx_hash; });
if (i != txids.end())
{
process_txs.push_back(std::make_pair(tx, tx_entry.double_spend_seen));
process_txs.push_back(std::make_tuple(tx, tx_hash, tx_entry.double_spend_seen));
}
else
{
@@ -3050,14 +3050,14 @@ void wallet2::update_pool_state(std::vector<std::pair<cryptonote::transaction, b
MTRACE("update_pool_state end");
}
//----------------------------------------------------------------------------------------------------
void wallet2::process_pool_state(const std::vector<std::pair<cryptonote::transaction, bool>> &txs)
void wallet2::process_pool_state(const std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &txs)
{
const time_t now = time(NULL);
for (const auto &e: txs)
{
const cryptonote::transaction &tx = e.first;
const bool double_spend_seen = e.second;
const crypto::hash tx_hash = get_transaction_hash(tx);
const cryptonote::transaction &tx = std::get<0>(e);
const crypto::hash &tx_hash = std::get<1>(e);
const bool double_spend_seen = std::get<2>(e);
process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, 0, now, false, true, double_spend_seen, {});
m_scanned_pool_txs[0].insert(tx_hash);
if (m_scanned_pool_txs[0].size() > 5000)
@@ -3276,7 +3276,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
// since that might cause a password prompt, which would introduce a data
// leak allowing a passive adversary with traffic analysis capability to
// infer when we get an incoming output
std::vector<std::pair<cryptonote::transaction, bool>> process_pool_txs;
std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_pool_txs;
update_pool_state(process_pool_txs, true);
bool first = true, last = false;
+2 -2
View File
@@ -1219,8 +1219,8 @@ private:
bool import_key_images(signed_tx_set & signed_tx, size_t offset=0, bool only_selected_transfers=false);
crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const;
void update_pool_state(std::vector<std::pair<cryptonote::transaction, bool>> &process_txs, bool refreshed = false);
void process_pool_state(const std::vector<std::pair<cryptonote::transaction, bool>> &txs);
void update_pool_state(std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed = false);
void process_pool_state(const std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &txs);
void remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashes);
std::string encrypt(const char *plaintext, size_t len, const crypto::secret_key &skey, bool authenticated = true) const;
+2 -2
View File
@@ -2438,7 +2438,7 @@ namespace tools
if (req.pool)
{
std::vector<std::pair<cryptonote::transaction, bool>> process_txs;
std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_txs;
m_wallet->update_pool_state(process_txs);
if (!process_txs.empty())
m_wallet->process_pool_state(process_txs);
@@ -2521,7 +2521,7 @@ namespace tools
}
}
std::vector<std::pair<cryptonote::transaction, bool>> process_txs;
std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_txs;
m_wallet->update_pool_state(process_txs);
if (!process_txs.empty())
m_wallet->process_pool_state(process_txs);
+3 -1
View File
@@ -92,7 +92,8 @@ set(unit_tests_sources
ringdb.cpp
wipeable_string.cpp
is_hdd.cpp
aligned.cpp)
aligned.cpp
zmq_rpc.cpp)
set(unit_tests_headers
unit_tests_utils.h)
@@ -105,6 +106,7 @@ target_link_libraries(unit_tests
ringct
cryptonote_protocol
cryptonote_core
daemon_messages
blockchain_db
lmdb_lib
rpc
+3
View File
@@ -840,6 +840,9 @@ TEST(FromHex, String)
// decoding it this way also, ignoring spaces and colons between the numbers
hex.assign("00:ff 0f:f0");
EXPECT_EQ(source, epee::from_hex::vector(hex));
hex.append("f0");
EXPECT_EQ(source, epee::from_hex::vector(boost::string_ref{hex.data(), hex.size() - 2}));
}
TEST(ToHex, Array)
+55
View File
@@ -0,0 +1,55 @@
// Copyright (c) 2020, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <gtest/gtest.h>
#include "rpc/message.h"
#include "serialization/json_object.h"
TEST(ZmqFullMessage, InvalidRequest)
{
EXPECT_THROW(
(cryptonote::rpc::FullMessage{"{\"jsonrpc\":\"2.0\",\"id\":0,\"params\":[]}", true}),
cryptonote::json::MISSING_KEY
);
EXPECT_THROW(
(cryptonote::rpc::FullMessage{"{\"jsonrpc\":\"2.0\",\"id\":0,\"method\":3,\"params\":[]}", true}),
cryptonote::json::WRONG_TYPE
);
}
TEST(ZmqFullMessage, Request)
{
static constexpr const char request[] = "{\"jsonrpc\":\"2.0\",\"id\":0,\"method\":\"foo\",\"params\":[]}";
EXPECT_NO_THROW(
(cryptonote::rpc::FullMessage{request, true})
);
cryptonote::rpc::FullMessage parsed{request, true};
EXPECT_STREQ("foo", parsed.getRequestType().c_str());
}