You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
434 lines
11 KiB
434 lines
11 KiB
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
#include <boost/iterator/filter_iterator.hpp>
|
|
|
|
#include "../src/UniversalIdentifier.hpp"
|
|
|
|
#include "mocks.h"
|
|
|
|
#define ADD_MOCKS(mcore) \
|
|
EXPECT_CALL(mcore, get_output_tx_and_index(_, _, _)) \
|
|
.WillRepeatedly( \
|
|
Invoke(&*jtx, &JsonTx::get_output_tx_and_index)); \
|
|
\
|
|
EXPECT_CALL(mcore, get_tx(_, _)) \
|
|
.WillRepeatedly( \
|
|
Invoke(&*jtx, &JsonTx::get_tx)); \
|
|
\
|
|
EXPECT_CALL(mcore, get_output_key(_, _, _)) \
|
|
.WillRepeatedly( \
|
|
Invoke(&*jtx, &JsonTx::get_output_key));\
|
|
\
|
|
EXPECT_CALL(mcore, get_num_outputs(_)) \
|
|
.WillRepeatedly(Return(1e10));
|
|
|
|
namespace
|
|
{
|
|
|
|
using namespace xmreg;
|
|
|
|
|
|
// equality operators for outputs
|
|
|
|
inline bool
|
|
operator==(const Output::info& lhs, const JsonTx::output& rhs)
|
|
{
|
|
return lhs.amount == rhs.amount
|
|
&& lhs.pub_key == rhs.pub_key
|
|
&& lhs.idx_in_tx == rhs.index;
|
|
}
|
|
|
|
inline bool
|
|
operator!=(const Output::info& lhs, const JsonTx::output& rhs)
|
|
{
|
|
return !(lhs == rhs);
|
|
}
|
|
|
|
inline bool
|
|
operator==(const vector<Output::info>& lhs, const vector<JsonTx::output>& rhs)
|
|
{
|
|
if (lhs.size() != rhs.size())
|
|
return false;
|
|
|
|
for (size_t i = 0; i < lhs.size(); i++)
|
|
{
|
|
if (lhs[i] != rhs[i])
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
// equality operators for inputs
|
|
|
|
inline bool
|
|
operator==(const Input::info& lhs, const JsonTx::input& rhs)
|
|
{
|
|
return lhs.amount == rhs.amount
|
|
&& lhs.out_pub_key == rhs.out_pub_key
|
|
&& lhs.key_img == rhs.key_img;
|
|
}
|
|
|
|
inline bool
|
|
operator!=(const Input::info& lhs, const JsonTx::input& rhs)
|
|
{
|
|
return !(lhs == rhs);
|
|
}
|
|
|
|
inline bool
|
|
operator==(const vector<Input::info>& lhs, const vector<JsonTx::input>& rhs)
|
|
{
|
|
if (lhs.size() != rhs.size())
|
|
return false;
|
|
|
|
for (size_t i = 0; i < lhs.size(); i++)
|
|
{
|
|
if (lhs[i] != rhs[i])
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
class DifferentJsonTxs :
|
|
public ::testing::TestWithParam<string>
|
|
{
|
|
|
|
};
|
|
|
|
class ModularIdentifierTest : public DifferentJsonTxs
|
|
{};
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
DifferentJsonTxs, ModularIdentifierTest,
|
|
::testing::Values(
|
|
"ddff95211b53c194a16c2b8f37ae44b643b8bd46b4cb402af961ecabeb8417b2"s,
|
|
"f3c84fe925292ec5b4dc383d306d934214f4819611566051bca904d1cf4efceb"s,
|
|
"d7dcb2daa64b5718dad71778112d48ad62f4d5f54337037c420cb76efdd8a21c"s,
|
|
"61f756a299efd17442eed5437fa03cbda6b01f341907845f8880bf30319fa01c"s,
|
|
"ae8f3ad29a40e02dff6a3267c769f08c0af3dc8858683c90ce3ef90212cb7e4b"s,
|
|
"140807b970e52b7c633d7ca0ba5be603922aa7a2a1213bdd16d3c1a531402bf6"s,
|
|
"a7a4e3bdb305b97c43034440b0bc5125c23b24d0730189261151c0aa3f2a05fc"s,
|
|
"c06df274acc273fbce0666b2c8846ac6925a1931fb61e3020b7cc5410d4646b1"s,
|
|
"d89f32f1434b6a668cbbc5c55cb1c0c64e41fccb89f6b1eef210fefdacbdd89f"s,
|
|
"bd461b938c3c8c8e4d9909852221d5c37350ade05e99ef836d6ccb628f6a5a0e"s
|
|
));
|
|
|
|
TEST_P(ModularIdentifierTest, OutputsRingCT)
|
|
{
|
|
string tx_hash_str = GetParam();
|
|
|
|
auto jtx = construct_jsontx(tx_hash_str);
|
|
|
|
ASSERT_TRUE(jtx);
|
|
|
|
auto identifier = make_identifier(jtx->tx,
|
|
make_unique<Output>(&jtx->sender.address,
|
|
&jtx->sender.viewkey));
|
|
|
|
identifier.identify();
|
|
|
|
ASSERT_EQ(identifier.get<0>()->get().size(),
|
|
jtx->sender.outputs.size());
|
|
|
|
ASSERT_TRUE(identifier.get<0>()->get() == jtx->sender.outputs);
|
|
|
|
ASSERT_EQ(identifier.get<0>()->get_total(),
|
|
jtx->sender.change);
|
|
}
|
|
|
|
|
|
|
|
TEST_P(ModularIdentifierTest, OutputsRingCTCoinbaseTx)
|
|
{
|
|
string tx_hash_str = GetParam();
|
|
|
|
auto jtx = construct_jsontx(tx_hash_str);
|
|
|
|
ASSERT_TRUE(jtx);
|
|
|
|
auto identifier = make_identifier(jtx->tx,
|
|
make_unique<Output>(&jtx->recipients.at(0).address,
|
|
&jtx->recipients.at(0).viewkey));
|
|
|
|
identifier.identify();
|
|
|
|
ASSERT_TRUE(identifier.get<0>()->get()
|
|
== jtx->recipients.at(0).outputs);
|
|
|
|
ASSERT_EQ(identifier.get<0>()->get_total(),
|
|
jtx->recipients.at(0).amount);
|
|
}
|
|
|
|
TEST_P(ModularIdentifierTest, MultiOutputsRingCT)
|
|
{
|
|
string tx_hash_str = GetParam();
|
|
|
|
auto jtx = construct_jsontx(tx_hash_str);
|
|
|
|
ASSERT_TRUE(jtx);
|
|
|
|
for (auto const& jrecipient: jtx->recipients)
|
|
{
|
|
auto identifier = make_identifier(jtx->tx,
|
|
make_unique<Output>(&jrecipient.address,
|
|
&jrecipient.viewkey));
|
|
|
|
identifier.identify();
|
|
|
|
EXPECT_TRUE(identifier.get<0>()->get()
|
|
== jrecipient.outputs);
|
|
}
|
|
}
|
|
|
|
|
|
TEST_P(ModularIdentifierTest, LegacyPaymentID)
|
|
{
|
|
string tx_hash_str = GetParam();
|
|
|
|
auto jtx = construct_jsontx(tx_hash_str);
|
|
|
|
auto identifier = make_identifier(jtx->tx,
|
|
make_unique<LegacyPaymentID>(nullptr, nullptr));
|
|
|
|
identifier.identify();
|
|
|
|
EXPECT_TRUE(identifier.get<0>()->get()
|
|
== jtx->payment_id);
|
|
|
|
EXPECT_TRUE(identifier.get<0>()->raw()
|
|
== jtx->payment_id);
|
|
}
|
|
|
|
|
|
TEST_P(ModularIdentifierTest, IntegratedPaymentID)
|
|
{
|
|
string tx_hash_str = GetParam();
|
|
|
|
auto jtx = construct_jsontx(tx_hash_str);
|
|
|
|
ASSERT_TRUE(jtx);
|
|
|
|
auto identifier = make_identifier(jtx->tx,
|
|
make_unique<IntegratedPaymentID>(
|
|
&jtx->recipients[0].address,
|
|
&jtx->recipients[0].viewkey));
|
|
|
|
identifier.identify();
|
|
|
|
//cout << "decrypted: " << pod_to_hex(identifier.get<0>()->get())
|
|
// << ", " << pod_to_hex(jtx->payment_id8e) << endl;
|
|
|
|
|
|
EXPECT_TRUE(identifier.get<0>()->get()
|
|
== jtx->payment_id8e);
|
|
|
|
EXPECT_TRUE(identifier.get<0>()->raw()
|
|
== jtx->payment_id8);
|
|
|
|
//cout << "row: " << pod_to_hex(identifier.get<0>()->raw())
|
|
// << ", " << pod_to_hex(jtx->payment_id8) << endl;
|
|
}
|
|
|
|
TEST_P(ModularIdentifierTest, InputWithKnownOutputs)
|
|
{
|
|
string tx_hash_str = GetParam();
|
|
|
|
auto jtx = construct_jsontx(tx_hash_str);
|
|
|
|
ASSERT_TRUE(jtx);
|
|
|
|
// make vector of known outputs
|
|
|
|
Input::known_outputs_t known_outputs;
|
|
uint64_t expected_total {};
|
|
|
|
// first add real public keys
|
|
for (auto&& input: jtx->sender.inputs)
|
|
{
|
|
known_outputs[input.out_pub_key] = input.amount;
|
|
expected_total += input.amount;
|
|
}
|
|
|
|
|
|
// now add some random ones
|
|
|
|
for (size_t i = 0; i < 20; ++i)
|
|
{
|
|
auto rand_pk = crypto::rand<public_key>();
|
|
known_outputs[rand_pk] = 4353534534; // some amount
|
|
}
|
|
|
|
MockMicroCore mcore;
|
|
|
|
ADD_MOCKS(mcore);
|
|
|
|
auto identifier = make_identifier(jtx->tx,
|
|
make_unique<Input>(
|
|
&jtx->sender.address,
|
|
&jtx->sender.viewkey,
|
|
&known_outputs,
|
|
&mcore));
|
|
|
|
|
|
identifier.identify();
|
|
|
|
|
|
ASSERT_TRUE(identifier.get<0>()->get()
|
|
== jtx->sender.inputs);
|
|
|
|
ASSERT_EQ(identifier.get<0>()->get_total(),
|
|
expected_total);
|
|
}
|
|
|
|
TEST_P(ModularIdentifierTest, GuessInputRingCT)
|
|
{
|
|
string tx_hash_str = GetParam();
|
|
|
|
auto jtx = construct_jsontx(tx_hash_str);
|
|
|
|
ASSERT_TRUE(jtx);
|
|
|
|
MockMicroCore mcore;
|
|
|
|
ADD_MOCKS(mcore);
|
|
|
|
auto identifier = make_identifier(jtx->tx,
|
|
make_unique<GuessInput>(
|
|
&jtx->sender.address,
|
|
&jtx->sender.viewkey,
|
|
&mcore));
|
|
|
|
identifier.identify();
|
|
|
|
// for (auto const& input_info: identifier.get<0>()->get())
|
|
// cout << input_info << endl;
|
|
|
|
auto const& found_inputs = identifier.get<0>()->get();
|
|
|
|
EXPECT_GE(found_inputs.size(),
|
|
jtx->sender.inputs.size());
|
|
|
|
if (!jtx->sender.inputs.empty())
|
|
{
|
|
// to check whether GuessIdentifier is correct
|
|
// we check weath all outputs and key images are present
|
|
// in its found_inputs
|
|
//
|
|
|
|
for (auto const& input: jtx->sender.inputs)
|
|
{
|
|
|
|
// make lambda that will check wether
|
|
// current input's public key matches a
|
|
// given one
|
|
//
|
|
// to do this, first we find all public
|
|
// output keys in found_inputs vector
|
|
// that GuessInput populated
|
|
|
|
using input_elem_type
|
|
= std::remove_reference_t<decltype(found_inputs)>::value_type;
|
|
|
|
vector<input_elem_type const*>
|
|
matched_public_keys;
|
|
|
|
for (auto const& fin: found_inputs)
|
|
{
|
|
if (input.out_pub_key == fin.out_pub_key)
|
|
matched_public_keys.push_back(&fin);
|
|
}
|
|
|
|
if (matched_public_keys.empty())
|
|
FAIL() << "matched_public_keys is empty";
|
|
|
|
// second, if we found something, check
|
|
// if matched_public_keys contains
|
|
// key image from senders.input
|
|
|
|
bool match_found {false};
|
|
|
|
for (auto const matched_pk: matched_public_keys)
|
|
if (input.key_img == matched_pk->key_img)
|
|
{
|
|
match_found = true;
|
|
SUCCEED();
|
|
}
|
|
|
|
if (!match_found)
|
|
FAIL() << "No maching key image found";
|
|
|
|
} //for (auto const& input: jtx->sender.inputs)
|
|
|
|
} // if (!jtx->sender.inputs.empty())
|
|
|
|
}
|
|
|
|
TEST_P(ModularIdentifierTest, RealInputRingCT)
|
|
{
|
|
string tx_hash_str = GetParam();
|
|
|
|
auto jtx = construct_jsontx(tx_hash_str);
|
|
|
|
ASSERT_TRUE(jtx);
|
|
|
|
MockMicroCore mcore;
|
|
|
|
ADD_MOCKS(mcore);
|
|
|
|
auto identifier = make_identifier(jtx->tx,
|
|
make_unique<RealInput>(
|
|
&jtx->sender.address,
|
|
&jtx->sender.viewkey,
|
|
&jtx->sender.spendkey,
|
|
&mcore));
|
|
|
|
identifier.identify();
|
|
|
|
// for (auto const& input_info: identifier.get<0>()->get())
|
|
// cout << input_info << endl;
|
|
|
|
EXPECT_TRUE(identifier.get<0>()->get()
|
|
== jtx->sender.inputs);
|
|
}
|
|
|
|
|
|
TEST(Subaddresses, RegularTwoOutputTxToSubaddress)
|
|
{
|
|
// this tx has funds for one subaddress. so we try to identify the outputs
|
|
// and the subaddress using primary address of the recipient
|
|
auto jtx = construct_jsontx("024dc13cb11d411682f04d41b52931849527d530e4cb198a63526c13da31a413");
|
|
|
|
ASSERT_TRUE(jtx);
|
|
|
|
// recipeint primary address and viewkey
|
|
string const raddress {"56heRv2ANffW1Py2kBkJDy8xnWqZsSrgjLygwjua2xc8Wbksead1NK1ehaYpjQhymGK4S8NPL9eLuJ16CuEJDag8Hq3RbPV"};
|
|
string const rviewkey {"b45e6f38b2cd1c667459527decb438cdeadf9c64d93c8bccf40a9bf98943dc09"};
|
|
|
|
auto racc = account_factory(raddress, rviewkey);
|
|
|
|
// make sure we have primary address
|
|
ASSERT_FALSE(racc->is_subaddress());
|
|
|
|
auto sacc = static_cast<PrimaryAccount*>(racc.get());
|
|
|
|
sacc->populate_subaddress_indices();
|
|
|
|
auto identifier = make_identifier(jtx->tx,
|
|
make_unique<Output>(sacc));
|
|
|
|
identifier.identify();
|
|
|
|
EXPECT_EQ(identifier.get<0>()->get().size(),
|
|
jtx->recipients.at(0).outputs.size());
|
|
|
|
EXPECT_TRUE(identifier.get<0>()->get()
|
|
== jtx->recipients.at(0).outputs);
|
|
}
|
|
|
|
|
|
}
|