Added pw_vout_object, pw_vout_manager and tests for pw_vout_manager

This commit is contained in:
Alexander Suslikov 2019-01-18 14:13:49 +03:00 committed by Anzhy Cherrnyavski
parent 0c42d7ea6f
commit 67486275c1
12 changed files with 372 additions and 22 deletions

View file

@ -51,6 +51,7 @@
#include <graphene/chain/game_object.hpp>
#include <graphene/chain/info_for_vout_object.hpp>
#include <graphene/chain/bitcoin_address_object.hpp>
#include <graphene/chain/primary_wallet_vout_object.hpp>
#include <graphene/chain/sport_object.hpp>
@ -311,6 +312,7 @@ void database::initialize_indexes()
add_index< primary_index<info_for_vout_index > >();
add_index< primary_index<bitcoin_address_index > >();
add_index< primary_index<primary_wallet_vout_index > >();
}
void database::init_genesis(const genesis_state_type& genesis_state)

View file

@ -226,3 +226,6 @@
#define TOURNAMENT_MAX_WHITELIST_LENGTH 1000
#define TOURNAMENT_MAX_START_TIME_IN_FUTURE (60*60*24*7*4) // 1 month
#define TOURNAMENT_MAX_START_DELAY (60*60*24*7) // 1 week
#define SIDECHAIN_NUMBER_UNCONFIRMED_VINS 25

View file

@ -0,0 +1,44 @@
#pragma once
#include <graphene/db/generic_index.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <sidechain/types.hpp>
namespace graphene { namespace chain {
class primary_wallet_vout_object : public abstract_object<primary_wallet_vout_object>
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = primary_wallet_vout_object_type;
struct comparer {
bool operator()(const primary_wallet_vout_object& lhs, const primary_wallet_vout_object& rhs) const;
};
primary_wallet_vout_id_type get_id() const { return id; }
sidechain::prev_out vout;
fc::uint256 hash_id; // sha256(hash + n_vout)
bool confirmed;
bool used;
};
struct by_hash_id;
typedef boost::multi_index_container<
primary_wallet_vout_object,
indexed_by<
ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >,
ordered_unique< tag< by_hash_id >, member< primary_wallet_vout_object, fc::uint256, &primary_wallet_vout_object::hash_id > >
>
> primary_wallet_vout_multi_index_container;
typedef generic_index<primary_wallet_vout_object, primary_wallet_vout_multi_index_container> primary_wallet_vout_index;
} }
FC_REFLECT_DERIVED( graphene::chain::primary_wallet_vout_object, (graphene::chain::object), (vout)(hash_id)(confirmed)(used) )

View file

@ -147,6 +147,7 @@ namespace graphene { namespace chain {
bet_object_type,
info_for_vout_object_type,
bitcoin_address_object_type,
primary_wallet_vout_object_type,
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
};
@ -206,6 +207,7 @@ namespace graphene { namespace chain {
class bet_object;
class info_for_vout_object;
class bitcoin_address_object;
class primary_wallet_vout_object;
typedef object_id< protocol_ids, account_object_type, account_object> account_id_type;
typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type;
@ -234,6 +236,7 @@ namespace graphene { namespace chain {
typedef object_id< protocol_ids, bet_object_type, bet_object> bet_id_type;
typedef object_id< protocol_ids, info_for_vout_object_type, info_for_vout_object> info_for_vout_id_type;
typedef object_id< protocol_ids, bitcoin_address_object_type, bitcoin_address_object> bitcoin_address_id_type;
typedef object_id< protocol_ids, primary_wallet_vout_object_type,primary_wallet_vout_object> primary_wallet_vout_id_type;
// implementation types
class global_property_object;
@ -410,6 +413,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
(bet_object_type)
(info_for_vout_object_type)
(bitcoin_address_object_type)
(primary_wallet_vout_object_type)
(OBJECT_TYPE_COUNT)
)
FC_REFLECT_ENUM( graphene::chain::impl_object_type,
@ -463,6 +467,7 @@ FC_REFLECT_TYPENAME( graphene::chain::bet_id_type )
FC_REFLECT_TYPENAME( graphene::chain::tournament_id_type )
FC_REFLECT_TYPENAME( graphene::chain::info_for_vout_id_type )
FC_REFLECT_TYPENAME( graphene::chain::bitcoin_address_id_type )
FC_REFLECT_TYPENAME( graphene::chain::primary_wallet_vout_id_type )
FC_REFLECT_TYPENAME( graphene::chain::global_property_id_type )
FC_REFLECT_TYPENAME( graphene::chain::dynamic_global_property_id_type )
FC_REFLECT_TYPENAME( graphene::chain::asset_dynamic_data_id_type )

View file

@ -19,13 +19,6 @@ namespace graphene { namespace chain { class database; } }
namespace sidechain {
struct prev_out
{
std::string hash_tx;
uint32_t n_vout;
uint64_t amount;
};
struct info_for_vin
{
info_for_vin() = default;

View file

@ -0,0 +1,36 @@
#pragma once
#include <graphene/chain/database.hpp>
#include <graphene/chain/primary_wallet_vout_object.hpp>
#include <fc/crypto/sha256.hpp>
namespace sidechain {
class primary_wallet_vout_manager
{
public:
primary_wallet_vout_manager( graphene::chain::database& _db ) : db( _db ) {}
bool is_reach_max_unconfirmaed_vout() const;
fc::optional< graphene::chain::primary_wallet_vout_object > get_latest_unused_vout() const;
void create_new_vout( const sidechain::prev_out& out );
void delete_vout_with_newer( fc::uint256 hash_id );
void confirm_vout( fc::uint256 hash_id );
void use_latest_vout( fc::uint256 hash_id );
private:
fc::optional< graphene::chain::primary_wallet_vout_id_type > get_vout_id( fc::uint256 hash_id ) const;
graphene::chain::database& db;
};
}

View file

@ -12,7 +12,8 @@ namespace sidechain {
using bytes = std::vector<char>;
using accounts_keys = std::map< graphene::chain::account_id_type, graphene::chain::public_key_type >;
enum class payment_type {
enum class payment_type
{
NULLDATA,
P2PK,
P2PKH,
@ -23,6 +24,14 @@ enum class payment_type {
P2SH_WSH
};
struct prev_out
{
std::string hash_tx;
uint32_t n_vout;
uint64_t amount;
};
}
FC_REFLECT_ENUM( sidechain::payment_type, (NULLDATA)(P2PK)(P2PKH)(P2SH)(P2WPKH)(P2WSH)(P2SH_WPKH)(P2SH_WSH) );
FC_REFLECT( sidechain::prev_out, (hash_tx)(n_vout)(amount) );

View file

@ -0,0 +1,97 @@
#include <sidechain/primary_wallet_vout_manager.hpp>
#include <graphene/chain/config.hpp>
namespace sidechain {
bool primary_wallet_vout_manager::is_reach_max_unconfirmaed_vout() const
{
const auto& PW_vout_idx = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
return !( PW_vout_idx.size() < SIDECHAIN_NUMBER_UNCONFIRMED_VINS );
}
fc::optional< graphene::chain::primary_wallet_vout_object > primary_wallet_vout_manager::get_latest_unused_vout() const
{
const auto& PW_vout_idx = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
auto itr = PW_vout_idx.end();
FC_ASSERT( itr != PW_vout_idx.begin() );
itr--;
if( itr->used )
return fc::optional< graphene::chain::primary_wallet_vout_object >();
return fc::optional< graphene::chain::primary_wallet_vout_object > (*itr);
}
void primary_wallet_vout_manager::create_new_vout( const sidechain::prev_out& out )
{
db.create<graphene::chain::primary_wallet_vout_object>([&]( graphene::chain::primary_wallet_vout_object& obj ) {
obj.vout = out;
obj.hash_id = fc::sha256::hash( out.hash_tx + std::to_string( out.n_vout ) );
obj.confirmed = false;
obj.used = false;
});
}
void primary_wallet_vout_manager::delete_vout_with_newer( fc::uint256 hash_id )
{
const auto& PW_vout_by_id = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
auto vout_id = get_vout_id( hash_id );
if( !vout_id.valid() )
return;
auto itr = PW_vout_by_id.find( *vout_id );
while( itr != PW_vout_by_id.end() )
{
auto temp = itr;
itr++;
db.remove( *temp );
}
}
void primary_wallet_vout_manager::confirm_vout( fc::uint256 hash_id )
{
const auto& PW_vout_by_id = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
auto vout_id = get_vout_id( hash_id );
FC_ASSERT( vout_id.valid() );
auto itr = PW_vout_by_id.find( *vout_id );
db.modify(*itr, [&]( graphene::chain::primary_wallet_vout_object& PW_vout ) {
PW_vout.confirmed = true;
});
if( itr != PW_vout_by_id.begin() ){
itr--;
FC_ASSERT( itr->confirmed == true );
db.remove(*itr);
}
}
void primary_wallet_vout_manager::use_latest_vout( fc::uint256 hash_id )
{
const auto& PW_vout_by_id = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
auto vout_id = get_vout_id( hash_id );
FC_ASSERT( vout_id.valid() );
auto itr = PW_vout_by_id.find( *vout_id );
db.modify(*itr, [&]( graphene::chain::primary_wallet_vout_object& PW_vout ) {
PW_vout.used = true;
});
if( itr != PW_vout_by_id.begin() ){
itr--;
FC_ASSERT( itr->used == true );
}
}
fc::optional< graphene::chain::primary_wallet_vout_id_type > primary_wallet_vout_manager::get_vout_id( fc::uint256 hash_id ) const
{
const auto& PW_vout_by_hash_id = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_hash_id >();
const auto& itr_hash_id = PW_vout_by_hash_id.find( hash_id );
if( itr_hash_id == PW_vout_by_hash_id.end() )
return fc::optional< graphene::chain::primary_wallet_vout_id_type >();
return fc::optional< graphene::chain::primary_wallet_vout_id_type >( itr_hash_id->get_id() );
}
}

View file

@ -9,7 +9,8 @@ using namespace graphene::chain;
BOOST_FIXTURE_TEST_SUITE( bitcoin_addresses_obj_tests, database_fixture )
BOOST_AUTO_TEST_CASE( create_bitcoin_address_test ) {
BOOST_AUTO_TEST_CASE( create_bitcoin_address_test )
{
transaction_evaluation_state context(&db);
bitcoin_address_create_operation op;

View file

@ -93,7 +93,8 @@ BOOST_AUTO_TEST_CASE( addresses_raw_test )
BOOST_CHECK( bitcoin_address( p2sh_testnet ).get_raw_address() == standard_p2sh_testnet );
}
BOOST_AUTO_TEST_CASE( create_multisig_address_test ) {
BOOST_AUTO_TEST_CASE( create_multisig_address_test )
{
std::vector<char> public_key1 = parse_hex( "03db643710666b862e0a97f7edbe8ef40ec2c4a29ef995c431c21ca85e35000010" );
std::vector<char> public_key2 = parse_hex( "0320000d982c156a6f09df8c7674abddc2bb326533268ed03572916221b4417983" );
@ -112,7 +113,8 @@ BOOST_AUTO_TEST_CASE( create_multisig_address_test ) {
BOOST_CHECK( redeem_script == cma.redeem_script );
}
BOOST_AUTO_TEST_CASE( create_segwit_address_test ) {
BOOST_AUTO_TEST_CASE( create_segwit_address_test )
{
// https://0bin.net/paste/nfnSf0HcBqBUGDto#7zJMRUhGEBkyh-eASQPEwKfNHgQ4D5KrUJRsk8MTPSa
std::vector<char> public_key1 = parse_hex( "03b3623117e988b76aaabe3d63f56a4fc88b228a71e64c4cc551d1204822fe85cb" );
std::vector<char> public_key2 = parse_hex( "03dd823066e096f72ed617a41d3ca56717db335b1ea47a1b4c5c9dbdd0963acba6" );

View file

@ -8,7 +8,8 @@ using namespace sidechain;
BOOST_FIXTURE_TEST_SUITE( input_withdrawal_info_tests, database_fixture )
BOOST_AUTO_TEST_CASE( input_withdrawal_info_insert_vin_test ) {
BOOST_AUTO_TEST_CASE( input_withdrawal_info_insert_vin_test )
{
input_withdrawal_info infos( db );
prev_out out = { "1", 1, 13 };
infos.insert_info_for_vin( out, "addr1", { 0x01, 0x02, 0x03 } );
@ -17,7 +18,8 @@ BOOST_AUTO_TEST_CASE( input_withdrawal_info_insert_vin_test ) {
info_for_vin::count_id_info_for_vin = 0;
}
BOOST_AUTO_TEST_CASE( input_withdrawal_info_many_insert_vin_test ) {
BOOST_AUTO_TEST_CASE( input_withdrawal_info_many_insert_vin_test )
{
input_withdrawal_info infos( db );
for( size_t i = 1; i <= 10; i++ ) {
prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
@ -28,7 +30,8 @@ BOOST_AUTO_TEST_CASE( input_withdrawal_info_many_insert_vin_test ) {
info_for_vin::count_id_info_for_vin = 0;
}
BOOST_AUTO_TEST_CASE( input_withdrawal_info_id_test ) {
BOOST_AUTO_TEST_CASE( input_withdrawal_info_id_test )
{
input_withdrawal_info infos( db );
for( size_t i = 0; i < 10; i++ ) {
prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
@ -40,7 +43,8 @@ BOOST_AUTO_TEST_CASE( input_withdrawal_info_id_test ) {
info_for_vin::count_id_info_for_vin = 0;
}
BOOST_AUTO_TEST_CASE( input_withdrawal_info_check_data_test ) {
BOOST_AUTO_TEST_CASE( input_withdrawal_info_check_data_test )
{
input_withdrawal_info infos( db );
for( size_t i = 0; i < 10; i++ ) {
prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
@ -61,7 +65,8 @@ BOOST_AUTO_TEST_CASE( input_withdrawal_info_check_data_test ) {
info_for_vin::count_id_info_for_vin = 0;
}
BOOST_AUTO_TEST_CASE( input_withdrawal_info_modify_test ) {
BOOST_AUTO_TEST_CASE( input_withdrawal_info_modify_test )
{
input_withdrawal_info infos( db );
for( size_t i = 0; i < 10; i++ ) {
prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
@ -97,7 +102,8 @@ BOOST_AUTO_TEST_CASE( input_withdrawal_info_modify_test ) {
info_for_vin::count_id_info_for_vin = 0;
}
BOOST_AUTO_TEST_CASE( input_withdrawal_info_remove_vin_test ) {
BOOST_AUTO_TEST_CASE( input_withdrawal_info_remove_vin_test )
{
input_withdrawal_info infos( db );
for( size_t i = 0; i < 10; i++ ) {
prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
@ -123,7 +129,8 @@ BOOST_AUTO_TEST_CASE( input_withdrawal_info_remove_vin_test ) {
info_for_vin::count_id_info_for_vin = 0;
}
BOOST_AUTO_TEST_CASE( input_withdrawal_info_get_info_for_vins_test ) {
BOOST_AUTO_TEST_CASE( input_withdrawal_info_get_info_for_vins_test )
{
input_withdrawal_info infos( db );
for( size_t i = 0; i < 10; i++ ) {
prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
@ -144,13 +151,15 @@ BOOST_AUTO_TEST_CASE( input_withdrawal_info_get_info_for_vins_test ) {
info_for_vin::count_id_info_for_vin = 0;
}
BOOST_AUTO_TEST_CASE( input_withdrawal_info_insert_vout_test ) {
BOOST_AUTO_TEST_CASE( input_withdrawal_info_insert_vout_test )
{
input_withdrawal_info infos( db );
infos.insert_info_for_vout( account_id_type(), payment_type::NULLDATA, "1", 1 );
BOOST_CHECK( infos.size_info_for_vouts() == 1 );
}
BOOST_AUTO_TEST_CASE( input_withdrawal_info_many_insert_vout_test ) {
BOOST_AUTO_TEST_CASE( input_withdrawal_info_many_insert_vout_test )
{
input_withdrawal_info infos( db );
for( size_t i = 1; i <= 10; i++ ) {
infos.insert_info_for_vout( account_id_type(i), payment_type::NULLDATA, std::to_string( i ), static_cast<uint64_t>( i ) );
@ -158,7 +167,8 @@ BOOST_AUTO_TEST_CASE( input_withdrawal_info_many_insert_vout_test ) {
BOOST_CHECK( infos.size_info_for_vouts() == 10 );
}
BOOST_AUTO_TEST_CASE( input_withdrawal_info_remove_vout_test ) {
BOOST_AUTO_TEST_CASE( input_withdrawal_info_remove_vout_test )
{
input_withdrawal_info infos( db );
for( size_t i = 0; i < 10; i++ ) {
infos.insert_info_for_vout( account_id_type(i), payment_type::NULLDATA, std::to_string( i ), static_cast<uint64_t>( i ) );
@ -181,7 +191,8 @@ BOOST_AUTO_TEST_CASE( input_withdrawal_info_remove_vout_test ) {
}
}
BOOST_AUTO_TEST_CASE( input_withdrawal_info_get_info_for_vouts_test ) {
BOOST_AUTO_TEST_CASE( input_withdrawal_info_get_info_for_vouts_test )
{
input_withdrawal_info infos( db );
for( size_t i = 0; i < 10; i++ ) {
infos.insert_info_for_vout( account_id_type(i), payment_type::NULLDATA, std::to_string( i ), static_cast<uint64_t>( i ) );

View file

@ -0,0 +1,147 @@
#include <boost/test/unit_test.hpp>
#include <sidechain/primary_wallet_vout_manager.hpp>
#include "../common/database_fixture.hpp"
#include <sidechain/types.hpp>
using namespace graphene::chain;
using namespace sidechain;
BOOST_FIXTURE_TEST_SUITE( primary_wallet_vout_manager_tests, database_fixture )
void create_primary_wallet_vouts( primary_wallet_vout_manager& pw_vout_manager, const graphene::chain::database& db, uint32_t num )
{
uint64_t start_pos = 0;
const auto& idx = db.get_index_type<primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
if( idx.begin() != idx.end() ){
auto last_itr = --idx.end();
start_pos = last_itr->id.instance() + 1;
}
for( uint64_t i = start_pos; i < start_pos + num; i++ ) {
prev_out out = { std::to_string(i), i, i };
pw_vout_manager.create_new_vout( out );
}
}
BOOST_AUTO_TEST_CASE( check_max_pw_vout_objects )
{
const auto& idx = db.get_index_type<primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
primary_wallet_vout_manager pw_vout_manager( db );
create_primary_wallet_vouts( pw_vout_manager, db, 1 );
BOOST_CHECK( idx.size() == 1 );
create_primary_wallet_vouts( pw_vout_manager, db, 24 );
BOOST_CHECK( idx.size() == 25 );
BOOST_CHECK( pw_vout_manager.is_reach_max_unconfirmaed_vout() == true );
}
BOOST_AUTO_TEST_CASE( check_pw_vout_objects_chain )
{
const auto& idx = db.get_index_type<primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
primary_wallet_vout_manager pw_vout_manager( db );
create_primary_wallet_vouts( pw_vout_manager, db, 24 );
auto itr = idx.begin();
for( size_t i = 0; i < idx.size(); i++ ) {
BOOST_CHECK( itr->hash_id == fc::sha256::hash( std::to_string( i ) + std::to_string( i )));
itr++;
}
BOOST_CHECK( pw_vout_manager.get_latest_unused_vout().valid() );
auto pw_vout = *pw_vout_manager.get_latest_unused_vout();
BOOST_CHECK( pw_vout.vout.hash_tx == "23" );
BOOST_CHECK( pw_vout.vout.n_vout == 23 );
BOOST_CHECK( pw_vout.hash_id == fc::sha256::hash( "23" + std::to_string( 23 )));
}
BOOST_AUTO_TEST_CASE( delete_pw_vout_objects )
{
const auto& idx = db.get_index_type<primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
primary_wallet_vout_manager pw_vout_manager( db );
create_primary_wallet_vouts( pw_vout_manager, db, 24 );
BOOST_CHECK( idx.size() == 24 );
pw_vout_manager.delete_vout_with_newer( fc::sha256::hash( "123" + std::to_string( 123 )));
BOOST_CHECK( idx.size() == 24 );
pw_vout_manager.delete_vout_with_newer( fc::sha256::hash( "13" + std::to_string( 13 )));
BOOST_CHECK( idx.size() == 13 );
auto itr = idx.begin();
for( size_t i = 0; i < idx.size(); i++ ) {
BOOST_CHECK( itr->hash_id == fc::sha256::hash( std::to_string( i ) + std::to_string( i )));
itr++;
}
create_primary_wallet_vouts( pw_vout_manager, db, 8 );
pw_vout_manager.delete_vout_with_newer( fc::sha256::hash( "20" + std::to_string( 20 )));
BOOST_CHECK( idx.size() == 20 );
itr = idx.begin();
for( size_t i = 0; i < idx.size(); i++ ) {
BOOST_CHECK( itr->hash_id == fc::sha256::hash( std::to_string( i ) + std::to_string( i )));
itr++;
}
pw_vout_manager.delete_vout_with_newer( fc::sha256::hash( "0" + std::to_string( 0 )));
BOOST_CHECK( idx.size() == 0 );
}
BOOST_AUTO_TEST_CASE( confirm_pw_vout_objects )
{
const auto& idx = db.get_index_type<primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
primary_wallet_vout_manager pw_vout_manager( db );
create_primary_wallet_vouts( pw_vout_manager, db, 2 );
pw_vout_manager.confirm_vout( fc::sha256::hash( "0" + std::to_string( 0 )));
auto itr = idx.begin();
BOOST_CHECK( itr->confirmed == true );
pw_vout_manager.confirm_vout( fc::sha256::hash( "1" + std::to_string( 1 )));
itr++;
BOOST_CHECK( itr->confirmed == true );
BOOST_CHECK( idx.size() == 1 );
create_primary_wallet_vouts( pw_vout_manager, db, 1 );
itr++;
pw_vout_manager.confirm_vout( fc::sha256::hash( "2" + std::to_string( 2 )));
BOOST_CHECK( itr->confirmed == true );
BOOST_CHECK( idx.size() == 1 );
GRAPHENE_REQUIRE_THROW( pw_vout_manager.confirm_vout( fc::sha256::hash( "4" + std::to_string( 4 ))), fc::exception );
GRAPHENE_REQUIRE_THROW( pw_vout_manager.confirm_vout( fc::sha256::hash( "123" + std::to_string( 123 ))), fc::exception );
}
BOOST_AUTO_TEST_CASE( use_pw_vout_objects )
{
const auto& idx = db.get_index_type<primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
primary_wallet_vout_manager pw_vout_manager( db );
auto itr = idx.begin();
create_primary_wallet_vouts( pw_vout_manager, db, 1 );
pw_vout_manager.use_latest_vout( fc::sha256::hash( "0" + std::to_string( 0 )));
BOOST_CHECK( !pw_vout_manager.get_latest_unused_vout().valid() );
BOOST_CHECK( itr->used == true );
create_primary_wallet_vouts( pw_vout_manager, db, 1 );
pw_vout_manager.use_latest_vout( fc::sha256::hash( "1" + std::to_string( 1 )));
itr++;
BOOST_CHECK( !pw_vout_manager.get_latest_unused_vout().valid() );
BOOST_CHECK( itr->used == true );
create_primary_wallet_vouts( pw_vout_manager, db, 2 );
GRAPHENE_REQUIRE_THROW( pw_vout_manager.use_latest_vout( fc::sha256::hash( "3" + std::to_string( 3 ))), fc::exception );
}
BOOST_AUTO_TEST_SUITE_END()