initial import of SPARC integration into latest Carrot

This commit is contained in:
Some Random Crypto Guy
2025-05-13 12:57:05 +01:00
parent 87be655738
commit da99d88bce
13 changed files with 296 additions and 2 deletions
+2 -1
View File
@@ -39,7 +39,8 @@ set(carrot_core_sources
output_set_finalization.cpp
payment_proposal.cpp
scan.cpp
scan_unsafe.cpp)
scan_unsafe.cpp
sparc.cpp)
monero_find_all_headers(carrot_core_headers, "${CMAKE_CURRENT_SOURCE_DIR}")
+3 -1
View File
@@ -57,6 +57,8 @@ bool operator==(const CarrotEnoteV1 &a, const CarrotEnoteV1 &b)
a.anchor_enc == b.anchor_enc &&
a.view_tag == b.view_tag &&
a.tx_first_key_image == b.tx_first_key_image &&
a.return_enc == b.return_enc &&
a.asset_type == b.asset_type &&
memcmp(a.enote_ephemeral_pubkey.data, b.enote_ephemeral_pubkey.data, sizeof(mx25519_pubkey)) == 0;
}
//-------------------------------------------------------------------------------------------------------------------
@@ -70,4 +72,4 @@ bool operator==(const CarrotCoinbaseEnoteV1 &a, const CarrotCoinbaseEnoteV1 &b)
memcmp(a.enote_ephemeral_pubkey.data, b.enote_ephemeral_pubkey.data, sizeof(mx25519_pubkey)) == 0;
}
//-------------------------------------------------------------------------------------------------------------------
} //namespace carrot
} //namespace carrot
+4
View File
@@ -71,6 +71,10 @@ struct CarrotEnoteV1 final
mx25519_pubkey enote_ephemeral_pubkey;
/// L_0
crypto::key_image tx_first_key_image;
/// return_enc
encrypted_return_pubkey_t return_enc;
/// asset_type
std::string asset_type;
};
/// equality operators
+5
View File
@@ -71,4 +71,9 @@ static constexpr const unsigned int CARROT_MIN_TX_OUTPUTS = 2;
static constexpr const unsigned int CARROT_MAX_TX_OUTPUTS = 8;
static constexpr const unsigned int CARROT_MIN_TX_INPUTS = 1;
static constexpr const unsigned int CARROT_MAX_TX_INPUTS = 8;
// SPARC addressing protocol domain separators
static constexpr const unsigned char SPARC_DOMAIN_SEP_RETURN_PUBKEY_ENCRYPTION_MASK[] = "SPARC return pubkey encryption mask";
static constexpr const unsigned char SPARC_DOMAIN_SEP_RETURN_ADDRESS_SCALAR[] = "SPARC return address scalar";
} //namespace carrot
+10
View File
@@ -82,6 +82,16 @@ encrypted_amount_t operator^(const encrypted_amount_t &a, const encrypted_amount
return xor_bytes(a, b);
}
//-------------------------------------------------------------------------------------------------------------------
bool operator==(const encrypted_return_pubkey_t &a, const encrypted_return_pubkey_t &b)
{
return memcmp(&a, &b, sizeof(encrypted_return_pubkey_t)) == 0;
}
//-------------------------------------------------------------------------------------------------------------------
encrypted_return_pubkey_t operator^(const encrypted_return_pubkey_t &a, const encrypted_return_pubkey_t &b)
{
return xor_bytes(a, b);
}
//-------------------------------------------------------------------------------------------------------------------
bool operator==(const payment_id_t &a, const payment_id_t &b)
{
return memcmp(&a, &b, sizeof(payment_id_t)) == 0;
+12
View File
@@ -96,6 +96,13 @@ struct input_context_t final
unsigned char bytes[INPUT_CONTEXT_BYTES];
};
// SPARC encrypted return public key
constexpr std::size_t ENCRYPTED_RETURN_PUBKEY_BYTES{32};
struct encrypted_return_pubkey_t final
{
unsigned char bytes[ENCRYPTED_RETURN_PUBKEY_BYTES];
};
/// overloaded operators: address tag
bool operator==(const janus_anchor_t &a, const janus_anchor_t &b);
static inline bool operator!=(const janus_anchor_t &a, const janus_anchor_t &b) { return !(a == b); }
@@ -119,6 +126,11 @@ static inline bool operator!=(const input_context_t &a, const input_context_t &b
bool operator==(const view_tag_t &a, const view_tag_t &b);
static inline bool operator!=(const view_tag_t &a, const view_tag_t &b) { return !(a == b); }
/// overloaded operators: encrypted return pubkey
bool operator==(const encrypted_return_pubkey_t &a, const encrypted_return_pubkey_t &b);
static inline bool operator!=(const encrypted_return_pubkey_t &a, const encrypted_return_pubkey_t &b) { return !(a == b); }
encrypted_return_pubkey_t operator^(const encrypted_return_pubkey_t &a, const encrypted_return_pubkey_t &b);
/// generate a random janus anchor
janus_anchor_t gen_janus_anchor();
/// generate a random (non-zero) payment ID
+11
View File
@@ -160,6 +160,17 @@ struct view_balance_secret_device
virtual void make_internal_sender_receiver_secret(const mx25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
crypto::hash &s_sender_receiver_out) const = 0;
/**
* brief: make_internal_return_privkey - make internal return private key, given non-secret data
* k_return = H_32(s_vb || input_context || Ko)
* param: input_context - input_context
* param: onetime_address - Ko
* outparam: return_privkey_out - k_return
*/
virtual void make_internal_return_privkey(const input_context_t &input_context,
const crypto::public_key &onetime_address,
crypto::secret_key &return_privkey_out) const = 0;
virtual ~view_balance_secret_device() = default;
};
+7
View File
@@ -86,6 +86,13 @@ void view_balance_secret_ram_borrowed_device::make_internal_sender_receiver_secr
s_sender_receiver_out);
}
//-------------------------------------------------------------------------------------------------------------------
void view_balance_secret_ram_borrowed_device::make_internal_return_privkey(const input_context_t &input_context,
const crypto::public_key &onetime_address,
crypto::secret_key &return_privkey_out) const
{
make_sparc_return_privkey(to_bytes(m_s_view_balance), input_context, onetime_address, return_privkey_out);
}
//-------------------------------------------------------------------------------------------------------------------
void generate_address_secret_ram_borrowed_device::make_index_extension_generator(
const std::uint32_t major_index,
const std::uint32_t minor_index,
+4
View File
@@ -78,6 +78,10 @@ public:
const input_context_t &input_context,
crypto::hash &s_sender_receiver_out) const override;
void make_internal_return_privkey(const input_context_t &input_context,
const crypto::public_key &onetime_address,
crypto::secret_key &return_privkey_out) const override;
protected:
const crypto::secret_key &m_s_view_balance;
};
+20
View File
@@ -190,6 +190,26 @@ void make_carrot_view_tag(const unsigned char s_sender_receiver_unctx[32],
derive_bytes_3(transcript.data(), transcript.size(), s_sender_receiver_unctx, &view_tag_out);
}
//-------------------------------------------------------------------------------------------------------------------
void make_sparc_return_privkey(const unsigned char s_sender_receiver_unctx[32],
const input_context_t &input_context,
const crypto::public_key &onetime_address,
crypto::secret_key &return_privkey_out)
{
// k_return = H_32(s_sr || input_context || Ko)
const auto transcript = sp::make_fixed_transcript<SPARC_DOMAIN_SEP_RETURN_ADDRESS_SCALAR>(input_context, onetime_address);
derive_scalar(transcript.data(), transcript.size(), s_sender_receiver_unctx, &return_privkey_out);
}
//-------------------------------------------------------------------------------------------------------------------
void make_sparc_return_pubkey_encryption_mask(const unsigned char s_sender_receiver_unctx[32],
const input_context_t &input_context,
const crypto::public_key &onetime_address,
encrypted_return_pubkey_t &return_pubkey_mask_out)
{
// m_return = H_32(s_sr || input_context || Ko)
const auto transcript = sp::make_fixed_transcript<SPARC_DOMAIN_SEP_RETURN_PUBKEY_ENCRYPTION_MASK>(input_context, onetime_address);
derive_bytes_32(transcript.data(), transcript.size(), s_sender_receiver_unctx, &return_pubkey_mask_out);
}
//-------------------------------------------------------------------------------------------------------------------
input_context_t make_carrot_input_context_coinbase(const std::uint64_t block_index)
{
// input_context = "C" || IntToBytes256(block_index)
+24
View File
@@ -125,6 +125,30 @@ void make_carrot_view_tag(const unsigned char s_sender_receiver_unctx[32],
const crypto::public_key &onetime_address,
view_tag_t &view_tag_out);
/**
* brief: make_sparc_return_privkey - return private key, given non-secret data
* k_return = H_32(s_sr || input_context || Ko)
* param: s_sender_receiver_unctx - s_sr
* param: input_context - input_context
* param: onetime_address - Ko
* outparam: return_privkey_out - k_return
*/
void make_sparc_return_privkey(const unsigned char s_sender_receiver_unctx[32],
const input_context_t &input_context,
const crypto::public_key &onetime_address,
crypto::secret_key &return_privkey_out);
/**
* brief: make_sparc_return_pubkey_encryption_mask - used for hiding return pubkey
* vt = H_32(s_sr || input_context || Ko)
* param: s_sender_receiver_unctx - s_sr
* param: input_context - input_context
* param: onetime_address - Ko
* outparam: return_pubkey_mask_out - m_return
*/
void make_sparc_return_pubkey_encryption_mask(const unsigned char s_sender_receiver_unctx[32],
const input_context_t &input_context,
const crypto::public_key &onetime_address,
encrypted_return_pubkey_t &return_pubkey_mask_out);
/**
* brief: make_carrot_input_context_coinbase - input context for a sender-receiver secret (coinbase txs)
* input_context = "C" || IntToBytes256(block_index)
* param: block_index - block index of the coinbase tx
+135
View File
@@ -0,0 +1,135 @@
// Copyright (c) 2024, Salvium (author: SRCG)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// A 'return address' is a proposal to facilitate pseudonymous transfers of received funds
// back to the originating wallet, as per the previously-published "Return Address Scheme"
// published by knaccc at https://github.com/monero-project/research-lab/issues/53
// This code is designed to implement the F point management and zero-knowledge proofs
// required to support the "Return Address Scheme" in Carrot.
// Carrot: Cryptonote Address For Rerandomizable-RingCT-Output Transactions
//paired header
#include "sparc.h"
//local headers
#include "crypto/crypto.h"
#include "crypto/generators.h"
#include "ringct/rctOps.h"
#include "ringct/rctTypes.h"
#include "crypto/hash.h"
#include "address_utils.h"
#include "misc_log_ex.h"
#include <vector>
#include <array>
namespace carrot {
// Optimized function to hash a vector of keys into a scalar
rct::key hash_to_scalar(std::vector<rct::key>& keys) {
// Create a fixed-size buffer large enough to hold all keys and a domain separator
const char* domain_separator = "sparc_spend_authority_proof";
const size_t domain_separator_length = strlen(domain_separator);
size_t total_size = keys.size() * sizeof(rct::key) + domain_separator_length;
std::vector<uint8_t> data(total_size);
// Copy the keys into the buffer
size_t offset = 0;
for (const auto& key : keys) {
std::memcpy(data.data() + offset, key.bytes, sizeof(rct::key));
offset += sizeof(rct::key);
}
// Add the domain separator "sparc_spend_authority_proof" at the _end_ of the buffer
std::memcpy(data.data() + offset, domain_separator, domain_separator_length);
// Hash the concatenated data into a fixed-size hash
rct::key hash_output;
keccak((const uint8_t *)data.data(), total_size, hash_output.bytes, sizeof(rct::key));
sc_reduce32(hash_output.bytes); // Reduce to valid scalar
return hash_output;
}
// Function to generate the zero-knowledge proof
void make_sparc_spend_authority_proof(const rct::key &x, const rct::key &y, const rct::key &K_o, rct::zk_proof &proof_out) {
// Step 1: Generate random scalars r_x and r_y
rct::key r_x = rct::skGen(); // Random scalar for G commitment
rct::key r_y = rct::skGen(); // Random scalar for T commitment
// Step 2: Calculate commitment by summing terms for G and T
rct::key commitment;
rct::key commitment_G = rct::scalarmultBase(r_x); // r_x * G
rct::key commitment_T = rct::scalarmultKey(rct::pk2rct(crypto::get_T()), r_y); // r_y * T (using T generator)
commitment = rct::addKeys(commitment_G, commitment_T); // R = r_xG + r_yT
// Step 3: Calculate the challenge scalar
std::vector<rct::key> keys{commitment, K_o};
rct::key challenge = rct::hash_to_scalar(keys); // c = H(R || K_o)
// Step 4: Calculate responses
rct::key response_x;
sc_muladd(response_x.bytes, challenge.bytes, x.bytes, r_x.bytes); // z_x = r_x + c * x
sc_reduce32(response_x.bytes);
rct::key response_y;
sc_muladd(response_y.bytes, challenge.bytes, y.bytes, r_y.bytes); // z_y = r_y + c * y
sc_reduce32(response_y.bytes);
// Step 5: Construct and return the proof
proof_out.R = commitment;
proof_out.z1 = response_x;
proof_out.z2 = response_y;
// Step 6: Sanity checks
rct::key resC = rct::addKeys(commitment, rct::scalarmultKey(K_o, challenge));
rct::key resZ = rct::addKeys(rct::scalarmultBase(response_x), rct::scalarmultKey(rct::pk2rct(crypto::get_T()), response_y));
if (!rct::equalKeys(resZ, resC)) assert(false);
}
// Function to verify the zero-knowledge proof
bool verify_sparc_spend_authority_proof(const rct::zk_proof &proof, const rct::key &K_o) {
// Step 1: calculate the challenge
std::vector<rct::key> keys{proof.R, K_o};
rct::key recomputed_challenge = rct::hash_to_scalar(keys);
// Step 2: Calculate z_xG + x_yT
rct::key z_xG = rct::scalarmultBase(proof.z1); // z1 * G
rct::key x_yT = rct::scalarmultKey(rct::pk2rct(crypto::get_T()), proof.z2); // z2 * T
rct::key resZ = rct::addKeys(z_xG, x_yT); // z_xG + x_yT
// Step 3: Calculate R + cK_o
rct::key resC = rct::addKeys(proof.R, rct::scalarmultKey(K_o, recomputed_challenge)); // R + cK_o
// Step 4: verify z_xG + x_yT ?= R + cK_o
return rct::equalKeys(resZ, resC);
}
} // namespace carrot
+59
View File
@@ -0,0 +1,59 @@
// Copyright (c) 2024, Salvium (author: SRCG)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// A 'return address' is a proposal to facilitate pseudonymous transfers of received funds
// back to the originating wallet, as per the previously-published "Return Address Scheme"
// published by knaccc at https://github.com/monero-project/research-lab/issues/53
// This code is designed to implement the F point management and zero-knowledge proofs
// required to support the "Return Address Scheme" in Carrot.
// Carrot: Cryptonote Address For Rerandomizable-RingCT-Output Transactions
#pragma once
#include "ringct/rctTypes.h"
namespace carrot
{
/**
* brief: make_sparc_spend_authority_proof - generate the zero-knowledge proof for spend authority for SPARC output pubkey
* param: x - G-term for the given output key
* param: y - T-term for the given output key
* param: K_o - output key to generate spend authority proof for
* outparam: proof_out - the complete spend authority proof
*/
void make_sparc_spend_authority_proof(const rct::key &x, const rct::key &y, const rct::key &K_o, rct::zk_proof &proof_out);
/**
* brief: verify_sparc_spend_authority_proof - verify the zero-knowledge proof for spend authority for SPARC output pubkey
* param: proof - the zero-knowledge proof to verify
* param: K_o - output key to verify the spend authority proof for
* return: true if proof is valid, false otherwise
*/
bool verify_sparc_spend_authority_proof(const rct::zk_proof &proof, const rct::key &K_o);
} //namespace carrot