Skip to content

Commit

Permalink
Add reserve proof, update transaction proof etc
Browse files Browse the repository at this point in the history
  • Loading branch information
aivve committed May 26, 2019
1 parent 9d68ebe commit 27f33b6
Show file tree
Hide file tree
Showing 15 changed files with 790 additions and 116 deletions.
7 changes: 6 additions & 1 deletion include/IWalletLegacy.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "crypto/crypto.h"
#include "CryptoNoteCore/CryptoNoteBasic.h"
#include "ITransfersContainer.h"
#include "Rpc/CoreRpcServerCommandsDefinitions.h"

namespace CryptoNote {

Expand Down Expand Up @@ -142,11 +143,15 @@ class IWalletLegacy {
virtual bool getSeed(std::string& electrum_words) = 0;

virtual Crypto::SecretKey getTxKey(Crypto::Hash& txid) = 0;
virtual bool getTxProof(Crypto::Hash& txid, CryptoNote::AccountPublicAddress& address, std::string& tx_key, std::string& sig_str) = 0;
virtual bool get_tx_key(Crypto::Hash& txid, Crypto::SecretKey& txSecretKey) = 0;
virtual bool getTxProof(Crypto::Hash& txid, CryptoNote::AccountPublicAddress& address, Crypto::SecretKey& tx_key, std::string& sig_str) = 0;
virtual bool checkTxProof(Crypto::Hash& txid, CryptoNote::AccountPublicAddress& address, std::string& sig_str) = 0;
virtual std::string getReserveProof(const uint64_t &reserve, const std::string &message) = 0;

virtual std::string sign_message(const std::string &data) = 0;
virtual bool verify_message(const std::string &data, const CryptoNote::AccountPublicAddress &address, const std::string &signature) = 0;

virtual bool isTrackingWallet() = 0;
};

}
5 changes: 5 additions & 0 deletions src/CryptoNoteCore/Core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2630,6 +2630,11 @@ uint64_t Core::get_current_blockchain_height() const {
return mainChainStorage->getBlockCount();
}

bool Core::isKeyImageSpent(const Crypto::KeyImage& key_im) {
auto mainChain = chainsLeaves[0];
return mainChain->checkIfSpent(key_im);
}

std::time_t Core::getStartTime() const {
return start_time;
}
Expand Down
2 changes: 2 additions & 0 deletions src/CryptoNoteCore/Core.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ class Core : public ICore, public ICoreInformation {
virtual uint64_t getMinimalFeeForHeight(uint32_t height) override;
virtual uint64_t getMinimalFee() override;

bool isKeyImageSpent(const Crypto::KeyImage& key_im);

private:
const Currency& currency;
System::Dispatcher& dispatcher;
Expand Down
84 changes: 84 additions & 0 deletions src/Rpc/CoreRpcServerCommandsDefinitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,7 @@ struct COMMAND_RPC_GET_BLOCK_DETAILS_BY_HEIGHT {
};
};

//-----------------------------------------------
struct COMMAND_RPC_GEN_PAYMENT_ID {
typedef EMPTY_STRUCT request;

Expand Down Expand Up @@ -1002,6 +1003,7 @@ struct K_COMMAND_RPC_CHECK_TX_PROOF {
};
};

//-----------------------------------------------
struct COMMAND_RPC_VALIDATE_ADDRESS {
struct request {
std::string address;
Expand All @@ -1028,4 +1030,86 @@ struct COMMAND_RPC_VALIDATE_ADDRESS {
};
};

//-----------------------------------------------
struct COMMAND_RPC_VERIFY_MESSAGE {
struct request {
std::string message;
std::string address;
std::string signature;

void serialize(ISerializer &s) {
KV_MEMBER(message)
KV_MEMBER(address)
KV_MEMBER(signature)
}
};

struct response {
bool sig_valid;
std::string status;

void serialize(ISerializer &s) {
KV_MEMBER(sig_valid)
KV_MEMBER(status)
}
};
};

//-----------------------------------------------
struct reserve_proof_entry
{
Crypto::Hash txid;
uint64_t index_in_tx;
Crypto::PublicKey shared_secret;
Crypto::KeyImage key_image;
Crypto::Signature shared_secret_sig;
Crypto::Signature key_image_sig;

void serialize(ISerializer& s)
{
KV_MEMBER(txid)
KV_MEMBER(index_in_tx)
KV_MEMBER(shared_secret)
KV_MEMBER(key_image)
KV_MEMBER(shared_secret_sig)
KV_MEMBER(key_image_sig)
}
};

struct reserve_proof {
std::vector<reserve_proof_entry> proofs;
Crypto::Signature signature;

void serialize(ISerializer &s) {
KV_MEMBER(proofs)
KV_MEMBER(signature)
}
};

struct K_COMMAND_RPC_CHECK_RESERVE_PROOF {
struct request {
std::string address;
std::string message;
std::string signature;

void serialize(ISerializer &s) {
KV_MEMBER(address)
KV_MEMBER(message)
KV_MEMBER(signature)
}
};

struct response {
bool good;
uint64_t total;
uint64_t spent;

void serialize(ISerializer &s) {
KV_MEMBER(good)
KV_MEMBER(total)
KV_MEMBER(spent)
}
};
};

}
156 changes: 154 additions & 2 deletions src/Rpc/RpcServer.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright (c) 2012-2016, The CryptoNote developers, The Bytecoin developers
// Copyright (c) 2014-2018, The Monero Project
// Copyright (c) 2016, The Forknote developers
// Copyright (c) 2016-2018, The Karbowanec developers
//
Expand Down Expand Up @@ -217,7 +218,9 @@ bool RpcServer::processJsonRpcRequest(const HttpRequest& request, HttpResponse&
{ "check_tx_key", { makeMemberMethod(&RpcServer::k_on_check_tx_key), false } },
{ "check_tx_with_view_key", { makeMemberMethod(&RpcServer::k_on_check_tx_with_view_key), false } },
{ "check_tx_proof", { makeMemberMethod(&RpcServer::k_on_check_tx_proof), false } },
{ "validateaddress", { makeMemberMethod(&RpcServer::on_validate_address), false } }
{ "check_reserve_proof", { makeMemberMethod(&RpcServer::k_on_check_reserve_proof), false } },
{ "validateaddress", { makeMemberMethod(&RpcServer::on_validate_address), false } },
{ "verifymessage", { makeMemberMethod(&RpcServer::on_verify_message), false } }

};

