mirror of https://github.com/oxen-io/oxen-core.git
basic structure for signing transactions
The keyring structure contains functions that will take a pending transaction and fill out the cryptonote::transaction member with the relevant signature fields ready for serialization.
This commit is contained in:
parent
3a1dfe3ea3
commit
44f565f085
|
@ -165,9 +165,9 @@ namespace cryptonote
|
|||
|
||||
struct tx_destination_entry
|
||||
{
|
||||
std::string original;
|
||||
uint64_t amount; //money
|
||||
account_public_address addr; //destination address
|
||||
std::string original; // Cached string of the address. Access using address()
|
||||
uint64_t amount; // Money
|
||||
account_public_address addr; // Destination Address
|
||||
bool is_subaddress;
|
||||
bool is_integrated;
|
||||
|
||||
|
|
|
@ -9,17 +9,19 @@ namespace wallet
|
|||
struct Decoy
|
||||
{
|
||||
//outs - array of structure outkey as follows:
|
||||
//height - unsigned int; block height of the output
|
||||
//key - String; the public key of the output
|
||||
//mask - String
|
||||
//height - int; block height of the output
|
||||
//key - crypto::public_key; the public key of the output
|
||||
//mask - rct::key;
|
||||
//txid - String; transaction id
|
||||
//unlocked - boolean; States if output is locked (false) or not (true)
|
||||
//index - int; absolute index of the decoy
|
||||
|
||||
int64_t height;
|
||||
std::string key; // Hex public key of the output
|
||||
std::string mask;
|
||||
crypto::public_key key; // Hex public key of the output
|
||||
rct::key mask;
|
||||
std::string txid;
|
||||
bool unlocked;
|
||||
int64_t global_index;
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -316,11 +316,11 @@ namespace wallet
|
|||
|
||||
if (output_dict.key() != "key")
|
||||
return;
|
||||
o.key = output_dict.consume_string_view();
|
||||
o.key = tools::make_from_guts<crypto::public_key>(output_dict.consume_string_view());
|
||||
|
||||
if (output_dict.key() != "mask")
|
||||
return;
|
||||
o.mask = output_dict.consume_string_view();
|
||||
o.mask = tools::make_from_guts<rct::key>(output_dict.consume_string_view());
|
||||
|
||||
if (output_dict.key() != "txid")
|
||||
return;
|
||||
|
|
|
@ -3,9 +3,37 @@
|
|||
#include "wallet2½.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <cryptonote_core/cryptonote_tx_utils.h>
|
||||
#include <cryptonote_basic/cryptonote_basic.h>
|
||||
#include <cryptonote_basic/txtypes.h>
|
||||
#include <cryptonote_basic/account.h>
|
||||
#include <device/device.hpp>
|
||||
|
||||
namespace wallet
|
||||
{
|
||||
crypto::secret_key
|
||||
Keyring::generate_tx_key(uint8_t hf_version)
|
||||
{
|
||||
// TODO sean make sure this is zero
|
||||
crypto::secret_key tx_key{};
|
||||
// TODO sean this should base itself on the hf version
|
||||
//return key_device.open_tx(tx_key, transaction::get_max_version_for_hf(hf_version), txtype::standard);
|
||||
if (!key_device.open_tx(tx_key, cryptonote::txversion::v4_tx_types, cryptonote::txtype::standard))
|
||||
throw std::runtime_error("Could not generate transaction secret key");
|
||||
|
||||
return tx_key;
|
||||
}
|
||||
|
||||
crypto::public_key
|
||||
Keyring::secret_tx_key_to_public_tx_key(const crypto::secret_key a)
|
||||
{
|
||||
// TODO sean make sure this is zero
|
||||
rct::key aG{};
|
||||
if (!key_device.scalarmultBase(aG, rct::sk2rct(a)))
|
||||
throw std::runtime_error("Could not convert secret tx key to public tx key");
|
||||
return rct::rct2pk(aG);
|
||||
}
|
||||
|
||||
crypto::key_derivation
|
||||
Keyring::generate_key_derivation(const crypto::public_key& tx_pubkey) const
|
||||
{
|
||||
|
@ -97,4 +125,172 @@ namespace wallet
|
|||
return wallet25::output_amount(rv, derivation, i, mask, key_device);
|
||||
}
|
||||
|
||||
// This gets called for every output in the transaction, there is some complication for how the
|
||||
// key gets generated for change address because the derivation is a*R or some simpler calc i guess
|
||||
// set the bool for this_dst_is_change_addr to false and optional null for the actual thingo
|
||||
crypto::public_key
|
||||
Keyring::generate_output_ephemeral_keys(const crypto::secret_key& tx_key, const cryptonote::tx_destination_entry& dst_entr, const size_t output_index, std::vector<rct::key>& amount_keys)
|
||||
{
|
||||
|
||||
crypto::public_key out_eph_public_key;
|
||||
cryptonote::account_keys sender_account_keys{};
|
||||
sender_account_keys.m_view_secret_key = view_private_key;
|
||||
const auto tx_key_pub = secret_tx_key_to_public_tx_key(tx_key);
|
||||
bool this_dst_is_change_addr = false;
|
||||
//std::optional<cryptonote::tx_destination_entry> change_addr = std::nullopt;
|
||||
bool need_additional_txkeys = false;
|
||||
std::vector<crypto::secret_key> additional_tx_keys{};
|
||||
std::vector<crypto::public_key> additional_tx_public_keys{};
|
||||
key_device.generate_output_ephemeral_keys(
|
||||
static_cast<uint16_t>(cryptonote::txversion::v4_tx_types), // size_t -> should be 4?
|
||||
this_dst_is_change_addr, // bool -> found change. Return parameter?
|
||||
sender_account_keys, // cryptonote::account_keys -> only uses view key i believe
|
||||
tx_key_pub, // crypto::public_key -> public key of the transaction
|
||||
tx_key, // crypto::secret_key -> secret key of the transaction
|
||||
dst_entr, // cryptonote::tx_destination_entry -> data of the transaction
|
||||
std::nullopt, // std::optional<cryptonote::tx_destination_entry> -> it will check if the data is the change because the one time address is different
|
||||
output_index, // position the output is in the transaction, concatenated to generate consistently
|
||||
need_additional_txkeys, // bool -> what are additional_txkeys ffs
|
||||
additional_tx_keys, // std::vector<crypto::secret_key> more additional tx keys, this time secret keys
|
||||
additional_tx_public_keys, // std::vector<crypto::public_key> public keys of additional keys. Return parameter?
|
||||
amount_keys, // std::vector<rct::key> keys that committing to the amount. Device APPENDS to the vector, is essentially a return parameter
|
||||
out_eph_public_key); // crypto::public_key -> Return parameter
|
||||
//
|
||||
return out_eph_public_key;
|
||||
}
|
||||
|
||||
// This is called over a transaction input to produce the secret key that can spend an outputs funds.
|
||||
// The key derivation is usually produced from calling generate_key_derivation().
|
||||
crypto::secret_key
|
||||
Keyring::derive_transaction_secret_key(const crypto::key_derivation& key_derivation, const size_t output_index)
|
||||
{
|
||||
crypto::secret_key output_secret_key;
|
||||
key_device.derive_secret_key(key_derivation, output_index, spend_private_key, output_secret_key);
|
||||
return output_secret_key;
|
||||
}
|
||||
|
||||
crypto::hash
|
||||
Keyring::get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx)
|
||||
{
|
||||
crypto::hash h = crypto::null_hash;
|
||||
key_device.get_transaction_prefix_hash(tx, h);
|
||||
return h;
|
||||
}
|
||||
|
||||
void
|
||||
Keyring::sign_transaction(PendingTransaction& ptx)
|
||||
{
|
||||
uint8_t hf_version = cryptonote::network_version_19;
|
||||
auto tx_key = generate_tx_key(hf_version);
|
||||
|
||||
rct::ctkeyV inSk;
|
||||
rct::keyV dest_keys;
|
||||
rct::ctkeyM mixRing(ptx.chosen_outputs.size());
|
||||
uint64_t amount_in = 0, amount_out = 0;
|
||||
std::vector<uint64_t> inamounts, outamounts;
|
||||
std::vector<unsigned int> index;
|
||||
inSk.reserve(ptx.chosen_outputs.size() + 1);
|
||||
|
||||
// Loop over inputs for the transaction to build the VIN array (Amount = 0, keyimage, array of offsets for ring)
|
||||
// and collect all the transaction private keys so we can spend our outputs in this transaction.
|
||||
int i = 0;
|
||||
for(const wallet::Output& src_entr: ptx.chosen_outputs)
|
||||
{
|
||||
// This takes the source outputs public transaction and combines it with our secret view key
|
||||
// to make a key derivation. This derivation can be used evaluate an output on the
|
||||
// blockchain to see if it is ours to spend. We already know its ours because the wallet
|
||||
// has collected them at an earlier point in time. Now we combine this derivation
|
||||
// with the output index and our secret spend key to generate
|
||||
// the actual transaction secret key which we can use to spend the output.
|
||||
crypto::key_derivation key_derivation = generate_key_derivation(src_entr.key);
|
||||
crypto::secret_key output_secret_key = derive_transaction_secret_key(key_derivation, src_entr.output_index);
|
||||
|
||||
// There is a input secret keys structure (inSk) that gets passed to the ringct library/module and it is
|
||||
// essentially an array of our output secret keys. It also needs to know the mask which is
|
||||
// another random number used to hide the amounts in our pederson commitments.
|
||||
rct::ctkey ctkey;
|
||||
ctkey.dest = rct::sk2rct(output_secret_key);
|
||||
ctkey.mask = src_entr.rct_mask;
|
||||
inSk.push_back(ctkey);
|
||||
|
||||
// Bookkeeping structures keeping track of how much $$ we are putting into the transaction
|
||||
inamounts.push_back(src_entr.amount);
|
||||
amount_in += src_entr.amount;
|
||||
|
||||
// Create the VIN structure of the transaction. This will just be a simple JSON without any crypto magic that
|
||||
// shows the key images and a now redundant amount field which always says zero. We generated the key images
|
||||
// when first scanning the outputs so we can just copy it straight from the database
|
||||
cryptonote::txin_to_key input_to_key;
|
||||
input_to_key.amount = 0;
|
||||
input_to_key.k_image = src_entr.key_image;
|
||||
|
||||
// The outputs array in the VIN structure lists all the global indexs of the ring decoys,
|
||||
// it uses offsets relative to the first output to save space on chain, so they need
|
||||
// to be converted from absolute to relative afterwards using the utility function.
|
||||
//
|
||||
// At this point we also push the public keys of the decoys into our mixRing struct which will get
|
||||
// passed to the ringct module which it actually will use to generate a ring signature.
|
||||
mixRing[i].reserve(ptx.decoys[i].size());
|
||||
for(const auto& decoy: ptx.decoys[i])
|
||||
{
|
||||
input_to_key.key_offsets.push_back(decoy.global_index);
|
||||
mixRing[i].push_back(rct::ctkey{});
|
||||
rct::ctkey& decoypk = mixRing[i].back();
|
||||
decoypk.dest = rct::pk2rct(decoy.key);
|
||||
decoypk.mask = decoy.mask;
|
||||
}
|
||||
input_to_key.key_offsets.push_back(src_entr.global_index);
|
||||
index.push_back(src_entr.global_index);
|
||||
|
||||
input_to_key.key_offsets = cryptonote::absolute_output_offsets_to_relative(input_to_key.key_offsets);
|
||||
i++;
|
||||
}
|
||||
|
||||
// TODO sean the inputs should be sorted by key_image
|
||||
|
||||
std::vector<rct::key> amount_keys;
|
||||
amount_keys.clear();
|
||||
i = 0;
|
||||
// Loop over destinations and generate one time destination keys (Output Ephemeral Key)
|
||||
for(const cryptonote::tx_destination_entry& recipient: ptx.recipients)
|
||||
{
|
||||
//amount_keys is a return parameter here, generate_output_ephemeral keys appends to the vector as it goes
|
||||
crypto::public_key out_eph_public_key = generate_output_ephemeral_keys(tx_key, recipient, i, amount_keys);
|
||||
cryptonote::tx_out out;
|
||||
out.amount = recipient.amount;
|
||||
cryptonote::txout_to_key tk;
|
||||
tk.key = out_eph_public_key;
|
||||
out.target = tk;
|
||||
dest_keys.push_back(rct::pk2rct(out_eph_public_key));
|
||||
outamounts.push_back(recipient.amount);
|
||||
amount_out += recipient.amount;
|
||||
// TODO sean the output should be shuffled
|
||||
// also a change address needs to be in here
|
||||
i++;
|
||||
}
|
||||
|
||||
crypto::hash tx_prefix_hash = get_transaction_prefix_hash(ptx.tx);
|
||||
|
||||
rct::ctkeyV outSk;
|
||||
const rct::RCTConfig rct_config{rct::RangeProofType::PaddedBulletproof, 3/*CLSAG*/};
|
||||
|
||||
// This generates the bulletproofs and also the ring signature, pretty much does everything and adds
|
||||
// the information for rct_signatures and rctsig_prunable to the transaction
|
||||
ptx.tx.rct_signatures = rct::genRctSimple(
|
||||
rct::hash2rct(tx_prefix_hash), // rct::key& message
|
||||
inSk, // rct::ctkeyV inSk
|
||||
dest_keys, // rct::keyV destinations
|
||||
inamounts, // std::vector<xmr_amount>& inamounts
|
||||
outamounts, // std::vector<xmr_amount>& outamounts
|
||||
amount_in - amount_out, // xmr_amount txnFee
|
||||
mixRing, // rct::ctkeyM& mixRing
|
||||
amount_keys, // rct::keyV amount_keys -> Return Parameter
|
||||
NULL, // std::vector<multisig_kLRki>* kLRki -> no multisig
|
||||
nullptr, // rct::multisig_out* msout -> no multisig
|
||||
index, // std::vector<unsigned int>& index -> array of real outputs within the mixRing keys
|
||||
outSk, // rct::ctkeyV& outSk -> Return Parameter
|
||||
rct_config, // rct::RCTConfig& rct_config
|
||||
key_device); // hw::device& hwdev
|
||||
}
|
||||
|
||||
} // namespace wallet
|
||||
|
|
|
@ -2,11 +2,14 @@
|
|||
|
||||
#include <crypto/crypto.h>
|
||||
#include <cryptonote_basic/subaddress_index.h>
|
||||
#include <cryptonote_basic/cryptonote_basic.h>
|
||||
#include <device/device_default.hpp>
|
||||
#include <ringct/rctSigs.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "pending_transaction.hpp"
|
||||
|
||||
namespace wallet
|
||||
{
|
||||
class Keyring
|
||||
|
@ -23,6 +26,9 @@ namespace wallet
|
|||
, view_public_key(_view_public_key)
|
||||
{}
|
||||
|
||||
virtual crypto::secret_key
|
||||
generate_tx_key(uint8_t hf_version);
|
||||
|
||||
// Derivation = a*R where
|
||||
// `a` is the private view key of the recipient
|
||||
// `R` is the tx public key for the output
|
||||
|
@ -35,6 +41,9 @@ namespace wallet
|
|||
virtual crypto::key_derivation
|
||||
generate_key_derivation(const crypto::public_key& tx_pubkey) const;
|
||||
|
||||
virtual crypto::public_key
|
||||
secret_tx_key_to_public_tx_key(const crypto::secret_key tx_key);
|
||||
|
||||
virtual std::vector<crypto::key_derivation>
|
||||
generate_key_derivations(const std::vector<crypto::public_key>& tx_pubkeys) const;
|
||||
|
||||
|
@ -64,6 +73,29 @@ namespace wallet
|
|||
unsigned int i,
|
||||
rct::key& mask);
|
||||
|
||||
virtual crypto::public_key
|
||||
generate_output_ephemeral_keys(
|
||||
const crypto::secret_key& tx_key,
|
||||
const cryptonote::tx_destination_entry& dst_entr,
|
||||
const size_t output_index,
|
||||
std::vector<rct::key>& amount_keys);
|
||||
|
||||
virtual crypto::secret_key
|
||||
derive_transaction_secret_key(
|
||||
const crypto::key_derivation& key_derivation,
|
||||
const size_t output_index
|
||||
);
|
||||
|
||||
virtual crypto::hash
|
||||
get_transaction_prefix_hash(
|
||||
const cryptonote::transaction_prefix&
|
||||
);
|
||||
|
||||
virtual void
|
||||
sign_transaction(
|
||||
PendingTransaction& ptx
|
||||
);
|
||||
|
||||
private:
|
||||
crypto::secret_key spend_private_key;
|
||||
crypto::public_key spend_public_key;
|
||||
|
|
|
@ -4,15 +4,15 @@
|
|||
|
||||
namespace wallet
|
||||
{
|
||||
PendingTransaction::PendingTransaction(const std::vector<TransactionRecipient>& new_recipients)
|
||||
PendingTransaction::PendingTransaction(const std::vector<cryptonote::tx_destination_entry>& new_recipients)
|
||||
: recipients(new_recipients)
|
||||
{
|
||||
// TODO sean address of change needs to be creator
|
||||
change = TransactionRecipient{{}, 0};
|
||||
change = cryptonote::tx_destination_entry{};
|
||||
int64_t sum_recipient_amounts = 0;
|
||||
for (const auto& recipient : new_recipients)
|
||||
{
|
||||
if (sum_recipient_amounts < 0 || recipient.amount < 0)
|
||||
if (sum_recipient_amounts < 0)
|
||||
throw std::runtime_error("Transaction amounts must be positive");
|
||||
sum_recipient_amounts += recipient.amount;
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ namespace wallet
|
|||
recipients.begin(),
|
||||
recipients.end(),
|
||||
0,
|
||||
[](int64_t accumulator, const TransactionRecipient& recipient) {
|
||||
[](int64_t accumulator, const cryptonote::tx_destination_entry& recipient) {
|
||||
return accumulator + recipient.amount;
|
||||
});
|
||||
}
|
||||
|
@ -116,6 +116,7 @@ namespace wallet
|
|||
bool
|
||||
PendingTransaction::finalise()
|
||||
{
|
||||
tx = cryptonote::transaction{};
|
||||
if (sum_inputs() - sum_outputs() - fee - change.amount == 0)
|
||||
return true;
|
||||
else
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <cryptonote_basic/cryptonote_basic.h>
|
||||
#include <cryptonote_core/cryptonote_tx_utils.h>
|
||||
#include "address.hpp"
|
||||
#include "output.hpp"
|
||||
#include "decoy.hpp"
|
||||
|
@ -13,22 +14,13 @@ namespace wallet
|
|||
struct version
|
||||
{}; // XXX: placeholder type
|
||||
|
||||
struct TransactionRecipient
|
||||
{
|
||||
address recipient_address;
|
||||
int64_t amount;
|
||||
|
||||
TransactionRecipient() = default;
|
||||
TransactionRecipient(address addr, int64_t amt) : recipient_address(addr), amount(amt){};
|
||||
};
|
||||
|
||||
struct PendingTransaction
|
||||
{
|
||||
version tx_version;
|
||||
|
||||
std::vector<TransactionRecipient> recipients; // does not include change
|
||||
std::vector<cryptonote::tx_destination_entry> recipients; // does not include change
|
||||
|
||||
TransactionRecipient change;
|
||||
cryptonote::tx_destination_entry change;
|
||||
|
||||
std::string memo;
|
||||
|
||||
|
@ -48,7 +40,7 @@ namespace wallet
|
|||
|
||||
PendingTransaction() = default;
|
||||
|
||||
PendingTransaction(const std::vector<TransactionRecipient>& new_recipients);
|
||||
PendingTransaction(const std::vector<cryptonote::tx_destination_entry>& new_recipients);
|
||||
|
||||
int64_t
|
||||
get_fee() const;
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace wallet
|
|||
// create_transaction will create a vanilla spend transaction without any special features.
|
||||
PendingTransaction
|
||||
TransactionConstructor::create_transaction(
|
||||
const std::vector<TransactionRecipient>& recipients) const
|
||||
const std::vector<cryptonote::tx_destination_entry>& recipients) const
|
||||
{
|
||||
PendingTransaction new_tx(recipients);
|
||||
new_tx.fee_per_byte = fee_per_byte;
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace wallet
|
|||
};
|
||||
|
||||
PendingTransaction
|
||||
create_transaction(const std::vector<TransactionRecipient>& recipients) const;
|
||||
create_transaction(const std::vector<cryptonote::tx_destination_entry>& recipients) const;
|
||||
|
||||
uint64_t fee_per_byte = FEE_PER_BYTE_V13;
|
||||
uint64_t fee_per_output = FEE_PER_OUTPUT_V18;
|
||||
|
|
|
@ -52,6 +52,26 @@ class MockWallet : public Wallet
|
|||
store_transaction(hash, height, dummy_outputs);
|
||||
db_tx.commit();
|
||||
};
|
||||
|
||||
void
|
||||
store_test_output(wallet::Output o)
|
||||
{
|
||||
height++;
|
||||
|
||||
wallet::Block b{};
|
||||
b.height = height;
|
||||
auto hash = debug_random_filled<crypto::hash>(height);
|
||||
b.hash = hash;
|
||||
add_block(b);
|
||||
|
||||
std::vector<wallet::Output> dummy_outputs;
|
||||
o.block_height = height;
|
||||
dummy_outputs.push_back(o);
|
||||
|
||||
SQLite::Transaction db_tx(db->db);
|
||||
store_transaction(hash, height, dummy_outputs);
|
||||
db_tx.commit();
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <sqlitedb/database.hpp>
|
||||
|
||||
#include "mock_wallet.hpp"
|
||||
#include "mock_keyring.hpp"
|
||||
#include "mock_daemon_comms.hpp"
|
||||
|
||||
|
||||
|
@ -19,8 +20,9 @@ TEST_CASE("Transaction Creation", "[wallet,tx]")
|
|||
ctor.fee_per_output = 0;
|
||||
SECTION("Expect Fail if database is empty")
|
||||
{
|
||||
std::vector<wallet::TransactionRecipient> recipients;
|
||||
recipients.emplace_back(wallet::address{}, 4);
|
||||
std::vector<cryptonote::tx_destination_entry> recipients;
|
||||
recipients.emplace_back(cryptonote::tx_destination_entry{});
|
||||
recipients.back().amount = 4;
|
||||
REQUIRE_THROWS(ctor.create_transaction(recipients));
|
||||
}
|
||||
|
||||
|
@ -28,8 +30,9 @@ TEST_CASE("Transaction Creation", "[wallet,tx]")
|
|||
|
||||
SECTION("Creates a successful single transaction")
|
||||
{
|
||||
std::vector<wallet::TransactionRecipient> recipients;
|
||||
recipients.emplace_back(wallet::address{}, 4);
|
||||
std::vector<cryptonote::tx_destination_entry> recipients;
|
||||
recipients.emplace_back(cryptonote::tx_destination_entry{});
|
||||
recipients.back().amount = 4;
|
||||
wallet::PendingTransaction ptx = ctor.create_transaction(recipients);
|
||||
REQUIRE(ptx.recipients.size() == 1);
|
||||
REQUIRE(ptx.chosen_outputs.size() == 1);
|
||||
|
@ -41,8 +44,9 @@ TEST_CASE("Transaction Creation", "[wallet,tx]")
|
|||
|
||||
SECTION("Fails to create a transaction if amount is not enough")
|
||||
{
|
||||
std::vector<wallet::TransactionRecipient> recipients;
|
||||
recipients.emplace_back(wallet::address{}, 6);
|
||||
std::vector<cryptonote::tx_destination_entry> recipients;
|
||||
recipients.emplace_back(cryptonote::tx_destination_entry{});
|
||||
recipients.back().amount = 6;
|
||||
REQUIRE_THROWS(ctor.create_transaction(recipients));
|
||||
}
|
||||
|
||||
|
@ -50,8 +54,9 @@ TEST_CASE("Transaction Creation", "[wallet,tx]")
|
|||
wallet.store_test_transaction(7);
|
||||
SECTION("Creates a successful single transaction prefering to use a single input if possible")
|
||||
{
|
||||
std::vector<wallet::TransactionRecipient> recipients;
|
||||
recipients.emplace_back(wallet::address{}, 6);
|
||||
std::vector<cryptonote::tx_destination_entry> recipients;
|
||||
recipients.emplace_back(cryptonote::tx_destination_entry{});
|
||||
recipients.back().amount = 6;
|
||||
wallet::PendingTransaction ptx = ctor.create_transaction(recipients);
|
||||
REQUIRE(ptx.recipients.size() == 1);
|
||||
REQUIRE(ptx.chosen_outputs.size() == 1);
|
||||
|
@ -63,8 +68,9 @@ TEST_CASE("Transaction Creation", "[wallet,tx]")
|
|||
|
||||
SECTION("Creates a successful transaction using 2 inputs")
|
||||
{
|
||||
std::vector<wallet::TransactionRecipient> recipients;
|
||||
recipients.emplace_back(wallet::address{}, 8);
|
||||
std::vector<cryptonote::tx_destination_entry> recipients;
|
||||
recipients.emplace_back(cryptonote::tx_destination_entry{});
|
||||
recipients.back().amount = 8;
|
||||
wallet::PendingTransaction ptx = ctor.create_transaction(recipients);
|
||||
REQUIRE(ptx.recipients.size() == 1);
|
||||
REQUIRE(ptx.chosen_outputs.size() == 2);
|
||||
|
@ -79,8 +85,9 @@ TEST_CASE("Transaction Creation", "[wallet,tx]")
|
|||
|
||||
SECTION("Creates a successful transaction using 2 inputs, avoids creating dust and uses correct fee using 1 oxen per byte")
|
||||
{
|
||||
std::vector<wallet::TransactionRecipient> recipients;
|
||||
recipients.emplace_back(wallet::address{}, 4001);
|
||||
std::vector<cryptonote::tx_destination_entry> recipients;
|
||||
recipients.emplace_back(cryptonote::tx_destination_entry{});
|
||||
recipients.back().amount = 4001;
|
||||
wallet::PendingTransaction ptx = ctor.create_transaction(recipients);
|
||||
REQUIRE(ptx.recipients.size() == 1);
|
||||
REQUIRE(ptx.chosen_outputs.size() == 2);
|
||||
|
@ -94,8 +101,9 @@ TEST_CASE("Transaction Creation", "[wallet,tx]")
|
|||
ctor.fee_per_output = 50;
|
||||
SECTION("Creates a successful transaction using 2 inputs, avoids creating dust and uses correct fee using 1 oxen per byte and 50 oxen per output")
|
||||
{
|
||||
std::vector<wallet::TransactionRecipient> recipients;
|
||||
recipients.emplace_back(wallet::address{}, 4001);
|
||||
std::vector<cryptonote::tx_destination_entry> recipients;
|
||||
recipients.emplace_back(cryptonote::tx_destination_entry{});
|
||||
recipients.back().amount = 4001;
|
||||
wallet::PendingTransaction ptx = ctor.create_transaction(recipients);
|
||||
REQUIRE(ptx.recipients.size() == 1);
|
||||
REQUIRE(ptx.chosen_outputs.size() == 2);
|
||||
|
@ -105,4 +113,24 @@ TEST_CASE("Transaction Creation", "[wallet,tx]")
|
|||
for (const auto& decoys : ptx.decoys)
|
||||
REQUIRE(decoys.size() == 13);
|
||||
}
|
||||
|
||||
SECTION("Creates a successful transaction then signs using the keyring successfully")
|
||||
{
|
||||
// Start a new wallet for real inputs to test signatures
|
||||
auto wallet_with_valid_inputs = wallet::MockWallet();
|
||||
auto ctor_for_signing = wallet::TransactionConstructor(wallet_with_valid_inputs.get_db(), comms);
|
||||
|
||||
wallet::Output o{};
|
||||
|
||||
wallet_with_valid_inputs.store_test_output(o);
|
||||
std::vector<cryptonote::tx_destination_entry> recipients;
|
||||
recipients.emplace_back(cryptonote::tx_destination_entry{});
|
||||
recipients.back().amount = 4001;
|
||||
wallet::PendingTransaction ptx = ctor_for_signing.create_transaction(recipients);
|
||||
REQUIRE(ptx.finalise());
|
||||
|
||||
auto keys = std::make_unique<wallet::MockKeyring>();
|
||||
REQUIRE_NOTHROW(keys->sign_transaction(ptx));
|
||||
auto& signedtx = ptx.tx;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue