2929#include < algorithm>
3030#include < cstring>
3131
32+ #include " carrot_core/account_secrets.h"
3233#include " common/error.h"
3334#include " common/expect.h"
35+ #include " db/carrot.h"
3436#include " db/data.h"
3537#include " db/string.h"
38+ #include " error.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 carrot::get_image (out, immutable_->pubs , immutable_->balance_key );
325+ }
326+ } // lws
245327
0 commit comments