Skip to content

Commit

Permalink
Merge pull request #221 from graft-project/feature/rta-tx
Browse files Browse the repository at this point in the history
RTA transaction
  • Loading branch information
mbg033 authored Mar 11, 2019
2 parents 7bdb2fb + 3640677 commit 065d683
Show file tree
Hide file tree
Showing 19 changed files with 635 additions and 46 deletions.
41 changes: 38 additions & 3 deletions src/cryptonote_basic/cryptonote_basic.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,14 +203,47 @@ namespace cryptonote
END_KV_SERIALIZE_MAP()
};

// container for RTA identities (public keys)
// stores RTA payment ID, PoS public one-time identification key (used to identify PoS in the network and protect data for it),
// auth sample supernode public identification keys (graftnode will need it to validate auth sample signatures),
// PoS and Wallet Proxy Supernode identification keys to transaction_header.extra.
// TODO: better name?
struct rta_header
{
std::string payment_id;
// pre-defined key indexes for POS, POS Proxy and Wallet Proxy
static constexpr size_t POS_KEY_INDEX = 0;
static constexpr size_t POS_PROXY_KEY_INDEX = 1;
static constexpr size_t WALLET_PROXY_KEY_INDEX = 2;
uint64_t auth_sample_height = 0; // block height for auth sample generation

std::vector<crypto::public_key> keys;
BEGIN_SERIALIZE_OBJECT()
FIELD(payment_id)
FIELD(auth_sample_height)
FIELD(keys)
END_SERIALIZE()
bool operator== (const rta_header &other) const
{
return this->payment_id == other.payment_id
&& this->keys == other.keys
&& this->auth_sample_height == other.auth_sample_height;
}
};

struct rta_signature
{
account_public_address address;
size_t key_index; // reference to the corresponding pubkey. alternatively we can just iterate by matching signatures and keys
crypto::signature signature;
BEGIN_SERIALIZE_OBJECT()
FIELD(address)
FIELD(key_index)
FIELD(signature)
END_SERIALIZE()
bool operator== (const rta_signature &other) const
{
return this->key_index == other.key_index
&& this->signature == other.signature;
}
};

class transaction: public transaction_prefix
Expand Down Expand Up @@ -238,8 +271,10 @@ namespace cryptonote
tx_type_invalid = 255
};
// graft: tx type field
// TODO: consider to removed 'type' field. we can check if transaction is rta either by
// 1. checking if 'tx_extra_graft_rta_header' is present in tx_extra
// 2. simply checking tx version, so 'type' only needed for 'alpha' compatibilty.
size_t type = tx_type_generic;

std::vector<rta_signature> rta_signatures;

transaction();
Expand Down
23 changes: 23 additions & 0 deletions src/cryptonote_basic/cryptonote_format_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,29 @@ namespace cryptonote
return true;
}

bool add_graft_rta_header_to_extra(std::vector<uint8_t> &extra, const rta_header &rta_header)
{
std::string blob;
::serialization::dump_binary(const_cast<struct rta_header&>(rta_header), blob);
tx_extra_graft_rta_header container;
container.data = blob;
blob.clear();
::serialization::dump_binary(container, blob);
extra.push_back(TX_EXTRA_GRAFT_RTA_HEADER_TAG);
std::copy(blob.begin(), blob.end(), std::back_inserter(extra));
return true;
}

bool get_graft_rta_header_from_extra(const transaction &tx, rta_header &rta_header)
{
std::vector<tx_extra_field> tx_extra_fields;
parse_tx_extra(tx.extra, tx_extra_fields);
tx_extra_graft_rta_header rta_header_data;
if(!find_tx_extra_field_by_type(tx_extra_fields, rta_header_data))
return false;
return ::serialization::parse_binary(rta_header_data.data, rta_header);
}

bool add_graft_tx_secret_key_to_extra(std::vector<uint8_t> &extra, const crypto::secret_key& secret_key)
{
tx_extra_graft_tx_secret_key container;
Expand Down
17 changes: 17 additions & 0 deletions src/cryptonote_basic/cryptonote_format_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ namespace cryptonote
* \param graft_extra - graft fields to add
* \return - true on success
*/
// TODO: probably we will remove "..graft_tx_extra.." methods
bool add_graft_tx_extra_to_extra(transaction &tx, const supernode::GraftTxExtra &graft_extra);

/*!
Expand Down Expand Up @@ -123,6 +124,22 @@ namespace cryptonote
crypto::signature &supernode_signature,
crypto::secret_key &tx_secret_key);

/*!
* \brief add_graft_rta_header_to_extra - add rta_header to the extra
* \param extra - extra to add fields to
* \param rta_header - source rta_header
* \return - true on success
*/
bool add_graft_rta_header_to_extra(std::vector<uint8_t> &extra, const rta_header &rta_header);

/*!
* \brief get_graft_rta_header_from_extra - read rta_header from tx extra
* \param tx - input transaction
* \param rta_header - output rta_header
* \return - true on success
*/
bool get_graft_rta_header_from_extra(const transaction& tx, rta_header &rta_header);

bool add_extra_nonce_to_tx_extra(std::vector<uint8_t>& tx_extra, const blobdata& extra_nonce);
bool remove_field_from_tx_extra(std::vector<uint8_t>& tx_extra, const std::type_info &type);
void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id);
Expand Down
22 changes: 19 additions & 3 deletions src/cryptonote_basic/tx_extra.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@

#pragma once

#include "cryptonote_basic.h" // only need rta_header so it probably make sense to move it to separate file


#define TX_EXTRA_PADDING_MAX_COUNT 255
#define TX_EXTRA_NONCE_MAX_COUNT 255
Expand All @@ -38,9 +40,13 @@
#define TX_EXTRA_TAG_PUBKEY 0x01
#define TX_EXTRA_NONCE 0x02
#define TX_EXTRA_MERGE_MINING_TAG 0x03
// TODO: suggested to remove
#define TX_EXTRA_GRAFT_EXTRA_TAG 0x04
#define TX_EXTRA_GRAFT_STAKE_TX_TAG 0x10
#define TX_EXTRA_GRAFT_TX_SECRET_KEY_TAG 0x11

#define TX_EXTRA_GRAFT_STAKE_TX_TAG 0x10 // TODO: change this to 0x80 before public testnet release
#define TX_EXTRA_GRAFT_TX_SECRET_KEY_TAG 0x11 // TODO: change this to 0x81 before public testnet release
#define TX_EXTRA_GRAFT_RTA_HEADER_TAG 0x83

#define TX_EXTRA_MYSTERIOUS_MINERGATE_TAG 0xDE

#define TX_EXTRA_NONCE_PAYMENT_ID 0x00
Expand Down Expand Up @@ -197,12 +203,21 @@ namespace cryptonote
END_SERIALIZE()
};

struct tx_extra_graft_rta_header
{
std::string data;
BEGIN_SERIALIZE()
FIELD(data)
END_SERIALIZE()
};


// tx_extra_field format, except tx_extra_padding and tx_extra_pub_key:
// varint tag;
// varint size;
// varint data[];
typedef boost::variant<tx_extra_padding, tx_extra_pub_key, tx_extra_nonce, tx_extra_merge_mining_tag,
tx_extra_mysterious_minergate, tx_extra_graft_extra, tx_extra_graft_stake_tx, tx_extra_graft_tx_secret_key> tx_extra_field;
tx_extra_mysterious_minergate, tx_extra_graft_extra, tx_extra_graft_stake_tx, tx_extra_graft_tx_secret_key, tx_extra_graft_rta_header> tx_extra_field;
}

VARIANT_TAG(binary_archive, cryptonote::tx_extra_padding, TX_EXTRA_TAG_PADDING);
Expand All @@ -213,3 +228,4 @@ VARIANT_TAG(binary_archive, cryptonote::tx_extra_mysterious_minergate, TX_EXTRA_
VARIANT_TAG(binary_archive, cryptonote::tx_extra_graft_extra, TX_EXTRA_GRAFT_EXTRA_TAG);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_graft_stake_tx, TX_EXTRA_GRAFT_STAKE_TX_TAG);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_graft_tx_secret_key, TX_EXTRA_GRAFT_TX_SECRET_KEY_TAG);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_graft_rta_header, TX_EXTRA_GRAFT_RTA_HEADER_TAG);
2 changes: 1 addition & 1 deletion src/cryptonote_core/cryptonote_core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ namespace cryptonote
blocks_per_sync, sync_mode, fast_sync);

