mirror of
				https://git.wownero.com/wownero/wownero.git
				synced 2024-08-15 01:03:23 +00:00 
			
		
		
		
	allow exporting outputs in chunks
this will make it easier huge wallets to do so without hitting random limits (eg, max string size in node).
This commit is contained in:
		
							parent
							
								
									b03d7091f7
								
							
						
					
					
						commit
						0cbf5571d3
					
				
					 13 changed files with 348 additions and 72 deletions
				
			
		| 
						 | 
				
			
			@ -511,7 +511,7 @@ namespace trezor {
 | 
			
		|||
                                tools::wallet2::signed_tx_set & signed_tx,
 | 
			
		||||
                                hw::tx_aux_data & aux_data)
 | 
			
		||||
    {
 | 
			
		||||
      CHECK_AND_ASSERT_THROW_MES(unsigned_tx.transfers.first == 0, "Unsuported non zero offset");
 | 
			
		||||
      CHECK_AND_ASSERT_THROW_MES(std::get<0>(unsigned_tx.transfers) == 0, "Unsuported non zero offset");
 | 
			
		||||
 | 
			
		||||
      TREZOR_AUTO_LOCK_CMD();
 | 
			
		||||
      require_connected();
 | 
			
		||||
| 
						 | 
				
			
			@ -522,7 +522,7 @@ namespace trezor {
 | 
			
		|||
      const size_t num_tx = unsigned_tx.txes.size();
 | 
			
		||||
      m_num_transations_to_sign = num_tx;
 | 
			
		||||
      signed_tx.key_images.clear();
 | 
			
		||||
      signed_tx.key_images.resize(unsigned_tx.transfers.second.size());
 | 
			
		||||
      signed_tx.key_images.resize(std::get<2>(unsigned_tx.transfers).size());
 | 
			
		||||
 | 
			
		||||
      for(size_t tx_idx = 0; tx_idx < num_tx; ++tx_idx) {
 | 
			
		||||
        std::shared_ptr<protocol::tx::Signer> signer;
 | 
			
		||||
| 
						 | 
				
			
			@ -566,8 +566,8 @@ namespace trezor {
 | 
			
		|||
        cpend.key_images = key_images;
 | 
			
		||||
 | 
			
		||||
        // KI sync
 | 
			
		||||
        for(size_t cidx=0, trans_max=unsigned_tx.transfers.second.size(); cidx < trans_max; ++cidx){
 | 
			
		||||
          signed_tx.key_images[cidx] = unsigned_tx.transfers.second[cidx].m_key_image;
 | 
			
		||||
        for(size_t cidx=0, trans_max=std::get<2>(unsigned_tx.transfers).size(); cidx < trans_max; ++cidx){
 | 
			
		||||
          signed_tx.key_images[cidx] = std::get<2>(unsigned_tx.transfers)[cidx].m_key_image;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        size_t num_sources = cdata.tx_data.sources.size();
 | 
			
		||||
| 
						 | 
				
			
			@ -579,9 +579,9 @@ namespace trezor {
 | 
			
		|||
          CHECK_AND_ASSERT_THROW_MES(src_idx < cdata.tx.vin.size(), "Invalid idx_mapped");
 | 
			
		||||
 | 
			
		||||
          size_t idx_map_src = cdata.tx_data.selected_transfers[idx_mapped];
 | 
			
		||||
          CHECK_AND_ASSERT_THROW_MES(idx_map_src >= unsigned_tx.transfers.first, "Invalid offset");
 | 
			
		||||
          CHECK_AND_ASSERT_THROW_MES(idx_map_src >= std::get<0>(unsigned_tx.transfers), "Invalid offset");
 | 
			
		||||
 | 
			
		||||
          idx_map_src -= unsigned_tx.transfers.first;
 | 
			
		||||
          idx_map_src -= std::get<0>(unsigned_tx.transfers);
 | 
			
		||||
          CHECK_AND_ASSERT_THROW_MES(idx_map_src < signed_tx.key_images.size(), "Invalid key image index");
 | 
			
		||||
 | 
			
		||||
          const auto vini = boost::get<cryptonote::txin_to_key>(cdata.tx.vin[src_idx]);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -230,8 +230,8 @@ namespace tx {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    const tools::wallet2::transfer_details & get_transfer(size_t idx) const {
 | 
			
		||||
      CHECK_AND_ASSERT_THROW_MES(idx < m_unsigned_tx->transfers.second.size() + m_unsigned_tx->transfers.first && idx >= m_unsigned_tx->transfers.first, "Invalid transfer index");
 | 
			
		||||
      return m_unsigned_tx->transfers.second[idx - m_unsigned_tx->transfers.first];
 | 
			
		||||
      CHECK_AND_ASSERT_THROW_MES(idx < std::get<2>(m_unsigned_tx->transfers).size() + std::get<0>(m_unsigned_tx->transfers) && idx >= std::get<0>(m_unsigned_tx->transfers), "Invalid transfer index");
 | 
			
		||||
      return std::get<2>(m_unsigned_tx->transfers)[idx - std::get<0>(m_unsigned_tx->transfers)];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const tools::wallet2::transfer_details & get_source_transfer(size_t idx) const {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										169
									
								
								src/serialization/tuple.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								src/serialization/tuple.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,169 @@
 | 
			
		|||
// Copyright (c) 2014-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.
 | 
			
		||||
// 
 | 
			
		||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include "serialization.h"
 | 
			
		||||
 | 
			
		||||
namespace serialization
 | 
			
		||||
{
 | 
			
		||||
  namespace detail
 | 
			
		||||
  {
 | 
			
		||||
    template <typename Archive, class T>
 | 
			
		||||
    bool serialize_tuple_element(Archive& ar, T& e)
 | 
			
		||||
    {
 | 
			
		||||
      return ::do_serialize(ar, e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <typename Archive>
 | 
			
		||||
    bool serialize_tuple_element(Archive& ar, uint64_t& e)
 | 
			
		||||
    {
 | 
			
		||||
      ar.serialize_varint(e);
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <template <bool> class Archive, class E0, class E1, class E2>
 | 
			
		||||
inline bool do_serialize(Archive<false>& ar, std::tuple<E0,E1,E2>& p)
 | 
			
		||||
{
 | 
			
		||||
  size_t cnt;
 | 
			
		||||
  ar.begin_array(cnt);
 | 
			
		||||
  if (!ar.good())
 | 
			
		||||
    return false;
 | 
			
		||||
  if (cnt != 3)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  if (!::serialization::detail::serialize_tuple_element(ar, std::get<0>(p)))
 | 
			
		||||
    return false;
 | 
			
		||||
  if (!ar.good())
 | 
			
		||||
    return false;
 | 
			
		||||
  ar.delimit_array();
 | 
			
		||||
  if (!::serialization::detail::serialize_tuple_element(ar, std::get<1>(p)))
 | 
			
		||||
    return false;
 | 
			
		||||
  if (!ar.good())
 | 
			
		||||
    return false;
 | 
			
		||||
  ar.delimit_array();
 | 
			
		||||
  if (!::serialization::detail::serialize_tuple_element(ar, std::get<2>(p)))
 | 
			
		||||
    return false;
 | 
			
		||||
  if (!ar.good())
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  ar.end_array();
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <template <bool> class Archive, class E0, class E1, class E2>
 | 
			
		||||
inline bool do_serialize(Archive<true>& ar, std::tuple<E0,E1,E2>& p)
 | 
			
		||||
{
 | 
			
		||||
  ar.begin_array(3);
 | 
			
		||||
  if (!ar.good())
 | 
			
		||||
    return false;
 | 
			
		||||
  if(!::serialization::detail::serialize_tuple_element(ar, std::get<0>(p)))
 | 
			
		||||
    return false;
 | 
			
		||||
  if (!ar.good())
 | 
			
		||||
    return false;
 | 
			
		||||
  ar.delimit_array();
 | 
			
		||||
  if(!::serialization::detail::serialize_tuple_element(ar, std::get<1>(p)))
 | 
			
		||||
    return false;
 | 
			
		||||
  if (!ar.good())
 | 
			
		||||
    return false;
 | 
			
		||||
  ar.delimit_array();
 | 
			
		||||
  if(!::serialization::detail::serialize_tuple_element(ar, std::get<2>(p)))
 | 
			
		||||
    return false;
 | 
			
		||||
  if (!ar.good())
 | 
			
		||||
    return false;
 | 
			
		||||
  ar.end_array();
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <template <bool> class Archive, class E0, class E1, class E2, class E3>
 | 
			
		||||
inline bool do_serialize(Archive<false>& ar, std::tuple<E0,E1,E2,E3>& p)
 | 
			
		||||
{
 | 
			
		||||
  size_t cnt;
 | 
			
		||||
  ar.begin_array(cnt);
 | 
			
		||||
  if (!ar.good())
 | 
			
		||||
    return false;
 | 
			
		||||
  if (cnt != 4)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  if (!::serialization::detail::serialize_tuple_element(ar, std::get<0>(p)))
 | 
			
		||||
    return false;
 | 
			
		||||
  if (!ar.good())
 | 
			
		||||
    return false;
 | 
			
		||||
  ar.delimit_array();
 | 
			
		||||
  if (!::serialization::detail::serialize_tuple_element(ar, std::get<1>(p)))
 | 
			
		||||
    return false;
 | 
			
		||||
  if (!ar.good())
 | 
			
		||||
    return false;
 | 
			
		||||
  ar.delimit_array();
 | 
			
		||||
  if (!::serialization::detail::serialize_tuple_element(ar, std::get<2>(p)))
 | 
			
		||||
    return false;
 | 
			
		||||
  if (!ar.good())
 | 
			
		||||
    return false;
 | 
			
		||||
  ar.delimit_array();
 | 
			
		||||
  if (!::serialization::detail::serialize_tuple_element(ar, std::get<3>(p)))
 | 
			
		||||
    return false;
 | 
			
		||||
  if (!ar.good())
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  ar.end_array();
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <template <bool> class Archive, class E0, class E1, class E2, class E3>
 | 
			
		||||
inline bool do_serialize(Archive<true>& ar, std::tuple<E0,E1,E2,E3>& p)
 | 
			
		||||
{
 | 
			
		||||
  ar.begin_array(4);
 | 
			
		||||
  if (!ar.good())
 | 
			
		||||
    return false;
 | 
			
		||||
  if(!::serialization::detail::serialize_tuple_element(ar, std::get<0>(p)))
 | 
			
		||||
    return false;
 | 
			
		||||
  if (!ar.good())
 | 
			
		||||
    return false;
 | 
			
		||||
  ar.delimit_array();
 | 
			
		||||
  if(!::serialization::detail::serialize_tuple_element(ar, std::get<1>(p)))
 | 
			
		||||
    return false;
 | 
			
		||||
  if (!ar.good())
 | 
			
		||||
    return false;
 | 
			
		||||
  ar.delimit_array();
 | 
			
		||||
  if(!::serialization::detail::serialize_tuple_element(ar, std::get<2>(p)))
 | 
			
		||||
    return false;
 | 
			
		||||
  if (!ar.good())
 | 
			
		||||
    return false;
 | 
			
		||||
  ar.delimit_array();
 | 
			
		||||
  if(!::serialization::detail::serialize_tuple_element(ar, std::get<3>(p)))
 | 
			
		||||
    return false;
 | 
			
		||||
  if (!ar.good())
 | 
			
		||||
    return false;
 | 
			
		||||
  ar.end_array();
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -7912,8 +7912,10 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes,
 | 
			
		|||
bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs)
 | 
			
		||||
{
 | 
			
		||||
  std::string extra_message;
 | 
			
		||||
  if (!txs.transfers.second.empty())
 | 
			
		||||
    extra_message = (boost::format("%u outputs to import. ") % (unsigned)txs.transfers.second.size()).str();
 | 
			
		||||
  if (!std::get<2>(txs.new_transfers).empty())
 | 
			
		||||
    extra_message = (boost::format("%u outputs to import. ") % (unsigned)std::get<2>(txs.new_transfers).size()).str();
 | 
			
		||||
  else if (!std::get<2>(txs.transfers).empty())
 | 
			
		||||
    extra_message = (boost::format("%u outputs to import. ") % (unsigned)std::get<2>(txs.transfers).size()).str();
 | 
			
		||||
  return accept_loaded_tx([&txs](){return txs.txes.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.txes[n];}, extra_message);
 | 
			
		||||
}
 | 
			
		||||
//----------------------------------------------------------------------------------------------------
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1146,8 +1146,8 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file
 | 
			
		|||
  
 | 
			
		||||
  // Check tx data and construct confirmation message
 | 
			
		||||
  std::string extra_message;
 | 
			
		||||
  if (!transaction->m_unsigned_tx_set.transfers.second.empty())
 | 
			
		||||
    extra_message = (boost::format("%u outputs to import. ") % (unsigned)transaction->m_unsigned_tx_set.transfers.second.size()).str();
 | 
			
		||||
  if (!std::get<2>(transaction->m_unsigned_tx_set.transfers).empty())
 | 
			
		||||
    extra_message = (boost::format("%u outputs to import. ") % (unsigned)std::get<2>(transaction->m_unsigned_tx_set.transfers).size()).str();
 | 
			
		||||
  transaction->checkLoadedTx([&transaction](){return transaction->m_unsigned_tx_set.txes.size();}, [&transaction](size_t n)->const tools::wallet2::tx_construction_data&{return transaction->m_unsigned_tx_set.txes[n];}, extra_message);
 | 
			
		||||
  setStatus(transaction->status(), transaction->errorString());
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6604,9 +6604,9 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s
 | 
			
		|||
//----------------------------------------------------------------------------------------------------
 | 
			
		||||
bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pending_tx> &txs, signed_tx_set &signed_txes)
 | 
			
		||||
{
 | 
			
		||||
  if (!exported_txs.new_transfers.second.empty())
 | 
			
		||||
  if (!std::get<2>(exported_txs.new_transfers).empty())
 | 
			
		||||
    import_outputs(exported_txs.new_transfers);
 | 
			
		||||
  else if (!exported_txs.transfers.second.empty())
 | 
			
		||||
  else if (!std::get<2>(exported_txs.transfers).empty())
 | 
			
		||||
    import_outputs(exported_txs.transfers);
 | 
			
		||||
 | 
			
		||||
  // sign the transactions
 | 
			
		||||
| 
						 | 
				
			
			@ -10800,7 +10800,7 @@ void wallet2::cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_
 | 
			
		|||
  {
 | 
			
		||||
    txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device()));
 | 
			
		||||
  }
 | 
			
		||||
  txs.transfers = std::make_pair(0, m_transfers);
 | 
			
		||||
  txs.transfers = std::make_tuple(0, m_transfers.size(), m_transfers);
 | 
			
		||||
 | 
			
		||||
  auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
 | 
			
		||||
  CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
 | 
			
		||||
| 
						 | 
				
			
			@ -13103,18 +13103,29 @@ void wallet2::import_blockchain(const std::tuple<size_t, crypto::hash, std::vect
 | 
			
		|||
  m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx);
 | 
			
		||||
}
 | 
			
		||||
//----------------------------------------------------------------------------------------------------
 | 
			
		||||
std::pair<uint64_t, std::vector<tools::wallet2::exported_transfer_details>> wallet2::export_outputs(bool all) const
 | 
			
		||||
std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::exported_transfer_details>> wallet2::export_outputs(bool all, uint32_t start, uint32_t count) const
 | 
			
		||||
{
 | 
			
		||||
  PERF_TIMER(export_outputs);
 | 
			
		||||
  std::vector<tools::wallet2::exported_transfer_details> outs;
 | 
			
		||||
 | 
			
		||||
  // invalid cases
 | 
			
		||||
  THROW_WALLET_EXCEPTION_IF(count == 0, error::wallet_internal_error, "Nothing requested");
 | 
			
		||||
  THROW_WALLET_EXCEPTION_IF(!all && start > 0, error::wallet_internal_error, "Incremental mode is incompatible with non-zero start");
 | 
			
		||||
 | 
			
		||||
  // valid cases:
 | 
			
		||||
  // all: all outputs, subject to start/count
 | 
			
		||||
  // !all: incremental, subject to count
 | 
			
		||||
  // for convenience, start/count are allowed to go past the valid range, then nothing is returned
 | 
			
		||||
 | 
			
		||||
  size_t offset = 0;
 | 
			
		||||
  if (!all)
 | 
			
		||||
    while (offset < m_transfers.size() && (m_transfers[offset].m_key_image_known && !m_transfers[offset].m_key_image_request))
 | 
			
		||||
      ++offset;
 | 
			
		||||
  else
 | 
			
		||||
    offset = start;
 | 
			
		||||
 | 
			
		||||
  outs.reserve(m_transfers.size() - offset);
 | 
			
		||||
  for (size_t n = offset; n < m_transfers.size(); ++n)
 | 
			
		||||
  for (size_t n = offset; n < m_transfers.size() && n - offset < count; ++n)
 | 
			
		||||
  {
 | 
			
		||||
    const transfer_details &td = m_transfers[n];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -13138,16 +13149,16 @@ std::pair<uint64_t, std::vector<tools::wallet2::exported_transfer_details>> wall
 | 
			
		|||
    outs.push_back(etd);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return std::make_pair(offset, outs);
 | 
			
		||||
  return std::make_tuple(offset, m_transfers.size(), outs);
 | 
			
		||||
}
 | 
			
		||||
//----------------------------------------------------------------------------------------------------
 | 
			
		||||
std::string wallet2::export_outputs_to_str(bool all) const
 | 
			
		||||
std::string wallet2::export_outputs_to_str(bool all, uint32_t start, uint32_t count) const
 | 
			
		||||
{
 | 
			
		||||
  PERF_TIMER(export_outputs_to_str);
 | 
			
		||||
 | 
			
		||||
  std::stringstream oss;
 | 
			
		||||
  binary_archive<true> ar(oss);
 | 
			
		||||
  auto outputs = export_outputs(all);
 | 
			
		||||
  auto outputs = export_outputs(all, start, count);
 | 
			
		||||
  THROW_WALLET_EXCEPTION_IF(!::serialization::serialize(ar, outputs), error::wallet_internal_error, "Failed to serialize output data");
 | 
			
		||||
 | 
			
		||||
  std::string magic(OUTPUT_EXPORT_FILE_MAGIC, strlen(OUTPUT_EXPORT_FILE_MAGIC));
 | 
			
		||||
| 
						 | 
				
			
			@ -13160,23 +13171,34 @@ std::string wallet2::export_outputs_to_str(bool all) const
 | 
			
		|||
  return magic + ciphertext;
 | 
			
		||||
}
 | 
			
		||||
//----------------------------------------------------------------------------------------------------
 | 
			
		||||
size_t wallet2::import_outputs(const std::pair<uint64_t, std::vector<tools::wallet2::transfer_details>> &outputs)
 | 
			
		||||
size_t wallet2::import_outputs(const std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::transfer_details>> &outputs)
 | 
			
		||||
{
 | 
			
		||||
  PERF_TIMER(import_outputs);
 | 
			
		||||
 | 
			
		||||
  THROW_WALLET_EXCEPTION_IF(!m_offline, error::wallet_internal_error, "Hot wallets cannot import outputs");
 | 
			
		||||
 | 
			
		||||
  THROW_WALLET_EXCEPTION_IF(outputs.first > m_transfers.size(), error::wallet_internal_error,
 | 
			
		||||
  // we can now import piecemeal
 | 
			
		||||
  const size_t offset = std::get<0>(outputs);
 | 
			
		||||
  const size_t num_outputs = std::get<1>(outputs);
 | 
			
		||||
  const std::vector<tools::wallet2::transfer_details> &output_array = std::get<2>(outputs);
 | 
			
		||||
 | 
			
		||||
  THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error,
 | 
			
		||||
      "Imported outputs omit more outputs that we know of");
 | 
			
		||||
 | 
			
		||||
  const size_t offset = outputs.first;
 | 
			
		||||
  THROW_WALLET_EXCEPTION_IF(offset >= num_outputs, error::wallet_internal_error,
 | 
			
		||||
      "Offset is larger than total outputs");
 | 
			
		||||
  THROW_WALLET_EXCEPTION_IF(output_array.size() > num_outputs - offset, error::wallet_internal_error,
 | 
			
		||||
      "Offset is larger than total outputs");
 | 
			
		||||
 | 
			
		||||
  const size_t original_size = m_transfers.size();
 | 
			
		||||
  m_transfers.resize(offset + outputs.second.size());
 | 
			
		||||
  for (size_t i = 0; i < offset; ++i)
 | 
			
		||||
    m_transfers[i].m_key_image_request = false;
 | 
			
		||||
  for (size_t i = 0; i < outputs.second.size(); ++i)
 | 
			
		||||
  if (offset + output_array.size() > m_transfers.size())
 | 
			
		||||
    m_transfers.resize(offset + output_array.size());
 | 
			
		||||
  else if (num_outputs < m_transfers.size())
 | 
			
		||||
    m_transfers.resize(num_outputs);
 | 
			
		||||
 | 
			
		||||
  for (size_t i = 0; i < output_array.size(); ++i)
 | 
			
		||||
  {
 | 
			
		||||
    transfer_details td = outputs.second[i];
 | 
			
		||||
    transfer_details td = output_array[i];
 | 
			
		||||
 | 
			
		||||
    // skip those we've already imported, or which have different data
 | 
			
		||||
    if (i + offset < original_size)
 | 
			
		||||
| 
						 | 
				
			
			@ -13228,23 +13250,34 @@ process:
 | 
			
		|||
  return m_transfers.size();
 | 
			
		||||
}
 | 
			
		||||
//----------------------------------------------------------------------------------------------------
 | 
			
		||||
size_t wallet2::import_outputs(const std::pair<uint64_t, std::vector<tools::wallet2::exported_transfer_details>> &outputs)
 | 
			
		||||
size_t wallet2::import_outputs(const std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::exported_transfer_details>> &outputs)
 | 
			
		||||
{
 | 
			
		||||
  PERF_TIMER(import_outputs);
 | 
			
		||||
 | 
			
		||||
  THROW_WALLET_EXCEPTION_IF(!m_offline, error::wallet_internal_error, "Hot wallets cannot import outputs");
 | 
			
		||||
 | 
			
		||||
  THROW_WALLET_EXCEPTION_IF(outputs.first > m_transfers.size(), error::wallet_internal_error,
 | 
			
		||||
  // we can now import piecemeal
 | 
			
		||||
  const size_t offset = std::get<0>(outputs);
 | 
			
		||||
  const size_t num_outputs = std::get<1>(outputs);
 | 
			
		||||
  const std::vector<tools::wallet2::exported_transfer_details> &output_array = std::get<2>(outputs);
 | 
			
		||||
 | 
			
		||||
  THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error,
 | 
			
		||||
      "Imported outputs omit more outputs that we know of. Try using export_outputs all.");
 | 
			
		||||
 | 
			
		||||
  const size_t offset = outputs.first;
 | 
			
		||||
  THROW_WALLET_EXCEPTION_IF(offset >= num_outputs, error::wallet_internal_error,
 | 
			
		||||
      "Offset is larger than total outputs");
 | 
			
		||||
  THROW_WALLET_EXCEPTION_IF(output_array.size() > num_outputs - offset, error::wallet_internal_error,
 | 
			
		||||
      "Offset is larger than total outputs");
 | 
			
		||||
 | 
			
		||||
  const size_t original_size = m_transfers.size();
 | 
			
		||||
  m_transfers.resize(offset + outputs.second.size());
 | 
			
		||||
  for (size_t i = 0; i < offset; ++i)
 | 
			
		||||
    m_transfers[i].m_key_image_request = false;
 | 
			
		||||
  for (size_t i = 0; i < outputs.second.size(); ++i)
 | 
			
		||||
  if (offset + output_array.size() > m_transfers.size())
 | 
			
		||||
    m_transfers.resize(offset + output_array.size());
 | 
			
		||||
  else if (num_outputs < m_transfers.size())
 | 
			
		||||
    m_transfers.resize(num_outputs);
 | 
			
		||||
 | 
			
		||||
  for (size_t i = 0; i < output_array.size(); ++i)
 | 
			
		||||
  {
 | 
			
		||||
    exported_transfer_details etd = outputs.second[i];
 | 
			
		||||
    exported_transfer_details etd = output_array[i];
 | 
			
		||||
    transfer_details &td = m_transfers[i + offset];
 | 
			
		||||
 | 
			
		||||
    // setup td with "cheap" loaded data
 | 
			
		||||
| 
						 | 
				
			
			@ -13358,7 +13391,7 @@ size_t wallet2::import_outputs_from_str(const std::string &outputs_st)
 | 
			
		|||
  {
 | 
			
		||||
    std::string body(data, headerlen);
 | 
			
		||||
 | 
			
		||||
    std::pair<uint64_t, std::vector<tools::wallet2::exported_transfer_details>> new_outputs;
 | 
			
		||||
    std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::exported_transfer_details>> new_outputs;
 | 
			
		||||
    try
 | 
			
		||||
    {
 | 
			
		||||
      binary_archive<false> ar{epee::strspan<std::uint8_t>(body)};
 | 
			
		||||
| 
						 | 
				
			
			@ -13368,9 +13401,9 @@ size_t wallet2::import_outputs_from_str(const std::string &outputs_st)
 | 
			
		|||
    }
 | 
			
		||||
    catch (...) {}
 | 
			
		||||
    if (!loaded)
 | 
			
		||||
      new_outputs.second.clear();
 | 
			
		||||
      std::get<2>(new_outputs).clear();
 | 
			
		||||
 | 
			
		||||
    std::pair<uint64_t, std::vector<tools::wallet2::transfer_details>> outputs;
 | 
			
		||||
    std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::transfer_details>> outputs;
 | 
			
		||||
    if (!loaded) try
 | 
			
		||||
    {
 | 
			
		||||
      binary_archive<false> ar{epee::strspan<std::uint8_t>(body)};
 | 
			
		||||
| 
						 | 
				
			
			@ -13395,11 +13428,12 @@ size_t wallet2::import_outputs_from_str(const std::string &outputs_st)
 | 
			
		|||
 | 
			
		||||
    if (!loaded)
 | 
			
		||||
    {
 | 
			
		||||
      outputs.first = 0;
 | 
			
		||||
      outputs.second = {};
 | 
			
		||||
      std::get<0>(outputs) = 0;
 | 
			
		||||
      std::get<1>(outputs) = 0;
 | 
			
		||||
      std::get<2>(outputs) = {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    imported_outputs = !new_outputs.second.empty() ? import_outputs(new_outputs) : !outputs.second.empty() ? import_outputs(outputs) : 0;
 | 
			
		||||
    imported_outputs = !std::get<2>(new_outputs).empty() ? import_outputs(new_outputs) : !std::get<2>(outputs).empty() ? import_outputs(outputs) : 0;
 | 
			
		||||
  }
 | 
			
		||||
  catch (const std::exception &e)
 | 
			
		||||
  {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -63,6 +63,7 @@
 | 
			
		|||
#include "serialization/crypto.h"
 | 
			
		||||
#include "serialization/string.h"
 | 
			
		||||
#include "serialization/pair.h"
 | 
			
		||||
#include "serialization/tuple.h"
 | 
			
		||||
#include "serialization/containers.h"
 | 
			
		||||
 | 
			
		||||
#include "wallet_errors.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -673,16 +674,32 @@ private:
 | 
			
		|||
    struct unsigned_tx_set
 | 
			
		||||
    {
 | 
			
		||||
      std::vector<tx_construction_data> txes;
 | 
			
		||||
      std::pair<size_t, wallet2::transfer_container> transfers;
 | 
			
		||||
      std::pair<size_t, std::vector<wallet2::exported_transfer_details>> new_transfers;
 | 
			
		||||
      std::tuple<uint64_t, uint64_t, wallet2::transfer_container> transfers;
 | 
			
		||||
      std::tuple<uint64_t, uint64_t, std::vector<wallet2::exported_transfer_details>> new_transfers;
 | 
			
		||||
 | 
			
		||||
      BEGIN_SERIALIZE_OBJECT()
 | 
			
		||||
        VERSION_FIELD(1)
 | 
			
		||||
        VERSION_FIELD(2)
 | 
			
		||||
        FIELD(txes)
 | 
			
		||||
        if (version >= 1)
 | 
			
		||||
          FIELD(new_transfers)
 | 
			
		||||
        else
 | 
			
		||||
          FIELD(transfers)
 | 
			
		||||
        if (version == 0)
 | 
			
		||||
        {
 | 
			
		||||
          std::pair<size_t, wallet2::transfer_container> v0_transfers;
 | 
			
		||||
          FIELD(v0_transfers);
 | 
			
		||||
          std::get<0>(transfers) = std::get<0>(v0_transfers);
 | 
			
		||||
          std::get<1>(transfers) = std::get<0>(v0_transfers) + std::get<1>(v0_transfers).size();
 | 
			
		||||
          std::get<2>(transfers) = std::get<1>(v0_transfers);
 | 
			
		||||
          return true;
 | 
			
		||||
        }
 | 
			
		||||
        if (version == 1)
 | 
			
		||||
        {
 | 
			
		||||
          std::pair<size_t, std::vector<wallet2::exported_transfer_details>> v1_transfers;
 | 
			
		||||
          FIELD(v1_transfers);
 | 
			
		||||
          std::get<0>(new_transfers) = std::get<0>(v1_transfers);
 | 
			
		||||
          std::get<1>(new_transfers) = std::get<0>(v1_transfers) + std::get<1>(v1_transfers).size();
 | 
			
		||||
          std::get<2>(new_transfers) = std::get<1>(v1_transfers);
 | 
			
		||||
          return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        FIELD(new_transfers)
 | 
			
		||||
      END_SERIALIZE()
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1453,10 +1470,10 @@ private:
 | 
			
		|||
    bool verify_with_public_key(const std::string &data, const crypto::public_key &public_key, const std::string &signature) const;
 | 
			
		||||
 | 
			
		||||
    // Import/Export wallet data
 | 
			
		||||
    std::pair<uint64_t, std::vector<tools::wallet2::exported_transfer_details>> export_outputs(bool all = false) const;
 | 
			
		||||
    std::string export_outputs_to_str(bool all = false) const;
 | 
			
		||||
    size_t import_outputs(const std::pair<uint64_t, std::vector<tools::wallet2::exported_transfer_details>> &outputs);
 | 
			
		||||
    size_t import_outputs(const std::pair<uint64_t, std::vector<tools::wallet2::transfer_details>> &outputs);
 | 
			
		||||
    std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::exported_transfer_details>> export_outputs(bool all = false, uint32_t start = 0, uint32_t count = 0xffffffff) const;
 | 
			
		||||
    std::string export_outputs_to_str(bool all = false, uint32_t start = 0, uint32_t count = 0) const;
 | 
			
		||||
    size_t import_outputs(const std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::exported_transfer_details>> &outputs);
 | 
			
		||||
    size_t import_outputs(const std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::transfer_details>> &outputs);
 | 
			
		||||
    size_t import_outputs_from_str(const std::string &outputs_st);
 | 
			
		||||
    payment_container export_payments() const;
 | 
			
		||||
    void import_payments(const payment_container &payments);
 | 
			
		||||
| 
						 | 
				
			
			@ -1904,7 +1921,7 @@ BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 8)
 | 
			
		|||
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 6)
 | 
			
		||||
BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 18)
 | 
			
		||||
BOOST_CLASS_VERSION(tools::wallet2::reserve_proof_entry, 0)
 | 
			
		||||
BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 0)
 | 
			
		||||
BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 1)
 | 
			
		||||
BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 1)
 | 
			
		||||
BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 4)
 | 
			
		||||
BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 3)
 | 
			
		||||
| 
						 | 
				
			
			@ -1914,6 +1931,17 @@ namespace boost
 | 
			
		|||
{
 | 
			
		||||
  namespace serialization
 | 
			
		||||
  {
 | 
			
		||||
    template<class Archive, class F, class S, class T>
 | 
			
		||||
    inline void serialize(
 | 
			
		||||
        Archive & ar,
 | 
			
		||||
        std::tuple<F, S, T> & t,
 | 
			
		||||
        const unsigned int /* file_version */
 | 
			
		||||
    ){
 | 
			
		||||
        ar & boost::serialization::make_nvp("f", std::get<0>(t));
 | 
			
		||||
        ar & boost::serialization::make_nvp("s", std::get<1>(t));
 | 
			
		||||
        ar & boost::serialization::make_nvp("t", std::get<2>(t));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <class Archive>
 | 
			
		||||
    inline typename std::enable_if<!Archive::is_loading::value, void>::type initialize_transfer_details(Archive &a, tools::wallet2::transfer_details &x, const boost::serialization::version_type ver)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -2274,7 +2302,17 @@ namespace boost
 | 
			
		|||
    inline void serialize(Archive &a, tools::wallet2::unsigned_tx_set &x, const boost::serialization::version_type ver)
 | 
			
		||||
    {
 | 
			
		||||
      a & x.txes;
 | 
			
		||||
      a & x.transfers;
 | 
			
		||||
      if (ver == 0)
 | 
			
		||||
      {
 | 
			
		||||
        // load old version
 | 
			
		||||
        std::pair<size_t, tools::wallet2::transfer_container> old_transfers;
 | 
			
		||||
        a & old_transfers;
 | 
			
		||||
        std::get<0>(x.transfers) = std::get<0>(old_transfers);
 | 
			
		||||
        std::get<1>(x.transfers) = std::get<0>(old_transfers) + std::get<1>(old_transfers).size();
 | 
			
		||||
        std::get<2>(x.transfers) = std::get<1>(old_transfers);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      throw std::runtime_error("Boost serialization not supported for newest unsigned_tx_set");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <class Archive>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2792,7 +2792,7 @@ namespace tools
 | 
			
		|||
 | 
			
		||||
    try
 | 
			
		||||
    {
 | 
			
		||||
      res.outputs_data_hex = epee::string_tools::buff_to_hex_nodelimer(m_wallet->export_outputs_to_str(req.all));
 | 
			
		||||
      res.outputs_data_hex = epee::string_tools::buff_to_hex_nodelimer(m_wallet->export_outputs_to_str(req.all, req.start, req.count));
 | 
			
		||||
    }
 | 
			
		||||
    catch (const std::exception &e)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1785,9 +1785,13 @@ namespace wallet_rpc
 | 
			
		|||
    struct request_t
 | 
			
		||||
    {
 | 
			
		||||
      bool all;
 | 
			
		||||
      uint32_t start;
 | 
			
		||||
      uint32_t count;
 | 
			
		||||
 | 
			
		||||
      BEGIN_KV_SERIALIZE_MAP()
 | 
			
		||||
        KV_SERIALIZE(all)
 | 
			
		||||
        KV_SERIALIZE_OPT(start, 0u)
 | 
			
		||||
        KV_SERIALIZE_OPT(count, 0xffffffffu)
 | 
			
		||||
      END_KV_SERIALIZE_MAP()
 | 
			
		||||
    };
 | 
			
		||||
    typedef epee::misc_utils::struct_init<request_t> request;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											Binary file not shown.
										
									
								
							| 
						 | 
				
			
			@ -34,6 +34,7 @@
 | 
			
		|||
from __future__ import print_function
 | 
			
		||||
from framework.daemon import Daemon
 | 
			
		||||
from framework.wallet import Wallet
 | 
			
		||||
import random
 | 
			
		||||
 | 
			
		||||
SEED = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted'
 | 
			
		||||
STANDARD_ADDRESS = '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
 | 
			
		||||
| 
						 | 
				
			
			@ -44,8 +45,10 @@ class ColdSigningTest():
 | 
			
		|||
        self.reset()
 | 
			
		||||
        self.create(0)
 | 
			
		||||
        self.mine()
 | 
			
		||||
        self.transfer()
 | 
			
		||||
        self.self_transfer_to_subaddress()
 | 
			
		||||
        for piecemeal_output_export in [False, True]:
 | 
			
		||||
            self.transfer(piecemeal_output_export)
 | 
			
		||||
        for piecemeal_output_export in [False, True]:
 | 
			
		||||
            self.self_transfer_to_subaddress(piecemeal_output_export)
 | 
			
		||||
        self.transfer_after_empty_export_import()
 | 
			
		||||
 | 
			
		||||
    def reset(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -93,19 +96,42 @@ class ColdSigningTest():
 | 
			
		|||
        daemon.generateblocks(STANDARD_ADDRESS, 80)
 | 
			
		||||
        wallet.refresh()
 | 
			
		||||
 | 
			
		||||
    def export_import(self):
 | 
			
		||||
    def export_import(self, piecemeal_output_export):
 | 
			
		||||
        self.hot_wallet.refresh()
 | 
			
		||||
        res = self.hot_wallet.export_outputs()
 | 
			
		||||
        self.cold_wallet.import_outputs(res.outputs_data_hex)
 | 
			
		||||
 | 
			
		||||
        if piecemeal_output_export:
 | 
			
		||||
            res = self.hot_wallet.incoming_transfers()
 | 
			
		||||
            num_outputs = len(res.transfers)
 | 
			
		||||
            done = [False] * num_outputs
 | 
			
		||||
            while len([x for x in done if not done[x]]) > 0:
 | 
			
		||||
                start = int(random.random() * num_outputs)
 | 
			
		||||
                if start == num_outputs:
 | 
			
		||||
                    num_outputs -= 1
 | 
			
		||||
                count = 1 + int(random.random() * 5)
 | 
			
		||||
                res = self.hot_wallet.export_outputs(all = True, start = start, count = count)
 | 
			
		||||
                try:
 | 
			
		||||
                    self.cold_wallet.import_outputs(res.outputs_data_hex)
 | 
			
		||||
                except Exception as e:
 | 
			
		||||
                    # this just means we selected later outputs first, without filling
 | 
			
		||||
                    # new outputs first
 | 
			
		||||
                    if 'Imported outputs omit more outputs that we know of' not in str(e):
 | 
			
		||||
                        raise
 | 
			
		||||
                for i in range(start, start + count):
 | 
			
		||||
                    if i < len(done):
 | 
			
		||||
                        done[i] = True
 | 
			
		||||
        else:
 | 
			
		||||
            res = self.hot_wallet.export_outputs()
 | 
			
		||||
            self.cold_wallet.import_outputs(res.outputs_data_hex)
 | 
			
		||||
 | 
			
		||||
        res = self.cold_wallet.export_key_images(True)
 | 
			
		||||
        self.hot_wallet.import_key_images(res.signed_key_images, offset = res.offset)
 | 
			
		||||
 | 
			
		||||
    def create_tx(self, destination_addr):
 | 
			
		||||
    def create_tx(self, destination_addr, piecemeal_output_export):
 | 
			
		||||
        daemon = Daemon()
 | 
			
		||||
 | 
			
		||||
        dst = {'address': destination_addr, 'amount': 1000000000000}
 | 
			
		||||
 | 
			
		||||
        self.export_import()
 | 
			
		||||
        self.export_import(piecemeal_output_export)
 | 
			
		||||
 | 
			
		||||
        res = self.hot_wallet.transfer([dst], ring_size = 16, get_tx_key = False)
 | 
			
		||||
        assert len(res.tx_hash) == 32*2
 | 
			
		||||
| 
						 | 
				
			
			@ -166,11 +192,11 @@ class ColdSigningTest():
 | 
			
		|||
        res = self.cold_wallet.get_tx_key(txid)
 | 
			
		||||
        assert len(res.tx_key) == 64
 | 
			
		||||
 | 
			
		||||
        self.export_import()
 | 
			
		||||
        self.export_import(piecemeal_output_export)
 | 
			
		||||
 | 
			
		||||
    def transfer(self):
 | 
			
		||||
    def transfer(self, piecemeal_output_export):
 | 
			
		||||
        print("Creating transaction in hot wallet")
 | 
			
		||||
        self.create_tx(STANDARD_ADDRESS)
 | 
			
		||||
        self.create_tx(STANDARD_ADDRESS, piecemeal_output_export)
 | 
			
		||||
 | 
			
		||||
        res = self.cold_wallet.get_address()
 | 
			
		||||
        assert len(res['addresses']) == 1
 | 
			
		||||
| 
						 | 
				
			
			@ -182,9 +208,9 @@ class ColdSigningTest():
 | 
			
		|||
        assert res['addresses'][0].address == STANDARD_ADDRESS
 | 
			
		||||
        assert res['addresses'][0].used
 | 
			
		||||
 | 
			
		||||
    def self_transfer_to_subaddress(self):
 | 
			
		||||
    def self_transfer_to_subaddress(self, piecemeal_output_export):
 | 
			
		||||
        print("Self-spending to subaddress in hot wallet")
 | 
			
		||||
        self.create_tx(SUBADDRESS)
 | 
			
		||||
        self.create_tx(SUBADDRESS, piecemeal_output_export)
 | 
			
		||||
 | 
			
		||||
        res = self.cold_wallet.get_address()
 | 
			
		||||
        assert len(res['addresses']) == 2
 | 
			
		||||
| 
						 | 
				
			
			@ -203,9 +229,9 @@ class ColdSigningTest():
 | 
			
		|||
    def transfer_after_empty_export_import(self):
 | 
			
		||||
        print("Creating transaction in hot wallet after empty export & import")
 | 
			
		||||
        start_len = len(self.hot_wallet.get_transfers()['in'])
 | 
			
		||||
        self.export_import()
 | 
			
		||||
        self.export_import(False)
 | 
			
		||||
        assert start_len == len(self.hot_wallet.get_transfers()['in'])
 | 
			
		||||
        self.create_tx(STANDARD_ADDRESS)
 | 
			
		||||
        self.create_tx(STANDARD_ADDRESS, False)
 | 
			
		||||
        assert start_len == len(self.hot_wallet.get_transfers()['in']) - 1
 | 
			
		||||
 | 
			
		||||
class Guard:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,7 +50,7 @@ BEGIN_INIT_SIMPLE_FUZZER()
 | 
			
		|||
END_INIT_SIMPLE_FUZZER()
 | 
			
		||||
 | 
			
		||||
BEGIN_SIMPLE_FUZZER()
 | 
			
		||||
  std::pair<uint64_t, std::vector<tools::wallet2::transfer_details>> outputs;
 | 
			
		||||
  std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::transfer_details>> outputs;
 | 
			
		||||
  binary_archive<false> ar{{buf, len}};
 | 
			
		||||
  ::serialization::serialize(ar, outputs);
 | 
			
		||||
  size_t n_outputs = wallet->import_outputs(outputs);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -763,10 +763,13 @@ class Wallet(object):
 | 
			
		|||
        }
 | 
			
		||||
        return self.rpc.send_json_rpc_request(get_languages)
 | 
			
		||||
 | 
			
		||||
    def export_outputs(self):
 | 
			
		||||
    def export_outputs(self, all = False, start = 0, count = 0xffffffff):
 | 
			
		||||
        export_outputs = {
 | 
			
		||||
            'method': 'export_outputs',
 | 
			
		||||
            'params': {
 | 
			
		||||
                'all': all,
 | 
			
		||||
                'start': start,
 | 
			
		||||
                'count': count,
 | 
			
		||||
            },
 | 
			
		||||
            'jsonrpc': '2.0', 
 | 
			
		||||
            'id': '0'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue