This is a big update towards working protocol transactions.
1. The CONVERT TX is creating the necessary information. 2. The PROTOCOL TX is creating the necessary information. 3. The wallet recognises the subaddress (kind of) on incoming amounts. At present, the PROTOCOL TX outputs are NOT spendable or included in balances.
This commit is contained in:
@@ -49,6 +49,68 @@ using namespace crypto;
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
|
||||
rct::key sm(rct::key y, int n, const rct::key &x)
|
||||
{
|
||||
while (n--)
|
||||
sc_mul(y.bytes, y.bytes, y.bytes);
|
||||
sc_mul(y.bytes, y.bytes, x.bytes);
|
||||
return y;
|
||||
}
|
||||
|
||||
// Compute the inverse of a scalar, the clever way
|
||||
rct::key invert(const rct::key &x)
|
||||
{
|
||||
rct::key _1, _10, _100, _11, _101, _111, _1001, _1011, _1111;
|
||||
|
||||
_1 = x;
|
||||
sc_mul(_10.bytes, _1.bytes, _1.bytes);
|
||||
sc_mul(_100.bytes, _10.bytes, _10.bytes);
|
||||
sc_mul(_11.bytes, _10.bytes, _1.bytes);
|
||||
sc_mul(_101.bytes, _10.bytes, _11.bytes);
|
||||
sc_mul(_111.bytes, _10.bytes, _101.bytes);
|
||||
sc_mul(_1001.bytes, _10.bytes, _111.bytes);
|
||||
sc_mul(_1011.bytes, _10.bytes, _1001.bytes);
|
||||
sc_mul(_1111.bytes, _100.bytes, _1011.bytes);
|
||||
|
||||
rct::key inv;
|
||||
sc_mul(inv.bytes, _1111.bytes, _1.bytes);
|
||||
|
||||
inv = sm(inv, 123 + 3, _101);
|
||||
inv = sm(inv, 2 + 2, _11);
|
||||
inv = sm(inv, 1 + 4, _1111);
|
||||
inv = sm(inv, 1 + 4, _1111);
|
||||
inv = sm(inv, 4, _1001);
|
||||
inv = sm(inv, 2, _11);
|
||||
inv = sm(inv, 1 + 4, _1111);
|
||||
inv = sm(inv, 1 + 3, _101);
|
||||
inv = sm(inv, 3 + 3, _101);
|
||||
inv = sm(inv, 3, _111);
|
||||
inv = sm(inv, 1 + 4, _1111);
|
||||
inv = sm(inv, 2 + 3, _111);
|
||||
inv = sm(inv, 2 + 2, _11);
|
||||
inv = sm(inv, 1 + 4, _1011);
|
||||
inv = sm(inv, 2 + 4, _1011);
|
||||
inv = sm(inv, 6 + 4, _1001);
|
||||
inv = sm(inv, 2 + 2, _11);
|
||||
inv = sm(inv, 3 + 2, _11);
|
||||
inv = sm(inv, 3 + 2, _11);
|
||||
inv = sm(inv, 1 + 4, _1001);
|
||||
inv = sm(inv, 1 + 3, _111);
|
||||
inv = sm(inv, 2 + 4, _1111);
|
||||
inv = sm(inv, 1 + 4, _1011);
|
||||
inv = sm(inv, 3, _101);
|
||||
inv = sm(inv, 2 + 4, _1111);
|
||||
inv = sm(inv, 3, _101);
|
||||
inv = sm(inv, 1 + 2, _11);
|
||||
|
||||
// Sanity check for successful inversion
|
||||
rct::key tmp;
|
||||
sc_mul(tmp.bytes, inv.bytes, x.bytes);
|
||||
CHECK_AND_ASSERT_THROW_MES(tmp == rct::identity(), "invert failed");
|
||||
return inv;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
void classify_addresses(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr, size_t &num_stdaddresses, size_t &num_subaddresses, account_public_address &single_dest_subaddress)
|
||||
{
|
||||
@@ -173,6 +235,9 @@ namespace cryptonote
|
||||
const oracle::pricing_record& pr,
|
||||
const uint8_t hf_version) {
|
||||
|
||||
// A vector to contain all of the additional _tx_secret_keys_
|
||||
//std::vector<crypto::secret_key>& additional_tx_keys;
|
||||
|
||||
// Clear the TX contents
|
||||
tx.set_null();
|
||||
tx.type = cryptonote::transaction_type::PROTOCOL;
|
||||
@@ -202,12 +267,63 @@ namespace cryptonote
|
||||
|
||||
// Calculate the slippage for the output amounts
|
||||
LOG_PRINT_L2("Creating protocol_tx...");
|
||||
std::vector<crypto::public_key> additional_tx_public_keys;
|
||||
for (auto const& entry: protocol_data) {
|
||||
if (entry.destination_asset == "BURN") {
|
||||
// BURN TX - no slippage, no money minted - skip
|
||||
continue;
|
||||
}
|
||||
// CONVERT TX - calculate the slippage, and decide if it is going to be converted or refunded
|
||||
// CONVERT TX
|
||||
|
||||
// Create a secret TX key (= s)
|
||||
crypto::secret_key s = keypair::generate(hw::get_device("default")).sec;
|
||||
//additional_tx_keys.push_back(s);
|
||||
|
||||
// Now add the correct TX public key (= sP_change)
|
||||
crypto::public_key txkey_pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(entry.P_change), rct::sk2rct(s)));
|
||||
additional_tx_public_keys.push_back(txkey_pub);
|
||||
|
||||
// Calculate the actual return address, because the field we already have is actually the TX pubkey to use
|
||||
// return address = Hs(syF || i)G + P_change = Hs(saP_change || i)G + P_change
|
||||
// Generate the uniqueness for the input
|
||||
size_t output_index = tx.vout.size();
|
||||
crypto::hash uniqueness = cn_fast_hash(&entry.input_k_image.data[0], 32);
|
||||
|
||||
// y = Hs(uniqueness)
|
||||
ec_scalar y;
|
||||
crypto::hash_to_scalar(&uniqueness, sizeof(crypto::hash), y);
|
||||
|
||||
rct::key key_y = (rct::key&)(y);
|
||||
rct::key key_F = (rct::key&)(entry.return_address);
|
||||
crypto::public_key yF = rct::rct2pk(rct::scalarmultKey(key_F, key_y));
|
||||
crypto::public_key syF = rct::rct2pk(rct::scalarmultKey(rct::scalarmultKey(key_F, key_y), rct::sk2rct(s)));
|
||||
crypto::key_derivation derivation_syF = AUTO_VAL_INIT(derivation_syF);
|
||||
std::memcpy(derivation_syF.data, syF.data, sizeof(crypto::key_derivation));
|
||||
|
||||
crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
|
||||
bool r = crypto::derive_public_key(derivation_syF, output_index, entry.P_change, out_eph_public_key);
|
||||
CHECK_AND_ASSERT_MES(r, false, "while creating protocol_tx outs: failed to derive_public_key(" << derivation_syF << ", " << uniqueness << ", "<< entry.P_change << ")");
|
||||
|
||||
// Sanity checks
|
||||
crypto::public_key P_change_verify = crypto::null_pkey;
|
||||
r = crypto::derive_subaddress_public_key(out_eph_public_key, derivation_syF, output_index, P_change_verify);
|
||||
CHECK_AND_ASSERT_MES(r, false, "while creating protocol_tx outs: failed to derive_subaddress_public_key(" << out_eph_public_key << ", " << derivation_syF << ", " << output_index << ", " << P_change_verify << ")");
|
||||
|
||||
LOG_ERROR("*****************************************************************************");
|
||||
LOG_ERROR("output_index : " << output_index);
|
||||
LOG_ERROR("P_change : " << entry.P_change);
|
||||
LOG_ERROR("key_y : " << key_y);
|
||||
LOG_ERROR("key_F : " << key_F);
|
||||
LOG_ERROR("s : " << s);
|
||||
LOG_ERROR("yF : " << yF);
|
||||
LOG_ERROR("der. (syF) : " << derivation_syF);
|
||||
LOG_ERROR("uniqueness : " << uniqueness);
|
||||
LOG_ERROR("txkey_pub : " << txkey_pub);
|
||||
LOG_ERROR("output_key : " << out_eph_public_key << " (derivation_syF, output_index, P_change)");
|
||||
LOG_ERROR("P_change_ver : " << P_change_verify);
|
||||
LOG_ERROR("*****************************************************************************");
|
||||
|
||||
// Now calculate the slippage, and decide if it is going to be converted or refunded
|
||||
uint64_t amount_slippage = 0, amount_minted = 0;
|
||||
bool ok = cryptonote::calculate_conversion(entry.source_asset, entry.destination_asset, entry.amount_burnt, entry.amount_slippage_limit, amount_minted, amount_slippage, circ_supply, pr, hf_version);
|
||||
if (!ok) {
|
||||
@@ -222,7 +338,7 @@ namespace cryptonote
|
||||
|
||||
// Create the TX output for this refund
|
||||
tx_out out;
|
||||
cryptonote::set_tx_out(entry.amount_burnt, entry.source_asset, 0, entry.destination_address, false, crypto::view_tag{}, out);
|
||||
cryptonote::set_tx_out(entry.amount_burnt, entry.source_asset, 0, out_eph_public_key, false, crypto::view_tag{}, out);
|
||||
tx.vout.push_back(out);
|
||||
} else {
|
||||
|
||||
@@ -232,12 +348,14 @@ namespace cryptonote
|
||||
|
||||
// Create the TX output for this conversion
|
||||
tx_out out;
|
||||
cryptonote::set_tx_out(amount_minted, entry.destination_asset, 0, entry.destination_address, false, crypto::view_tag{}, out);
|
||||
cryptonote::set_tx_out(amount_minted, entry.destination_asset, 0, out_eph_public_key, false, crypto::view_tag{}, out);
|
||||
tx.vout.push_back(out);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Add in all of the additional TX pubkeys we need to process the payments
|
||||
add_additional_tx_pub_keys_to_extra(tx.extra, additional_tx_public_keys);
|
||||
|
||||
// TODO: create the YIELD outputs
|
||||
|
||||
// Create the txin_gen now
|
||||
@@ -291,13 +409,6 @@ namespace cryptonote
|
||||
r = crypto::derive_public_key(derivation, /*output_index*/uniqueness, miner_address.m_spend_public_key, out_eph_public_key);
|
||||
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << 0 << ", "<< miner_address.m_spend_public_key << ")");
|
||||
|
||||
LOG_ERROR("*****************************************************************************");
|
||||
LOG_ERROR("derivation: " << derivation);
|
||||
LOG_ERROR("uniqueness: " << uniqueness);
|
||||
LOG_ERROR("txkey_pub : " << txkey.pub);
|
||||
LOG_ERROR("output_key: " << out_eph_public_key);
|
||||
LOG_ERROR("*****************************************************************************");
|
||||
|
||||
uint64_t amount = block_reward;
|
||||
summary_amounts += amount;
|
||||
|
||||
@@ -348,23 +459,71 @@ namespace cryptonote
|
||||
return addr.m_view_public_key;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool get_protocol_destination_address(const size_t tx_version, const crypto::key_image& ki, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key, crypto::public_key& tx_destination_address, hw::device& hwdev) {
|
||||
bool get_return_address(const size_t tx_version, // needed in case we change implementation down the line
|
||||
const cryptonote::transaction_type& type, // needed to determine between TRANSFER, CONVERT, YIELD
|
||||
const crypto::key_image& ki, // needed for uniqueness
|
||||
const cryptonote::account_keys &sender_account_keys, // needed to calculate pretty much anything
|
||||
const crypto::public_key &P_change, // one-time public key from CONVERT/YIELD change
|
||||
const crypto::public_key &txkey_pub, // public TX key from CONVERT/YIELD TX
|
||||
crypto::public_key& F, // OUTPUT
|
||||
hw::device& hwdev // hardware device to use (usually a software dev)
|
||||
) {
|
||||
|
||||
// With a protocol destination address, you are always sending the payment to yourself; derivation = a*R
|
||||
// Derivation ( = shared secret = z_i)
|
||||
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
|
||||
bool r = hwdev.generate_key_derivation(txkey_pub, sender_account_keys.m_view_secret_key, derivation);
|
||||
CHECK_AND_ASSERT_MES(r, false, "at get_protocol_destination_address: failed to generate_key_derivation(" << txkey_pub << ", " << sender_account_keys.m_view_secret_key << ")");
|
||||
CHECK_AND_ASSERT_MES(r, false, "at get_return_address: failed to generate_key_derivation(" << txkey_pub << ", " << sender_account_keys.m_view_secret_key << ")");
|
||||
|
||||
// Generate the uniqueness for the input
|
||||
crypto::hash uniqueness = cn_fast_hash(&ki.data[0], 32);
|
||||
r = hwdev.derive_public_key(derivation, uniqueness, sender_account_keys.m_account_address.m_spend_public_key, tx_destination_address);
|
||||
CHECK_AND_ASSERT_MES(r, false, "at get_protocol_destination_address: failed to derive_public_key()");
|
||||
|
||||
ec_scalar y;
|
||||
if (type == cryptonote::TRANSFER) {
|
||||
// TRANSFER relies on a shared secret (the key_derivation Z_i) between sender and recipient
|
||||
// y = Hs(uniqueness || z_i)
|
||||
r = hwdev.derivation_to_scalar(derivation, uniqueness, y);
|
||||
CHECK_AND_ASSERT_MES(r, false, "at get_return_address: failed to derivation_to_scalar(" << derivation << ", " << uniqueness << ")");
|
||||
} else if (type == cryptonote::CONVERT || type == cryptonote::YIELD) {
|
||||
// CONVERT & YIELD do not use the shared secret, because protocol_tx cannot have a wallet address or keys
|
||||
// Instead, we just use the uniqueness value from tx.vin[0].k_image
|
||||
crypto::hash_to_scalar(&uniqueness, sizeof(crypto::hash), y);
|
||||
} else {
|
||||
LOG_ERROR("Invalid TX type - return_address is not applicable");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now generate the return address
|
||||
// F = (y^-1).a.P_change
|
||||
|
||||
// First, we need to produce the multiplicative inverse of the scalar "y" (aka "y^-1")
|
||||
rct::key key_y = (rct::key&)(y);
|
||||
rct::key key_inv_y = invert(key_y);
|
||||
|
||||
// Now convert this value back into a secret key that we can use
|
||||
crypto::secret_key sk_y = rct::rct2sk(key_y);
|
||||
crypto::secret_key sk_inv_y = rct::rct2sk(key_inv_y);
|
||||
crypto::key_derivation derivation_aP_change = AUTO_VAL_INIT(derivation_aP_change);
|
||||
r = hwdev.generate_key_derivation(P_change, sender_account_keys.m_view_secret_key, derivation_aP_change);
|
||||
CHECK_AND_ASSERT_MES(r, false, "while calculating get_return_address: failed to generate_key_derivation(" << P_change << ", " << sender_account_keys.m_view_secret_key << ")");
|
||||
crypto::public_key pk_aP_change = crypto::null_pkey;
|
||||
memcpy(pk_aP_change.data, derivation_aP_change.data, sizeof(crypto::public_key));
|
||||
|
||||
// Sanity check that we can reverse the invert safely
|
||||
rct::key key_aP_change = rct::pk2rct(pk_aP_change);
|
||||
rct::key key_test = rct::scalarmultKey(key_aP_change, key_inv_y);
|
||||
rct::key key_verify = rct::scalarmultKey(key_test, key_y);
|
||||
CHECK_AND_ASSERT_MES(key_verify == key_aP_change, false, "at get_return_address: failed to verify invert() function with smK() approach");
|
||||
F = rct::rct2pk(key_test);
|
||||
|
||||
LOG_ERROR("*****************************************************************************");
|
||||
LOG_ERROR("derivation: " << derivation);
|
||||
LOG_ERROR("key_image : " << ki);
|
||||
LOG_ERROR("uniqueness: " << uniqueness);
|
||||
LOG_ERROR("txkey_pub : " << txkey_pub);
|
||||
LOG_ERROR("tx_address: " << tx_destination_address);
|
||||
LOG_ERROR("a : " << sender_account_keys.m_view_secret_key);
|
||||
LOG_ERROR("y : " << key_y);
|
||||
LOG_ERROR("P_change : " << P_change);
|
||||
LOG_ERROR("aP_change : " << pk_aP_change);
|
||||
LOG_ERROR("F : " << F);
|
||||
LOG_ERROR("*****************************************************************************");
|
||||
|
||||
return true;
|
||||
@@ -630,16 +789,6 @@ namespace cryptonote
|
||||
if (need_additional_txkeys)
|
||||
CHECK_AND_ASSERT_MES(destinations.size() == additional_tx_keys.size(), false, "Wrong amount of additional tx keys");
|
||||
|
||||
// Is this a CONVERT tx?
|
||||
if (tx_type == cryptonote::transaction_type::CONVERT) {
|
||||
// Set the destination address to be something only our wallet can identify
|
||||
// This is where Fulmo gets interesting... we need to include the input key images
|
||||
// so that we get uniqueness and prevent either Monero burning bug or key leakage.
|
||||
// tx.d_a = Hs("convert" || input_key_images || 8rAG) + B
|
||||
const txin_to_key &in = boost::get<txin_to_key>(tx.vin[0]);
|
||||
CHECK_AND_ASSERT_MES(get_protocol_destination_address(tx.version, in.k_image, sender_account_keys, txkey_pub, tx_key, tx.destination_address, hwdev), false, "Failed to get protocol destination address");
|
||||
}
|
||||
|
||||
uint64_t summary_outs_money = 0;
|
||||
//fill outputs
|
||||
size_t output_index = 0;
|
||||
@@ -678,6 +827,19 @@ namespace cryptonote
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(additional_tx_public_keys.size() == additional_tx_keys.size(), false, "Internal error creating additional public keys");
|
||||
|
||||
// Is this a CONVERT tx?
|
||||
if (tx_type == cryptonote::transaction_type::CONVERT) {
|
||||
// Set the destination address to be something our wallet can prove ownership of.
|
||||
// This is where Fulmo gets interesting... we need to include the input key images
|
||||
// so that we get uniqueness and prevent either Monero burning bug or key leakage.
|
||||
// tx.d_a = Hs("convert" || input_key_image[0] || 8rAG) + B
|
||||
const txin_to_key &in = boost::get<txin_to_key>(tx.vin[0]);
|
||||
crypto::public_key P_change;
|
||||
CHECK_AND_ASSERT_MES(tx.vout.size() == 1, false, "Internal error - too many outputs for CONVERT tx");
|
||||
CHECK_AND_ASSERT_MES(cryptonote::get_output_public_key(tx.vout[0], P_change), false, "Internal error - failed to get TX change output public key");
|
||||
CHECK_AND_ASSERT_MES(get_return_address(tx.version, tx.type, in.k_image, sender_account_keys, P_change, txkey_pub, tx.return_address, hwdev), false, "Failed to get protocol destination address");
|
||||
}
|
||||
|
||||
remove_field_from_tx_extra(tx.extra, typeid(tx_extra_additional_pub_keys));
|
||||
|
||||
LOG_PRINT_L2("tx pubkey: " << txkey_pub);
|
||||
|
||||
Reference in New Issue
Block a user