r = m_blockchain_storage.init(db, m_testnet, test_options);

m_mempool.set_stake_transaction_processor(&m_graft_stake_transaction_processor);
r = m_mempool.init();
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool");

Expand Down
83 changes: 61 additions & 22 deletions src/cryptonote_core/tx_pool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
#include "warnings.h"
#include "common/perf_timer.h"
#include "crypto/hash.h"
#include "stake_transaction_processor.h"
#include "graft_rta_config.h"

#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "txpool"
Expand Down Expand Up @@ -174,32 +176,24 @@ namespace cryptonote
// 1. if tx.type == tx_type_rta and tx.rta_signatures.size() > 0
// 2. if tx.version >= 3 and tx.rta_signatures.size() > 0

bool is_rta_tx = tx.type == transaction::tx_type_rta && tx.rta_signatures.size() > 0;
// validator for rta_transaction:
auto rta_validator = [&](const std::vector<cryptonote::rta_signature> &rta_signs) -> bool {
bool result = true;
for (const auto &rta_sign : rta_signs) {
result &= crypto::check_signature(id, rta_sign.address.m_spend_public_key, rta_sign.signature);
if (!result) {

LOG_ERROR("Failed to validate rta tx "
<< epee::string_tools::pod_to_hex(id)
<< " for address: "
<< cryptonote::get_account_address_as_str(m_blockchain.testnet(), rta_sign.address));

break;
}
}
return result;
};
bool is_rta_tx = tx.type == transaction::tx_type_rta;
if (is_rta_tx) {
bool is_rta_tx_valid = rta_validator(tx.rta_signatures);
cryptonote::rta_header rta_hdr;
if (!cryptonote::get_graft_rta_header_from_extra(tx, rta_hdr)) {
MERROR("Failed to parse rta-header from tx extra: " << id);
tvc.m_rta_signature_failed = true;
tvc.m_verifivation_failed = true;
return false;
}

bool is_rta_tx_valid = validate_rta_tx(id, tx.rta_signatures, rta_hdr);
if (!kept_by_block && !is_rta_tx_valid) {
LOG_ERROR("failed to validate rta tx, tx contains " << tx.rta_signatures.size() << " signatures");
tvc.m_rta_signature_failed = true;
tvc.m_verifivation_failed = true;
return false;
}

} else {
if (!kept_by_block && !m_blockchain.check_fee(blob_size, fee))
{
Expand All @@ -209,9 +203,6 @@ namespace cryptonote
}
}




