initial import of SPARC integration into latest Carrot
This commit is contained in:
@@ -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}")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user