Skip to content

Commit a179709

Browse files
committed
Add support for carrot (legacy and view-balance keys). WIP
1 parent 36aa014 commit a179709

25 files changed

Lines changed: 1941 additions & 274 deletions

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ set(MONERO_LIBRARIES
6969
serialization
7070
lmdb_lib
7171
net
72+
carrot_core
7273
cryptonote_core
7374
cryptonote_basic
7475
cryptonote_format_utils_basic
@@ -87,6 +88,9 @@ set(MONERO_LIBRARIES
8788
easylogging
8889
version
8990
wallet-crypto
91+
fcmp_pp
92+
mx25519
93+
wallet
9094
)
9195

9296
set(MONERO_OPTIONAL wallet-crypto)
@@ -280,6 +284,7 @@ set_property(TARGET monero::libraries PROPERTY
280284
"${MONERO_SOURCE_DIR}/external/easylogging++"
281285
"${MONERO_SOURCE_DIR}/external/rapidjson/include"
282286
"${MONERO_SOURCE_DIR}/external/supercop/include"
287+
"${MONERO_SOURCE_DIR}/external/mx25519/include"
283288
"${MONERO_SOURCE_DIR}/src"
284289
)
285290

src/db/account.cpp

Lines changed: 102 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,16 @@
2929
#include <algorithm>
3030
#include <cstring>
3131

32+
#include "carrot_core/account_secrets.h"
3233
#include "common/error.h"
3334
#include "common/expect.h"
3435
#include "db/data.h"
3536
#include "db/string.h"
37+
#include "error.h"
38+
#include "util/transactions.h"
3639
#include "wire/adapted/crypto.h"
3740
#include "wire/adapted/pair.h"
41+
#include "wire/adapted/tuple.h"
3842
#include "wire/msgpack.h"
3943
#include "wire/vector.h"
4044
#include "wire/wrapper/trusted_array.h"
@@ -51,28 +55,53 @@ namespace lws
5155
return std::memcmp(std::addressof(lhs), std::addressof(rhs), sizeof(lhs)) < 0;
5256
}
5357
};
58+
59+
account::key_type get_type(const db::account_flags flags) noexcept
60+
{
61+
if (flags & db::account_flags::view_balance_key)
62+
return account::key_type::balance;
63+
return account::key_type::legacy;
64+
}
5465
}
5566

67+
WIRE_AS_INTEGER(account::key_type);
68+
5669
struct account::internal
5770
{
5871
internal()
59-
: address(), id(db::account_id::invalid), pubs{}, view_key{}
72+
: address(),
73+
id(db::account_id::invalid),
74+
pubs{},
75+
view_key{},
76+
balance_key{},
77+
type(account::key_type::legacy)
6078
{}
6179

6280
explicit internal(db::account const& source)
63-
: address(db::address_string(source.address)), id(source.id), pubs(source.address), view_key()
81+
: address(db::address_string(source.address)),
82+
id(source.id),
83+
pubs(source.address),
84+
view_key(),
85+
balance_key{},
86+
type(get_type(source.flags))
6487
{
6588
using inner_type =
6689
std::remove_reference<decltype(tools::unwrap(view_key))>::type;
6790

91+
crypto::secret_key& target = type == account::key_type::balance ?
92+
balance_key : view_key;
93+
6894
static_assert(std::is_standard_layout<db::view_key>(), "need standard layout source");
6995
static_assert(std::is_pod<inner_type>(), "need pod target");
70-
static_assert(sizeof(view_key) == sizeof(source.key), "different size keys");
96+
static_assert(sizeof(target) == sizeof(source.key), "different size keys");
7197
std::memcpy(
72-
std::addressof(tools::unwrap(view_key)),
98+
std::addressof(tools::unwrap(target)),
7399
std::addressof(source.key),
74100
sizeof(source.key)
75101
);
102+
103+
if (type == account::key_type::balance)
104+
carrot::make_carrot_viewincoming_key(balance_key, view_key);
76105
}
77106

78107
void read_bytes(wire::msgpack_reader& source)
@@ -88,19 +117,24 @@ namespace lws
88117
WIRE_FIELD_ID(0, address),
89118
WIRE_FIELD_ID(1, id),
90119
WIRE_FIELD_ID(2, pubs),
91-
WIRE_FIELD_ID(3, view_key)
120+
WIRE_FIELD_ID(3, view_key),
121+
WIRE_FIELD_ID(4, balance_key),
122+
WIRE_FIELD_ID(5, type)
92123
);
93124
}
94125

