Skip to content

Commit

Permalink
Update of Safe purchase logic
Browse files Browse the repository at this point in the history
* Wallet now needs OfferID and quantity of product as input
* Offer creation takes private view key and public address
* With this, we can verify if funds are sent to the seller
* Updated tests
  • Loading branch information
VanGrx committed Jan 22, 2020
1 parent 20a91ab commit aa06b37
Show file tree
Hide file tree
Showing 16 changed files with 120 additions and 88 deletions.
2 changes: 2 additions & 0 deletions src/blockchain_db/lmdb/db_lmdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5505,6 +5505,8 @@ bool BlockchainLMDB::is_valid_transaction_output_type(const txout_target_v &txou
offer.offer_id = offer_result.offer_id;
offer.active = offer_result.active;
offer.title = std::string{offer_result.title.begin(),offer_result.title.end()};
offer.seller_private_view_key = offer_result.seller_private_view_key;
offer.seller_address = offer_result.seller_address;
}
else if (get_result == MDB_NOTFOUND)
{
Expand Down
13 changes: 9 additions & 4 deletions src/cryptonote_core/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3320,11 +3320,11 @@ bool Blockchain::check_safex_tx(const transaction &tx, tx_verification_context &
total_payment = purchase.price;
get_safex_offer(purchase.offer_id, offer_to_purchase);

std::string desc_secret_key{offer_to_purchase.description.begin(), offer_to_purchase.description.begin()+64};
epee::string_tools::hex_to_pod(desc_secret_key, secret_seller_view_key);
secret_seller_view_key = offer_to_purchase.seller_private_view_key;
cryptonote::account_public_address seller_address = offer_to_purchase.seller_address;

std::string desc_public_key{offer_to_purchase.description.begin()+65, offer_to_purchase.description.end()};
epee::string_tools::hex_to_pod(desc_public_key, public_seller_spend_key);
public_seller_spend_key = seller_address.m_spend_public_key;
std::string seller_key_str = epee::string_tools::pod_to_hex(public_seller_spend_key);

}
}
Expand Down Expand Up @@ -6053,6 +6053,11 @@ bool Blockchain::get_safex_offers( std::vector<safex::safex_offer> &safex_offers

std::vector<crypto::public_key> Blockchain::is_safex_purchase_right_address(crypto::secret_key seller_secret_view_key, crypto::public_key public_seller_spend_key, const cryptonote::transaction& tx) {

crypto::public_key pkey;
if (!crypto::secret_key_to_public_key(seller_secret_view_key, pkey)) {
return {};
}

hw::device &hwdev = hw::get_device("default");

boost::unique_lock<hw::device> hwdev_lock (hwdev);
Expand Down
2 changes: 1 addition & 1 deletion src/cryptonote_core/cryptonote_core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1167,7 +1167,7 @@ namespace cryptonote
//todo Atana optimize somehow key image validation, so many conversions
const crypto::key_image &k_image = *boost::apply_visitor(key_image_visitor(), in);
std::unique_ptr<safex::create_offer> cmd = safex::safex_command_serializer::parse_safex_command<safex::create_offer>(txin.script);
safex::create_offer_data offer(cmd->get_offerid(),cmd->get_seller(),cmd->get_title(),cmd->get_quantity(),cmd->get_price(),cmd->get_description(),cmd->get_active());
safex::create_offer_data offer(cmd->get_offerid(),cmd->get_seller(),cmd->get_title(),cmd->get_quantity(),cmd->get_price(),cmd->get_description(),cmd->get_active(),cmd->get_seller_address(),cmd->get_seller_private_view_key());
crypto::hash cmd_hash{};
get_object_hash(offer, cmd_hash);
if (memcmp(cmd_hash.data, k_image.data, sizeof(k_image.data)) != 0)
Expand Down
2 changes: 1 addition & 1 deletion src/rpc/core_rpc_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ namespace cryptonote

for(auto offer: offers) {
std::string offer_id_str = epee::string_tools::pod_to_hex(offer.offer_id);
COMMAND_RPC_GET_SAFEX_OFFERS::entry ent{offer.title,offer.quantity,offer.price,offer.description,offer.active,offer.shipping,offer_id_str,offer.seller};
COMMAND_RPC_GET_SAFEX_OFFERS::entry ent{offer.title,offer.quantity,offer.price,offer.description,offer.active,offer.shipping,offer_id_str,offer.seller,offer.seller_address};
res.offers.push_back(ent);
}
res.status = CORE_RPC_STATUS_OK;
Expand Down
2 changes: 2 additions & 0 deletions src/rpc/core_rpc_server_commands_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ namespace cryptonote
std::vector<uint8_t> shipping;
std::string offer_id;
std::string seller;
cryptonote::account_public_address seller_address;

BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(title)
Expand All @@ -141,6 +142,7 @@ namespace cryptonote
KV_SERIALIZE(shipping)
KV_SERIALIZE(offer_id)
KV_SERIALIZE(seller)
KV_SERIALIZE(seller_address)
END_KV_SERIALIZE_MAP()
};

Expand Down
18 changes: 14 additions & 4 deletions src/safex/command.h
Original file line number Diff line number Diff line change
Expand Up @@ -276,13 +276,15 @@ struct edit_offer_result : public execution_result
uint64_t price;
std::vector<uint8_t> description{};
bool active{false};
crypto::secret_key seller_private_view_key;
cryptonote::account_public_address seller_address;

create_offer_data() {}
create_offer_data(const safex::safex_offer& offer): offer_id{offer.offer_id}, description{offer.description},quantity{offer.quantity},price{offer.price},seller(offer.seller.begin(),offer.seller.end()),active{offer.active},title{offer.title.begin(),offer.title.end()}
create_offer_data(const safex::safex_offer& offer): offer_id{offer.offer_id}, description{offer.description},quantity{offer.quantity},price{offer.price},seller(offer.seller.begin(),offer.seller.end()),active{offer.active},title{offer.title.begin(),offer.title.end()},seller_address{offer.seller_address},seller_private_view_key{offer.seller_private_view_key}
{
}
create_offer_data(const crypto::hash &_offer_id, const std::vector<uint8_t> &_seller, const std::vector<uint8_t> &_title, const uint64_t &_quantity, const uint64_t &_price, const std::vector<uint8_t> &_offer_data,const bool &_active):
offer_id{_offer_id},seller{_seller},title{_title},quantity{_quantity},price{_price},description{_offer_data},active{_active}{}
create_offer_data(const crypto::hash &_offer_id, const std::vector<uint8_t> &_seller, const std::vector<uint8_t> &_title, const uint64_t &_quantity, const uint64_t &_price, const std::vector<uint8_t> &_offer_data,const bool &_active, const cryptonote::account_public_address& _seller_address, const crypto::secret_key& _seller_private_view_key):
offer_id{_offer_id},seller{_seller},title{_title},quantity{_quantity},price{_price},description{_offer_data},active{_active},seller_address{_seller_address},seller_private_view_key{_seller_private_view_key}{}

BEGIN_SERIALIZE_OBJECT()
FIELD(offer_id)
Expand All @@ -292,6 +294,8 @@ struct edit_offer_result : public execution_result
FIELD(quantity)
FIELD(active)
FIELD(description)
FIELD(seller_private_view_key)
FIELD(seller_address)
END_SERIALIZE()
};

Expand Down Expand Up @@ -698,7 +702,7 @@ class create_offer : public command
* */
create_offer(const uint32_t _version, const safex::create_offer_data &offer) :
command(_version, command_t::create_offer), offer_id(offer.offer_id), description{offer.description},
seller{offer.seller},title{offer.title},price{offer.price},quantity{offer.quantity},active{offer.active}{
seller{offer.seller},title{offer.title},price{offer.price},quantity{offer.quantity},active{offer.active},seller_address{offer.seller_address},seller_private_view_key{offer.seller_private_view_key}{
}

create_offer() : command(0, command_t::create_offer), offer_id{}, description{} {}
Expand All @@ -710,6 +714,8 @@ class create_offer : public command
uint64_t get_quantity() const { return quantity; }
bool get_active() const { return active; }
std::vector<uint8_t> get_description() const { return description; }
cryptonote::account_public_address get_seller_address() const { return seller_address; }
crypto::secret_key get_seller_private_view_key() const { return seller_private_view_key; }

virtual create_offer_result* execute(const cryptonote::BlockchainDB &blokchain, const cryptonote::txin_to_script &txin) override;
virtual execution_status validate(const cryptonote::BlockchainDB &blokchain, const cryptonote::txin_to_script &txin) override;
Expand All @@ -724,6 +730,8 @@ class create_offer : public command
FIELD(quantity)
FIELD(active)
FIELD(description)
FIELD(seller_private_view_key)
FIELD(seller_address)
END_SERIALIZE()

private:
Expand All @@ -734,6 +742,8 @@ class create_offer : public command
uint64_t price;
std::vector<uint8_t> description{};
bool active{};
crypto::secret_key seller_private_view_key;
cryptonote::account_public_address seller_address;
};

class edit_offer : public command
Expand Down
16 changes: 12 additions & 4 deletions src/safex/safex_offer.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ namespace safex
}

safex_offer(const std::string &_title, const uint64_t _quantity, const uint64_t _price, const std::vector<uint8_t> &_description,
crypto::hash _id, std::string seller_username, bool _active = true):title{_title},quantity{_quantity},price{_price},
description{_description},offer_id{_id},seller{seller_username},active{_active}
crypto::hash _id, std::string seller_username, bool _active = true , const cryptonote::account_public_address& _seller_address = {}, const crypto::secret_key& view_key = {}):title{_title},quantity{_quantity},price{_price},
description{_description},offer_id{_id},seller{seller_username},active{_active},seller_private_view_key{view_key},seller_address{_seller_address}
{
}


safex_offer(const std::string &_title, const uint64_t _quantity, const uint64_t _price, const std::string& _description,
std::string seller_username):
title{_title}, quantity{_quantity}, price{_price}, active{true}, shipping{}, seller{seller_username} {
std::string seller_username, const crypto::secret_key& view_key, const cryptonote::account_public_address& _seller_address = {}):
title{_title}, quantity{_quantity}, price{_price}, active{true}, shipping{}, seller{seller_username},seller_private_view_key{view_key},seller_address{_seller_address} {

description = std::vector<uint8_t>(_description.begin(),_description.end());
offer_id = create_offer_id(seller_username);
Expand All @@ -56,6 +56,8 @@ namespace safex
KV_SERIALIZE(shipping)
KV_SERIALIZE(offer_id)
KV_SERIALIZE(seller)
KV_SERIALIZE(seller_private_view_key)
KV_SERIALIZE(seller_address)
END_KV_SERIALIZE_MAP()

BEGIN_SERIALIZE_OBJECT()
Expand All @@ -67,6 +69,8 @@ namespace safex
FIELD(shipping)
FIELD(offer_id)
FIELD(seller)
FIELD(seller_private_view_key)
FIELD(seller_address)
END_SERIALIZE()

template<class t_archive>
Expand All @@ -80,6 +84,8 @@ namespace safex
a & shipping;
a & offer_id;
a & seller;
a & seller_private_view_key;
a & seller_address;
}


Expand All @@ -91,6 +97,8 @@ namespace safex
std::vector<uint8_t> shipping;
crypto::hash offer_id; //unique id of the offer
std::string seller; // username of the seller
crypto::secret_key seller_private_view_key;
cryptonote::account_public_address seller_address;

private:
crypto::hash create_offer_id(std::string& username);
Expand Down
2 changes: 1 addition & 1 deletion src/simplewallet/simplewallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1220,7 +1220,7 @@ simple_wallet::simple_wallet()

m_cmd_binder.set_handler("safex_purchase",
boost::bind(&simple_wallet::safex_purchase, this, _1),
tr("safex_purchase [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <quantity> [<payment_id>] <offer_id>\"),"),
tr("safex_purchase [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <offer_id> <quantity>\"),"),
tr("Safex purchase. 95% of cash sent is given to the seller, 5% is taken as fee"));

m_cmd_binder.set_handler("donate_safex_fee",
Expand Down
97 changes: 54 additions & 43 deletions src/simplewallet/simplewallet_safex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,26 +98,7 @@ namespace cryptonote

std::vector<std::string> local_args = args_;

crypto::hash purchase_offer_id{};
std::vector<safex::safex_offer> offers = m_wallet->get_safex_offers();
std::vector<safex::safex_offer>::iterator offer_to_purchase;
uint64_t quantity_to_purchase;
if(command_type == CommandType::TransferPurchase) {
if(!epee::string_tools::hex_to_pod(local_args.back(), purchase_offer_id)){
fail_msg_writer() << tr("Bad offer ID given!!!");
return true;
}

offer_to_purchase = std::find_if(offers.begin(), offers.end(), [purchase_offer_id](safex::safex_offer offer){
return offer.offer_id == purchase_offer_id;});

if(offer_to_purchase!=offers.end())
local_args.pop_back();
else {
fail_msg_writer() << tr("There is no offer with given id!!");
return true;
}
}

std::set<uint32_t> subaddr_indices;
if (!local_args.empty() && local_args[0].substr(0, 6) == "index=")
Expand Down Expand Up @@ -306,7 +287,7 @@ namespace cryptonote
std::copy(local_args.begin() + 4, local_args.end(), ostream_iterator<string>(offerdata_ostr, " "));
std::string description = offerdata_ostr.str();

safex::safex_offer sfx_offer{offer_title, quantity, price, description, my_safex_account.username};
safex::safex_offer sfx_offer{offer_title, quantity, price, description, my_safex_account.username,m_wallet->get_account().get_keys().m_view_secret_key,m_wallet->get_account().get_keys().m_account_address};

cryptonote::tx_destination_entry de_offer = create_safex_offer_destination(info.address, sfx_offer);
dsts.push_back(de_offer);
Expand Down Expand Up @@ -335,14 +316,64 @@ namespace cryptonote
std::string description = offerdata_ostr.str();

safex::safex_offer sfx_offer{offer_title, quantity, price, std::vector<uint8_t>{description.begin(),description.end()},
offer_id_hash, my_safex_account.username};
sfx_offer.active = active;
offer_id_hash, my_safex_account.username, active, m_wallet->get_account().get_keys().m_account_address, m_wallet->get_account().get_keys().m_view_secret_key};

cryptonote::tx_destination_entry de_offer_update = edit_safex_offer_destination(info.address, sfx_offer);
dsts.push_back(de_offer_update);

}
}
else if (command_type == CommandType::TransferPurchase)
{
crypto::hash purchase_offer_id{};
std::vector<safex::safex_offer> offers = m_wallet->get_safex_offers();
std::vector<safex::safex_offer>::iterator offer_to_purchase;
uint64_t quantity_to_purchase;

if (!epee::string_tools::get_xtype_from_string(quantity_to_purchase, local_args.back())){
fail_msg_writer() << tr("Bad quantity to purchase given!!!");
return true;
}
local_args.pop_back();
if(!epee::string_tools::hex_to_pod(local_args.back(), purchase_offer_id)){
fail_msg_writer() << tr("Bad offer ID given!!!");
return true;
}

offer_to_purchase = std::find_if(offers.begin(), offers.end(), [purchase_offer_id](safex::safex_offer offer){
return offer.offer_id == purchase_offer_id;});

if(offer_to_purchase!=offers.end())
local_args.pop_back();
else {
fail_msg_writer() << tr("There is no offer with given id!!");
return true;
}

cryptonote::tx_destination_entry de = AUTO_VAL_INIT(de);

de.amount = quantity_to_purchase*offer_to_purchase->price * 95 / 100;
de.output_type = tx_out_type::out_cash;
safex_network_fee += quantity_to_purchase*offer_to_purchase->price * 5 / 100;

cryptonote::address_parse_info info = AUTO_VAL_INIT(info);
cryptonote::tx_destination_entry de_purchase = AUTO_VAL_INIT(de_purchase);
std::string destination_addr = m_wallet->get_subaddress_as_str({m_current_subaddress_account, 0});
if (!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), destination_addr))
{
fail_msg_writer() << tr("failed to parse address");
return true;
}

safex::create_purchase_data safex_purchase_output_data{purchase_offer_id,quantity_to_purchase,offer_to_purchase->price};
blobdata blobdata = cryptonote::t_serializable_object_to_blob(safex_purchase_output_data);
de_purchase = tx_destination_entry{0, info.address, false, tx_out_type::out_safex_purchase, blobdata};
dsts.push_back(de_purchase);

de.addr = offer_to_purchase->seller_address;

dsts.push_back(de);
}
else
{

Expand Down Expand Up @@ -423,26 +454,6 @@ namespace cryptonote
de.output_type = tx_out_type::out_network_fee;
}
// Allow to collect outputs for regular SFX transaction.
else if (command_type == CommandType::TransferPurchase)
{
quantity_to_purchase = stoi(local_args[i + 1]);
de.amount = quantity_to_purchase*offer_to_purchase->price * 95 / 100;
de.output_type = tx_out_type::out_cash;
safex_network_fee += quantity_to_purchase*offer_to_purchase->price * 5 / 100;

cryptonote::tx_destination_entry de_purchase = AUTO_VAL_INIT(de_purchase);
std::string destination_addr = m_wallet->get_subaddress_as_str({m_current_subaddress_account, 0});

cryptonote::address_parse_info info_dest = AUTO_VAL_INIT(info_dest);
if (!cryptonote::get_account_address_from_str(info_dest, m_wallet->nettype(), destination_addr)){
fail_msg_writer() << tr("failed to parse address");
return true;
}
safex::create_purchase_data safex_purchase_output_data{purchase_offer_id,quantity_to_purchase,offer_to_purchase->price};
blobdata blobdata = cryptonote::t_serializable_object_to_blob(safex_purchase_output_data);
de_purchase = tx_destination_entry{0, info_dest.address, false, tx_out_type::out_safex_purchase, blobdata};
dsts.push_back(de_purchase);
}

dsts.push_back(de);
}
Expand Down Expand Up @@ -771,7 +782,7 @@ namespace cryptonote
if (args.empty())
{
success_msg_writer() << tr("usage:\n"
" safex_purchase [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <quantity> [<payment_id>] <offer_id>\n");
" safex_purchase [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <offer_id> <quantity>\n");
return true;
}
return create_command(CommandType::TransferPurchase, args);
Expand Down
2 changes: 1 addition & 1 deletion src/wallet/wallet_safex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ namespace tools
for (auto &item : res.offers) {
crypto::hash hash;
epee::string_tools::hex_to_pod(item.offer_id, hash);
offers.emplace_back(item.title, item.quantity, item.price, item.description, hash, item.seller, item.active);
offers.emplace_back(item.title, item.quantity, item.price, item.description, hash, item.seller, item.active,item.seller_address);
}

return offers;
Expand Down
Loading

0 comments on commit aa06b37

Please sign in to comment.