This code is hacky in the extreme, but it appears to contain a fairly complete
implementation of knaccc's "return address scheme" (for more information on this, please see https://github.com/monero-project/research-lab/issues/53). Fulmo's PROTOCOL_TX construct relies on a variation of knaccc's design to be able to make return payments to individuals. This code is not commercial-grade - it isn't even safe for alpha testing yet, but it is operational under at least some circumstances, and the math is correct. Caveat emptor.
This commit is contained in:
@@ -320,6 +320,116 @@ namespace cryptonote
|
||||
return generate_key_image_helper_precomp(ack, out_key, subaddr_recv_info->derivation, real_output_index, uniqueness, subaddr_recv_info->index, in_ephemeral, ki, hwdev);
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const crypto::hash& uniqueness, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev, const cryptonote::transaction_type& tx_type, const crypto::public_key& pk_change_tx)
|
||||
{
|
||||
if (hwdev.compute_key_image(ack, out_key, recv_derivation, real_output_index, received_index, in_ephemeral, ki))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ack.m_spend_secret_key == crypto::null_skey)
|
||||
{
|
||||
// for watch-only wallet, simply copy the known output pubkey
|
||||
in_ephemeral.pub = out_key;
|
||||
in_ephemeral.sec = crypto::null_skey;
|
||||
}
|
||||
else
|
||||
{
|
||||
// derive secret key with subaddress - step 1: original CN derivation
|
||||
crypto::secret_key scalar_step1;
|
||||
crypto::secret_key spend_skey = crypto::null_skey;
|
||||
|
||||
if (ack.m_multisig_keys.empty())
|
||||
{
|
||||
// if not multisig, use normal spend skey
|
||||
spend_skey = ack.m_spend_secret_key;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if multisig, use sum of multisig privkeys (local account's share of aggregate spend key)
|
||||
for (const auto &multisig_key : ack.m_multisig_keys)
|
||||
{
|
||||
sc_add((unsigned char*)spend_skey.data,
|
||||
(const unsigned char*)multisig_key.data,
|
||||
(const unsigned char*)spend_skey.data);
|
||||
}
|
||||
}
|
||||
|
||||
if (tx_type == cryptonote::transaction_type::PROTOCOL) {
|
||||
|
||||
// Calculate the subaddress public_key (P_change)
|
||||
crypto::public_key P_change = crypto::null_pkey;
|
||||
hwdev.derive_subaddress_public_key(out_key, recv_derivation, real_output_index, P_change);
|
||||
|
||||
crypto::key_derivation derivation_P_change_tx = AUTO_VAL_INIT(derivation_P_change_tx);
|
||||
CHECK_AND_ASSERT_MES(hwdev.generate_key_derivation(pk_change_tx, ack.m_view_secret_key, derivation_P_change_tx), false, "Failed to generate key_derivation for P_change");
|
||||
size_t idx = 0;
|
||||
crypto::hash uniqueness = cn_fast_hash(reinterpret_cast<void*>(&idx), sizeof(size_t));
|
||||
crypto::secret_key sk_spend = crypto::null_skey;
|
||||
CHECK_AND_ASSERT_MES(hwdev.derive_secret_key(derivation_P_change_tx, uniqueness, spend_skey, sk_spend), false, "Failed to derive secret key for P_change");
|
||||
crypto::public_key change_pk;
|
||||
CHECK_AND_ASSERT_MES(hwdev.secret_key_to_public_key(sk_spend, change_pk), false, "Failed to derive public key for P_change");
|
||||
CHECK_AND_ASSERT_MES(P_change == change_pk, false, "derived P_change public key does not match P_change");
|
||||
hwdev.derive_secret_key(recv_derivation, real_output_index, sk_spend, scalar_step1);
|
||||
in_ephemeral.sec = scalar_step1;
|
||||
CHECK_AND_ASSERT_MES(hwdev.secret_key_to_public_key(in_ephemeral.sec, in_ephemeral.pub), false, "Failed to derive public key");
|
||||
CHECK_AND_ASSERT_MES(in_ephemeral.pub == out_key,
|
||||
false, "key image helper precomp: given output pubkey doesn't match the derived one");
|
||||
hwdev.generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki);
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
// computes Hs(a*R || uniqueness) + b
|
||||
//crypto::hash uniqueness = cn_fast_hash(reinterpret_cast<void*>(&real_output_index), sizeof(size_t));
|
||||
if (uniqueness == crypto::null_hash) {
|
||||
hwdev.derive_secret_key(recv_derivation, real_output_index, spend_skey, scalar_step1);
|
||||
} else {
|
||||
hwdev.derive_secret_key(recv_derivation, uniqueness, spend_skey, scalar_step1);
|
||||
}
|
||||
}
|
||||
|
||||
// step 2: add Hs(a || index_major || index_minor)
|
||||
crypto::secret_key subaddr_sk;
|
||||
crypto::secret_key scalar_step2;
|
||||
if (received_index.is_zero())
|
||||
{
|
||||
scalar_step2 = scalar_step1; // treat index=(0,0) as a special case representing the main address
|
||||
}
|
||||
else
|
||||
{
|
||||
subaddr_sk = hwdev.get_subaddress_secret_key(ack.m_view_secret_key, received_index);
|
||||
hwdev.sc_secret_add(scalar_step2, scalar_step1,subaddr_sk);
|
||||
}
|
||||
|
||||
in_ephemeral.sec = scalar_step2;
|
||||
|
||||
if (ack.m_multisig_keys.empty())
|
||||
{
|
||||
// when not in multisig, we know the full spend secret key, so the output pubkey can be obtained by scalarmultBase
|
||||
CHECK_AND_ASSERT_MES(hwdev.secret_key_to_public_key(in_ephemeral.sec, in_ephemeral.pub), false, "Failed to derive public key");
|
||||
}
|
||||
else
|
||||
{
|
||||
// when in multisig, we only know the partial spend secret key. but we do know the full spend public key, so the output pubkey can be obtained by using the standard CN key derivation
|
||||
CHECK_AND_ASSERT_MES(hwdev.derive_public_key(recv_derivation, uniqueness, ack.m_account_address.m_spend_public_key, in_ephemeral.pub), false, "Failed to derive public key");
|
||||
// and don't forget to add the contribution from the subaddress part
|
||||
if (!received_index.is_zero())
|
||||
{
|
||||
crypto::public_key subaddr_pk;
|
||||
CHECK_AND_ASSERT_MES(hwdev.secret_key_to_public_key(subaddr_sk, subaddr_pk), false, "Failed to derive public key");
|
||||
add_public_key(in_ephemeral.pub, in_ephemeral.pub, subaddr_pk);
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_AND_ASSERT_MES(in_ephemeral.pub == out_key,
|
||||
false, "key image helper precomp: given output pubkey doesn't match the derived one");
|
||||
}
|
||||
|
||||
hwdev.generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki);
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const crypto::hash& uniqueness, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev)
|
||||
{
|
||||
if (hwdev.compute_key_image(ack, out_key, recv_derivation, real_output_index, received_index, in_ephemeral, ki))
|
||||
@@ -357,9 +467,12 @@ namespace cryptonote
|
||||
|
||||
// computes Hs(a*R || uniqueness) + b
|
||||
//crypto::hash uniqueness = cn_fast_hash(reinterpret_cast<void*>(&real_output_index), sizeof(size_t));
|
||||
hwdev.derive_secret_key(recv_derivation, uniqueness, spend_skey, scalar_step1);
|
||||
//hwdev.derive_secret_key(recv_derivation, real_output_index, spend_skey, scalar_step1);
|
||||
|
||||
if (uniqueness == crypto::null_hash) {
|
||||
hwdev.derive_secret_key(recv_derivation, real_output_index, spend_skey, scalar_step1);
|
||||
} else {
|
||||
hwdev.derive_secret_key(recv_derivation, uniqueness, spend_skey, scalar_step1);
|
||||
}
|
||||
|
||||
// step 2: add Hs(a || index_major || index_minor)
|
||||
crypto::secret_key subaddr_sk;
|
||||
crypto::secret_key scalar_step2;
|
||||
|
||||
@@ -107,6 +107,7 @@ namespace cryptonote
|
||||
uint64_t get_tx_fee(const transaction& tx);
|
||||
bool generate_key_image_helper(const account_keys& ack, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, const crypto::hash& uniqueness, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev);
|
||||
bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const crypto::hash& uniqueness, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev);
|
||||
bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const crypto::hash& uniqueness, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev, const cryptonote::transaction_type& tx_type, const crypto::public_key& pk_change_tx = crypto::null_pkey);
|
||||
void get_blob_hash(const blobdata& blob, crypto::hash& res);
|
||||
void get_blob_hash(const blobdata_ref& blob, crypto::hash& res);
|
||||
crypto::hash get_blob_hash(const blobdata& blob);
|
||||
|
||||
@@ -1981,6 +1981,25 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons
|
||||
crypto::public_key output_public_key;
|
||||
THROW_WALLET_EXCEPTION_IF(!get_output_public_key(tx.vout[i], output_public_key), error::wallet_internal_error, "Failed to get output public key");
|
||||
|
||||
// Is this a payout from a PROTOCOL_TX?
|
||||
crypto::public_key pk_change_tx = crypto::null_pkey;
|
||||
if (tx.type == cryptonote::transaction_type::PROTOCOL) {
|
||||
|
||||
// Calculate the subaddress public_key (P_change)
|
||||
crypto::public_key pk_change = crypto::null_pkey;
|
||||
bool ok = m_account.get_device().derive_subaddress_public_key(output_public_key, tx_scan_info.received->derivation, i, pk_change);
|
||||
THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to derive subaddress public key for CONVERT/YIELD TX");
|
||||
|
||||
// Find the TX public key for P_change
|
||||
auto search = m_protocol_txs.find(pk_change);
|
||||
THROW_WALLET_EXCEPTION_IF(search == m_protocol_txs.end(), error::wallet_internal_error, "failed to locate protocol_tx entry to permit source usage");
|
||||
size_t idx = search->second;
|
||||
THROW_WALLET_EXCEPTION_IF(idx >= get_num_transfer_details(), error::wallet_internal_error, "cannot locate protocol_txs index in m_transfers");
|
||||
const transfer_details& td = get_transfer_details(idx);
|
||||
THROW_WALLET_EXCEPTION_IF(td.m_tx.type != cryptonote::transaction_type::CONVERT && td.m_tx.type != cryptonote::transaction_type::YIELD, error::wallet_internal_error, "incorrect TX type for protocol_tx origin in m_transfers");
|
||||
pk_change_tx = get_tx_pub_key_from_extra(td.m_tx);
|
||||
}
|
||||
|
||||
if (m_multisig)
|
||||
{
|
||||
tx_scan_info.in_ephemeral.pub = output_public_key;
|
||||
@@ -1989,12 +2008,12 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons
|
||||
}
|
||||
else
|
||||
{
|
||||
bool r = cryptonote::generate_key_image_helper_precomp(m_account.get_keys(), output_public_key, tx_scan_info.received->derivation, i, tx_scan_info.uniqueness, tx_scan_info.received->index, tx_scan_info.in_ephemeral, tx_scan_info.ki, m_account.get_device());
|
||||
bool r = cryptonote::generate_key_image_helper_precomp(m_account.get_keys(), output_public_key, tx_scan_info.received->derivation, i, tx_scan_info.uniqueness, tx_scan_info.received->index, tx_scan_info.in_ephemeral, tx_scan_info.ki, m_account.get_device(), tx.type, pk_change_tx);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
|
||||
THROW_WALLET_EXCEPTION_IF(tx_scan_info.in_ephemeral.pub != output_public_key,
|
||||
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
|
||||
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
|
||||
}
|
||||
|
||||
|
||||
THROW_WALLET_EXCEPTION_IF(std::find(outs.begin(), outs.end(), i) != outs.end(), error::wallet_internal_error, "Same output cannot be added twice");
|
||||
if (tx_scan_info.money_transfered == 0 && !miner_tx)
|
||||
{
|
||||
@@ -2593,7 +2612,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
// Add the change output_public_key to the list of subaddresses to check
|
||||
crypto::public_key P_change = crypto::null_pkey;
|
||||
THROW_WALLET_EXCEPTION_IF(!cryptonote::get_output_public_key(tx.vout[0], P_change), error::wallet_internal_error, "Failed to get change output public key");
|
||||
m_subaddresses[P_change] = {0,0};
|
||||
m_subaddresses[P_change] = {0x50524F54,0x4F434F4C};
|
||||
m_protocol_txs.insert({P_change, m_transfers.size()-1});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user