size_t tx_size_limit = get_transaction_size_limit(version);
if (!kept_by_block && blob_size >= tx_size_limit)
{
Expand Down Expand Up @@ -1077,4 +1068,52 @@ namespace cryptonote
{
return true;
}

//---------------------------------------------------------------------------------
bool tx_memory_pool::validate_rta_tx(const crypto::hash &txid, const std::vector<rta_signature> &rta_signs, const rta_header &rta_hdr) const
{
bool result = true;

if (rta_hdr.keys.size() == 0) {
MERROR("Failed to validate rta tx, missing auth sample keys for tx: " << txid );
return false;
}
#if 0 // don't validate signatures for rta mining
if (rta_hdr.keys.size() != rta_signs.size()) {
MERROR("Failed to validate rta tx: " << txid << ", keys.size() != signatures.size()");
return false;
}

for (const auto &rta_sign : rta_signs) {
// check if key index is in range
if (rta_sign.key_index >= rta_hdr.keys.size()) {
MERROR("signature: " << rta_sign.signature << " has wrong key index: " << rta_sign.key_index);
result = false;
break;
}


result &= crypto::check_signature(txid, rta_hdr.keys[rta_sign.key_index], rta_sign.signature);
if (!result) {
MERROR("Failed to validate rta tx signature: " << epee::string_tools::pod_to_hex(txid) << " for key: " << rta_hdr.keys[rta_sign.key_index]);
break;
}
}
#endif
for (const crypto::public_key &key : rta_hdr.keys) {
result &= validate_supernode(rta_hdr.auth_sample_height, key);
if (!result) {
MERROR("Failed to validate rta tx: " << epee::string_tools::pod_to_hex(txid) << ", key: " << key << " doesn't belong to a valid supernode");
break;
}
}

return result;
}

bool tx_memory_pool::validate_supernode(uint64_t height, const public_key &id) const
{
supernode_stake * stake = const_cast<supernode_stake*>(m_stp->find_supernode_stake(height, epee::string_tools::pod_to_hex(id)));
return stake ? stake->amount >= config::graft::TIER1_STAKE_AMOUNT : false;
};
}
15 changes: 14 additions & 1 deletion src/cryptonote_core/tx_pool.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
namespace cryptonote
{
class Blockchain;
class StakeTransactionProcessor;
/************************************************************************/
/* */
/************************************************************************/
Expand Down Expand Up @@ -106,7 +107,8 @@ namespace cryptonote
* @param id the transaction's hash
* @param blob_size the transaction's size
*/
bool add_tx(transaction &tx, const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version);
bool add_tx(transaction &tx, const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay,
uint8_t version);

/**
* @brief add a transaction to the transaction pool
Expand Down Expand Up @@ -329,6 +331,11 @@ namespace cryptonote
size_t validate(uint8_t version);


void set_stake_transaction_processor(StakeTransactionProcessor * arg)
{
m_stp = arg;
}

#define CURRENT_MEMPOOL_ARCHIVE_VER 11
#define CURRENT_MEMPOOL_TX_DETAILS_ARCHIVE_VER 12

Expand Down Expand Up @@ -455,6 +462,11 @@ namespace cryptonote
*/
bool is_transaction_ready_to_go(txpool_tx_meta_t& txd, transaction &tx) const;


bool validate_rta_tx(const crypto::hash &txid, const std::vector<cryptonote::rta_signature> &rta_signs, const cryptonote::rta_header &rta_hdr) const;

bool validate_supernode(uint64_t height, const crypto::public_key &id) const;

//TODO: confirm the below comments and investigate whether or not this
// is the desired behavior
//! map key images to transactions which spent them
Expand Down Expand Up @@ -501,6 +513,7 @@ namespace cryptonote
std::unordered_set<crypto::hash> m_timed_out_transactions;

Blockchain& m_blockchain; //!< reference to the Blockchain object
StakeTransactionProcessor * m_stp = nullptr;
};
}

Expand Down
10 changes: 5 additions & 5 deletions src/wallet/api/pending_transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,11 @@ void PendingTransactionImpl::putRtaSignatures(const std::vector<RtaSignature> &
std::vector<cryptonote::rta_signature> bin_signs;
for (const auto &sign : signs) {
cryptonote::rta_signature bin_sign;

if (!cryptonote::get_account_address_from_str(bin_sign.address, m_wallet.testnet(), sign.address)) {
LOG_ERROR("error parsing address from string: " << sign.address);
continue;
}
// TODO: update for new rta transaction
// if (!cryptonote::get_account_address_from_str(bin_sign.address, m_wallet.testnet(), sign.address)) {
// LOG_ERROR("error parsing address from string: " << sign.address);
// continue;
// }

epee::string_tools::hex_to_pod(sign.signature, bin_sign.signature);
bin_signs.push_back(bin_sign);
Expand Down
1 change: 1 addition & 0 deletions src/wallet/api/pending_transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class PendingTransactionImpl : public PendingTransaction
bool save(std::ostream &stream);
std::vector<std::string> getRawTransaction() const override;
void updateTransactionCache() override;
// TODO: update for new rta tx structure
void putRtaSignatures(const std::vector<RtaSignature> &) override;
private:
friend class WalletImpl;
Expand Down
2 changes: 1 addition & 1 deletion src/wallet/wallet2_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ namespace Monero {

struct RtaSignature
{
std::string address;
size_t key_index;
std::string signature;
};

Expand Down
Loading

0 comments on commit 065d683

Please sign in to comment.