95126
std::string address;
96127
db::account_id id;
97128
db::account_address pubs;
98129
crypto::secret_key view_key;
130+
crypto::secret_key balance_key;
131+
account::key_type type;
99132
};
100133

101-
account::account(std::shared_ptr<const internal> immutable, db::block_id height, std::vector<std::pair<db::output_id, db::address_index>> spendable, std::vector<crypto::public_key> pubs) noexcept
134+
account::account(std::shared_ptr<const internal> immutable, db::block_id height, std::vector<spendable_t> spendable, std::vector<balance_t> balance, std::vector<crypto::public_key> pubs) noexcept
102135
: immutable_(std::move(immutable))
103136
, spendable_(std::move(spendable))
137+
, balance_(std::move(balance))
104138
, pubs_(std::move(pubs))
105139
, spends_()
106140
, outputs_()
@@ -122,18 +156,20 @@ namespace lws
122156
wire::optional_field<2>("pubs_", wire::trusted_array(std::ref(self.pubs_))),
123157
wire::optional_field<3>("spends_", wire::trusted_array(std::ref(self.spends_))),
124158
wire::optional_field<4>("outputs_", wire::trusted_array(std::ref(self.outputs_))),
125-
WIRE_FIELD_ID(5, height_)
159+
WIRE_FIELD_ID(5, height_),
160+
wire::optional_field<6>("balance_", wire::trusted_array(std::ref(self.balance_)))
126161
);
127162
}
128163

129164
account::account() noexcept
130-
: immutable_(nullptr), spendable_(), pubs_(), spends_(), outputs_(), height_(db::block_id(0))
165+
: immutable_(nullptr), spendable_(), balance_(), pubs_(), spends_(), outputs_(), height_(db::block_id(0))
131166
{}
132167

133-
account::account(db::account const& source, std::vector<std::pair<db::output_id, db::address_index>> spendable, std::vector<crypto::public_key> pubs)
134-
: account(std::make_shared<internal>(source), source.scan_height, std::move(spendable), std::move(pubs))
168+
account::account(db::account const& source, std::vector<spendable_t> spendable, std::vector<balance_t> balance, std::vector<crypto::public_key> pubs)
169+
: account(std::make_shared<internal>(source), source.scan_height, std::move(spendable), std::move(balance), std::move(pubs))
135170
{
136171
std::sort(spendable_.begin(), spendable_.end());
172+
std::sort(balance_.begin(), balance_.end());
137173
std::sort(pubs_.begin(), pubs_.end(), sort_pubs{});
138174
}
139175

@@ -146,6 +182,7 @@ namespace lws
146182
map(source, *this, *immutable);
147183
immutable_ = std::move(immutable);
148184
std::sort(spendable_.begin(), spendable_.end());
185+
std::sort(balance_.begin(), balance_.end());
149186
std::sort(pubs_.begin(), pubs_.end(), sort_pubs{});
150187
}
151188

@@ -157,7 +194,7 @@ namespace lws
157194

158195
account account::clone() const
159196
{
160-
account result{immutable_, height_, spendable_, pubs_};
197+
account result{immutable_, height_, spendable_, balance_, pubs_};
161198
result.outputs_ = outputs_;
162199
result.spends_ = spends_;
163200
return result;
@@ -179,6 +216,13 @@ namespace lws
179216
return db::account_id::invalid;
180217
}
181218

219+
account::key_type account::type() const noexcept
220+
{
221+
if (immutable_)
222+
return immutable_->type;
223+
return key_type::legacy;
224+
}
225+
182226
std::string const& account::address() const
183227
{
184228
null_check();
@@ -209,29 +253,61 @@ namespace lws
209253
return immutable_->view_key;
210254
}
211255

212-
boost::optional<db::address_index> account::get_spendable(db::output_id const& id) const noexcept
256+
crypto::secret_key const& account::balance_key() const
257+
{
258+
null_check();
259+
return immutable_->balance_key;
260+
}
261+
262+
std::optional<db::address_index> account::get_spendable(db::output_id const& id) const noexcept
213263
{
214264
const auto searchable =
215-
std::make_pair(id, db::address_index{db::major_index::primary, db::minor_index::primary});
265+
std::make_pair(id, db::address_index::primary());
216266
const auto account =
217267
std::lower_bound(spendable_.begin(), spendable_.end(), searchable);
218268
if (account == spendable_.end() || account->first != id)
219-
return boost::none;
269+
return std::nullopt;
220270
return account->second;
221271
}
222272

273+
std::optional<std::pair<db::output_id, db::address_index>> account::get_spendable(crypto::key_image const& image) const noexcept
274+
{
275+
const auto searchable =
276+
std::make_tuple(image, std::uint64_t(0), db::address_index::primary());
277+
const auto account =
278+
std::lower_bound(balance_.begin(), balance_.end(), searchable);
279+
if (account == balance_.end() || std::get<0>(*account) != image)
280+
return std::nullopt;
281+
return std::make_pair(db::output_id{0, std::get<1>(*account)}, std::get<2>(*account));
282+
}
283+
223284
bool account::add_out(db::output const& out)
224285
{
225286
auto existing_pub = std::lower_bound(pubs_.begin(), pubs_.end(), out.pub, sort_pubs{});
226287
if (existing_pub != pubs_.end() && *existing_pub == out.pub)
227288
return false;
228289

229290
pubs_.insert(existing_pub, out.pub);
230-
auto spendable_value = std::make_pair(out.spend_meta.id, out.recipient);
231-
spendable_.insert(
232-
std::lower_bound(spendable_.begin(), spendable_.end(), spendable_value),
233-
spendable_value
234-
);
291+
292+
if (type() == key_type::balance)
293+
{
294+
const std::optional<crypto::key_image> image = get_image(out);
295+
if (!image)
296+
MONERO_THROW(error::crypto_failure, "Failed to generate carrot key image");
297+
auto elem = std::make_tuple(*image, out.spend_meta.id.low, out.recipient);
298+
balance_.insert(
299+
std::lower_bound(balance_.begin(), balance_.end(), elem),
300+
elem
301+
);
302+
}
303+
else if (!out.is_carrot())
304+
{
305+
auto spendable_value = std::make_pair(out.spend_meta.id, out.recipient);
306+
spendable_.insert(
307+
std::lower_bound(spendable_.begin(), spendable_.end(), spendable_value),
308+
spendable_value
309+
);
310+
}
235311
outputs_.push_back(out);
236312
return true;
237313
}
@@ -240,6 +316,12 @@ namespace lws
240316
{
241317
spends_.push_back(spend);
242318
}
243-
} // lws
244319

320+
std::optional<crypto::key_image> account::get_image(db::output const& out) const
321+
{
322+
if (!immutable_ || type() != key_type::balance)
323+
return std::nullopt;
324+
return ::lws::get_image(out, immutable_->pubs, immutable_->balance_key);
325+
}
326+
} // lws
245327

src/db/account.h

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@
2626
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2727
#pragma once
2828

29-
#include <boost/optional/optional.hpp>
3029
#include <cstdint>
3130
#include <memory>
31+
#include <optional>
3232
#include <string>
33+
#include <tuple>
34+
#include <utility>
3335
#include <vector>
3436