Expand Down Expand Up @@ -1419,7 +1422,6 @@ bool RpcServer::k_on_check_tx_proof(const K_COMMAND_RPC_CHECK_TX_PROOF::request&
Transaction tx;

std::vector<uint32_t> out;
uint64_t amount;
std::vector<Crypto::Hash> tx_ids;
tx_ids.push_back(txid);
std::vector<Crypto::Hash> missed_txs;
Expand Down Expand Up @@ -1476,6 +1478,131 @@ bool RpcServer::k_on_check_tx_proof(const K_COMMAND_RPC_CHECK_TX_PROOF::request&
return true;
}

bool RpcServer::k_on_check_reserve_proof(const K_COMMAND_RPC_CHECK_RESERVE_PROOF::request& req, K_COMMAND_RPC_CHECK_RESERVE_PROOF::response& res) {

static const Crypto::SecretKey I = { { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };

// parse address
CryptoNote::AccountPublicAddress address;
if (!m_core.getCurrency().parseAccountAddressString(req.address, address)) {
throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_PARAM, "Failed to parse address " + req.address + '.' };
}

// parse sugnature
static constexpr char header[] = "ReserveProofV1";
const size_t header_len = strlen(header);
if (req.signature.size() < header_len || req.signature.substr(0, header_len) != header) {
throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Signature header check error" };
}

std::string sig_decoded;
if (!Tools::Base58::decode(req.signature.substr(header_len), sig_decoded)) {
throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Signature decoding error" };
}

BinaryArray ba;
if (!Common::fromHex(sig_decoded, ba)) {
throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Proof decoding error" };
}
reserve_proof proof_decoded;
if (!fromBinaryArray(proof_decoded, ba)) {
throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "BinaryArray decoding error" };
}

std::vector<reserve_proof_entry>& proofs = proof_decoded.proofs;

// compute signature prefix hash
std::string prefix_data = req.message;
prefix_data.append((const char*)&address, sizeof(CryptoNote::AccountPublicAddress));
for (size_t i = 0; i < proofs.size(); ++i) {
prefix_data.append((const char*)&proofs[i].key_image, sizeof(Crypto::PublicKey));
}
Crypto::Hash prefix_hash;
Crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash);

// fetch txes
std::vector<Crypto::Hash> transactionHashes;
for (size_t i = 0; i < proofs.size(); ++i) {
transactionHashes.push_back(proofs[i].txid);
}
std::vector<Hash> missed_txs;
std::vector<BinaryArray> txs;
m_core.getTransactions(transactionHashes, txs, missed_txs);
std::vector<Transaction> transactions;

// check spent status
res.total = 0;
res.spent = 0;
for (size_t i = 0; i < proofs.size(); ++i) {
const reserve_proof_entry& proof = proofs[i];
Transaction tx;
if (!fromBinaryArray(tx, txs[i])) {
JsonRpc::JsonRpcError{
CORE_RPC_ERROR_CODE_WRONG_PARAM, "Couldn't deserialize transaction" };
}

CryptoNote::TransactionPrefix txp = *static_cast<const TransactionPrefix*>(&tx);

if (proof.index_in_tx >= txp.outputs.size()) {
throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "index_in_tx is out of bound" };
}

const KeyOutput out_key = boost::get<KeyOutput>(txp.outputs[proof.index_in_tx].target);

// get tx pub key
Crypto::PublicKey txPubKey = getTransactionPublicKeyFromExtra(txp.extra);

// check singature for shared secret
if (!Crypto::check_tx_proof(prefix_hash, address.viewPublicKey, txPubKey, proof.shared_secret, proof.shared_secret_sig)) {
//throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Failed to check singature for shared secret" };
res.good = false;
return true;
}

// check signature for key image
const std::vector<const Crypto::PublicKey *>& pubs = { &out_key.key };
if (!Crypto::check_ring_signature(prefix_hash, proof.key_image, &pubs[0], 1, &proof.key_image_sig, false)) {
//throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Failed to check signature for key image" };
res.good = false;
return true;
}

// check if the address really received the fund
Crypto::KeyDerivation derivation;
if (!Crypto::generate_key_derivation(proof.shared_secret, I, derivation)) {
throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Failed to generate key derivation" };
}
try {
Crypto::PublicKey pubkey;
derive_public_key(derivation, proof.index_in_tx, address.spendPublicKey, pubkey);
if (pubkey == out_key.key) {
uint64_t amount = txp.outputs[proof.index_in_tx].amount;
res.total += amount;

if (m_core.isKeyImageSpent(proof.key_image)) {
res.spent += amount;
}
}
}
catch (...)
{
throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Unknown error" };
}

}

// check signature for address spend keys
Crypto::Signature sig = proof_decoded.signature;
if (!Crypto::check_signature(prefix_hash, address.spendPublicKey, sig)) {
res.good = false;
return true;
}

res.good = true;

return true;
}

bool RpcServer::on_validate_address(const COMMAND_RPC_VALIDATE_ADDRESS::request& req, COMMAND_RPC_VALIDATE_ADDRESS::response& res) {
AccountPublicAddress acc = boost::value_initialized<AccountPublicAddress>();
bool r = m_core.getCurrency().parseAccountAddressString(req.address, acc);
Expand All @@ -1489,4 +1616,29 @@ bool RpcServer::on_validate_address(const COMMAND_RPC_VALIDATE_ADDRESS::request&
return true;
}

bool RpcServer::on_verify_message(const COMMAND_RPC_VERIFY_MESSAGE::request& req, COMMAND_RPC_VERIFY_MESSAGE::response& res) {
Crypto::Hash hash;
Crypto::cn_fast_hash(req.message.data(), req.message.size(), hash);

AccountPublicAddress acc = boost::value_initialized<AccountPublicAddress>();
if (!m_core.getCurrency().parseAccountAddressString(req.address, acc)) {
throw JsonRpc::JsonRpcError(CORE_RPC_ERROR_CODE_WRONG_PARAM, std::string("Failed to parse address"));
}

const size_t header_len = strlen("SigV1");
if (req.signature.size() < header_len || req.signature.substr(0, header_len) != "SigV1") {
throw JsonRpc::JsonRpcError(CORE_RPC_ERROR_CODE_WRONG_PARAM, std::string("Signature header check error"));
}
std::string decoded;
Crypto::Signature s;
if (!Tools::Base58::decode(req.signature.substr(header_len), decoded) || sizeof(s) != decoded.size()) {
throw JsonRpc::JsonRpcError(CORE_RPC_ERROR_CODE_WRONG_PARAM, std::string("Signature decoding error"));
return false;
}
memcpy(&s, decoded.data(), sizeof(s));
res.sig_valid = Crypto::check_signature(hash, acc.spendPublicKey, s);
res.status = CORE_RPC_STATUS_OK;
return true;
}

}
2 changes: 2 additions & 0 deletions src/Rpc/RpcServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@ class RpcServer : public HttpServer {
bool k_on_check_tx_key(const K_COMMAND_RPC_CHECK_TX_KEY::request& req, K_COMMAND_RPC_CHECK_TX_KEY::response& res);
bool k_on_check_tx_with_view_key(const K_COMMAND_RPC_CHECK_TX_WITH_PRIVATE_VIEW_KEY::request& req, K_COMMAND_RPC_CHECK_TX_WITH_PRIVATE_VIEW_KEY::response& res);
bool k_on_check_tx_proof(const K_COMMAND_RPC_CHECK_TX_PROOF::request& req, K_COMMAND_RPC_CHECK_TX_PROOF::response& res);
bool k_on_check_reserve_proof(const K_COMMAND_RPC_CHECK_RESERVE_PROOF::request& req, K_COMMAND_RPC_CHECK_RESERVE_PROOF::response& res);
bool on_validate_address(const COMMAND_RPC_VALIDATE_ADDRESS::request& req, COMMAND_RPC_VALIDATE_ADDRESS::response& res);
bool on_verify_message(const COMMAND_RPC_VERIFY_MESSAGE::request& req, COMMAND_RPC_VERIFY_MESSAGE::response& res);

bool f_getMixin(const Transaction& transaction, uint64_t& mixin);

Expand Down
Loading

0 comments on commit 27f33b6

Please sign in to comment.