3537
#include "crypto/crypto.h"
@@ -46,26 +48,32 @@ namespace lws
4648
{
4749
struct internal;
4850

51+
using balance_t = std::tuple<crypto::key_image, std::uint64_t, db::address_index>;
52+
using spendable_t = std::pair<db::output_id, db::address_index>;
53+
4954
std::shared_ptr<const internal> immutable_;
5055
std::vector<std::pair<db::output_id, db::address_index>> spendable_;
56+
std::vector<balance_t> balance_;
5157
std::vector<crypto::public_key> pubs_;
5258
std::vector<db::spend> spends_;
5359
std::vector<db::output> outputs_;
5460
db::block_id height_;
5561

56-
explicit account(std::shared_ptr<const internal> immutable, db::block_id height, std::vector<std::pair<db::output_id, db::address_index>> spendable, std::vector<crypto::public_key> pubs) noexcept;
62+
explicit account(std::shared_ptr<const internal> immutable, db::block_id height, std::vector<spendable_t> spendable, std::vector<balance_t> balance, std::vector<crypto::public_key> pubs) noexcept;
5763
void null_check() const;
5864

5965
template<typename F, typename T, typename U>
6066
static void map(F& format, T& self, U& immutable);
6167

6268
public:
69+
//! `view_key()` can be one of several different "types"
70+
enum class key_type : std::uint8_t { balance = 0, incoming, legacy };
6371

6472
//! Construct an "invalid" account (for de-serialization)
6573
account() noexcept;
6674

6775
//! Construct an account from `source` and current `spendable` outputs.
68-
explicit account(db::account const& source, std::vector<std::pair<db::output_id, db::address_index>> spendable, std::vector<crypto::public_key> pubs);
76+
explicit account(db::account const& source, std::vector<spendable_t> spendable, std::vector<balance_t> balance, std::vector<crypto::public_key> pubs);
6977

7078
/*!
7179
\return False if this is a "moved-from" account (i.e. the internal memory
@@ -94,6 +102,9 @@ namespace lws
94102
//! \return Unique ID from the account database, possibly `db::account_id::kInvalid`.
95103
db::account_id id() const noexcept;
96104

105+
//! \return Key-type
106+
key_type type() const noexcept;
107+
97108
//! \return Monero base58 string for account.
98109
std::string const& address() const;
99110

@@ -106,14 +117,20 @@ namespace lws
106117
//! \return Extracted spend public key from `address()`.
107118
crypto::public_key const& spend_public() const;
108119

109-
//! \return Secret view key for the account.
120+
//! \return Secret legacy or carrot incoming key for the account.
110121
crypto::secret_key const& view_key() const;
111122

123+
//! \return Secret balance view key, iff `type() == key_type::balance`.
124+
crypto::secret_key const& balance_key() const;
125+
112126
//! \return Current scan height of `this`.
113127
db::block_id scan_height() const noexcept { return height_; }
114128

115129
//! \return Subaddress index iff `id` is spendable by `this`.
116-
boost::optional<db::address_index> get_spendable(db::output_id const& id) const noexcept;
130+
std::optional<db::address_index> get_spendable(db::output_id const& id) const noexcept;
131+
132+
//! \return Output index + subaddress index iff `image` is spendable by `this`.
133+
std::optional<std::pair<db::output_id, db::address_index>> get_spendable(crypto::key_image const& image) const noexcept;
117134

118135
//! \return Outputs matched during the latest scan.
119136
std::vector<db::output> const& outputs() const noexcept { return outputs_; }
@@ -126,6 +143,9 @@ namespace lws
126143

127144
//! Track a possible `spend`.
128145
void add_spend(db::spend const& spend);
146+
147+
//! \return Key image for `out` iff `type() == key_type::balance`.
148+
std::optional<crypto::key_image> get_image(db::output const& out) const;
129149
};
130150

131151
struct by_height

src/db/data.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,14 @@
2929
#include <cstring>
3030
#include <memory>
3131

32+
#include "carrot_core/destination.h" // monero/src
33+
#include "carrot_core/device_ram_borrowed.h" // monero/src
3234
#include "cryptonote_config.h" // monero/src
3335
#include "db/string.h"
3436
#include "int-util.h" // monero/contribe/epee/include
3537
#include "ringct/rctOps.h" // monero/src
3638
#include "ringct/rctTypes.h" // monero/src
39+
#include "util/account.h"
3740
#include "wire.h"
3841
#include "wire/adapted/array.h"
3942
#include "wire/adapted/crypto.h"
@@ -163,6 +166,19 @@ namespace db
163166
return rct::rct2pk(rct::addKeys(rct::pk2rct(base.spend_public), rct::pk2rct(M)));
164167
}
165168

169+
crypto::public_key address_index::get_spend_public(carrot_account const& base, crypto::secret_key const& address) const
170+
{
171+
if (is_zero())
172+
return base.spend;
173+
174+
carrot::CarrotDestinationV1 out{};
175+
const carrot::generate_address_secret_ram_borrowed_device address_device{address};
176+
carrot::make_carrot_subaddress_v1(
177+
base.spend, base.view, address_device, std::uint32_t(maj_i), std::uint32_t(min_i), out
178+
);
179+
return out.address_spend_pubkey;
180+
}
181+
166182
namespace
167183
{
168184
template<typename F, typename T>

0 commit comments

Comments
 (0)