FCMP++: output_to_tuple {output pubkey, commitment} -> {O,I,C}
What changed, and why it matters
This commit adds new code for an upcoming Monero privacy feature called FCMP++. It creates a helper function that converts an output's public key and commitment into a special three-part tuple used by the new system. The commit is defensive in nature: it carefully clears mathematical 'torsion' from points, rejects identity points, and explicitly avoids a subtle double-spend risk that could occur if the wrong version of a key were used around a network upgrade. There is no indication this commit fixes an active bug or vulnerability; it appears to be a building block for future functionality.
No immediate security action is required. Treat this as routine review of new FCMP++ infrastructure. Monitor subsequent commits for integration of `output_to_tuple` into consensus/validation paths and ensure the torsion-clearing and key-image derivation rules are consistently applied across all call sites.
Security signals we found
Defensive torsion clearing with identity checks for O and C
Explicit anti-double-spend safeguard: key-image generator derived from original output pubkey, not torsion-cleared O
Variant-based handling of Legacy vs Carrot V1 output semantics
No bug fix, CVE reference, or incident disclosure present in commit or supplied references
Evidence from the diff
The patch introduces fcmp_pp::curve_trees::output_to_tuple(), which maps an OutputPair (Legacy or Carrot V1) to an OutputTuple {O, I, C}. O and C are torsion-cleared valid curve points; I is a key-image generator derived from the original output pubkey. The code deliberately derives I from the un-cleared pubkey to prevent a fork-time double-spend where a torsioned output would yield one key image pre-fork and another post-fork. Legacy outputs skip torsion clearing and use the biased hash-to-point; Carrot V1 outputs are already torsion-checked and use the unbiased hash-to-point. Identity checks and debug assertions are included. The change is preparatory and adds types, helpers, and build scaffolding.
Changed components
src/fcmp_pp/curve_trees.cppsrc/fcmp_pp/curve_trees.hsrc/fcmp_pp/fcmp_pp_types.cppsrc/fcmp_pp/fcmp_pp_types.hsrc/fcmp_pp/fcmp_pp_rust/fcmp++.hsrc/fcmp_pp/fcmp_pp_rust/CMakeLists.txtsrc/fcmp_pp/ffi_api_c_compat.csrc/fcmp_pp/CMakeLists.txtInspect captured patch +452 / −1
diff --git a/src/fcmp_pp/CMakeLists.txt b/src/fcmp_pp/CMakeLists.txt
index 32ca1b4..d5eebdc 100644
--- a/src/fcmp_pp/CMakeLists.txt
+++ b/src/fcmp_pp/CMakeLists.txt
@@ -27,10 +27,14 @@
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set(fcmp_pp_sources
- fcmp_pp_crypto.cpp)
+ curve_trees.cpp
+ fcmp_pp_crypto.cpp
+ fcmp_pp_types.cpp)
monero_find_all_headers(fcmp_pp_headers "${CMAKE_CURRENT_SOURCE_DIR}")
+add_subdirectory(fcmp_pp_rust)
+
monero_add_library(fcmp_pp
${fcmp_pp_sources}
${fcmp_pp_headers})
diff --git a/src/fcmp_pp/curve_trees.cpp b/src/fcmp_pp/curve_trees.cpp
new file mode 100644
index 0000000..a533390
--- /dev/null
+++ b/src/fcmp_pp/curve_trees.cpp
@@ -0,0 +1,109 @@
+// Copyright (c) 2024, The Monero Project
+//
+// 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.
+
+#include "curve_trees.h"
+
+#include "crypto/crypto.h"
+#include "fcmp_pp_crypto.h"
+#include "fcmp_pp_types.h"
+#include "profile_tools.h"
+
+
+namespace fcmp_pp
+{
+namespace curve_trees
+{
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+OutputTuple output_to_tuple(const OutputPair &output_pair)
+{
+ const crypto::public_key &output_pubkey = output_pubkey_cref(output_pair);
+ const crypto::ec_point &commitment = commitment_cref(output_pair);
+
+ TIME_MEASURE_NS_START(derive_key_image_generator_ns);
+
+ // Derive key image generator using the original output pubkey.
+ // CRITICAL: it's CRITICALLY important that we use the original output pubkey here, and not `O` calculated later.
+ // If we were to use `O`, then an output with torsion could be double spent before and after the FCMP++ fork, since
+ // it would have 1 key image before the fork (tied to the output pubkey) and a distinct key image (tied to torsion
+ // cleared `O`).
+ crypto::ec_point I;
+ crypto::derive_key_image_generator(output_pubkey, use_biased_hash_to_point(output_pair), I);
+
+ TIME_MEASURE_NS_FINISH(derive_key_image_generator_ns);
+
+ LOG_PRINT_L3("derive_key_image_generator_ns: " << derive_key_image_generator_ns);
+
+ // Get O and C from output pubkey and commitment
+ crypto::ec_point O = output_pubkey;
+ crypto::ec_point C = commitment;
+
+ // If the output has already been checked for torsion, then we don't need to clear torsion here
+ if (!output_checked_for_torsion(output_pair))
+ {
+ TIME_MEASURE_NS_START(clear_torsion_ns);
+
+ if (!fcmp_pp::get_valid_torsion_cleared_point_vartime(output_pubkey, O))
+ throw std::runtime_error("O is invalid for insertion to tree");
+ if (!fcmp_pp::get_valid_torsion_cleared_point_vartime(commitment, C))
+ throw std::runtime_error("C is invalid for insertion to tree");
+
+ if (O != output_pubkey)
+ LOG_PRINT_L2("Output pubkey has torsion: " << output_pubkey);
+ if (C != commitment)
+ LOG_PRINT_L2("Commitment has torsion: " << commitment);
+
+ TIME_MEASURE_NS_FINISH(clear_torsion_ns);
+
+ LOG_PRINT_L3("clear_torsion_ns: " << clear_torsion_ns);
+ }
+
+#if !defined(NDEBUG)
+ {
+ // Debug build safety checks
+ crypto::ec_point O_debug;
+ crypto::ec_point C_debug;
+ assert(fcmp_pp::get_valid_torsion_cleared_point_vartime(output_pubkey, O_debug));
+ assert(fcmp_pp::get_valid_torsion_cleared_point_vartime(commitment, C_debug));
+ assert(O == O_debug);
+ assert(C == C_debug);
+ }
+#endif
+
+ // Redundant check for safety
+ if (O == fcmp_pp::EC_I)
+ throw std::runtime_error("O cannot equal identity");
+ if (C == fcmp_pp::EC_I)
+ throw std::runtime_error("C cannot equal identity");
+
+ return output_tuple_from_bytes(O, I, C);
+}
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+} //namespace curve_trees
+} //namespace fcmp_pp
diff --git a/src/fcmp_pp/curve_trees.h b/src/fcmp_pp/curve_trees.h
new file mode 100644
index 0000000..ceb62f3
--- /dev/null
+++ b/src/fcmp_pp/curve_trees.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2024, The Monero Project
+//
+// 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.
+
+#pragma once
+
+#include "crypto/crypto.h"
+#include "fcmp_pp_types.h"
+#include "misc_log_ex.h"
+
+
+namespace fcmp_pp
+{
+namespace curve_trees
+{
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+OutputTuple output_to_tuple(const OutputPair &output_pair);
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+} //namespace curve_trees
+} //namespace fcmp_pp
diff --git a/src/fcmp_pp/fcmp_pp_rust/CMakeLists.txt b/src/fcmp_pp/fcmp_pp_rust/CMakeLists.txt
new file mode 100644
index 0000000..bdad6f5
--- /dev/null
+++ b/src/fcmp_pp/fcmp_pp_rust/CMakeLists.txt
@@ -0,0 +1,27 @@
+# Copyright (c) 2024, The Monero Project
+#
+# 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.
diff --git a/src/fcmp_pp/fcmp_pp_rust/fcmp++.h b/src/fcmp_pp/fcmp_pp_rust/fcmp++.h
new file mode 100644
index 0000000..a4f2e93
--- /dev/null
+++ b/src/fcmp_pp/fcmp_pp_rust/fcmp++.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2025, The Monero Project
+//
+// 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.
+
+#pragma once
+
+#include <stdint.h>
+
+
+struct OutputTuple
+{
+ uint8_t O[32];
+ uint8_t I[32];
+ uint8_t C[32];
+};
diff --git a/src/fcmp_pp/fcmp_pp_types.cpp b/src/fcmp_pp/fcmp_pp_types.cpp
new file mode 100644
index 0000000..517f69f
--- /dev/null
+++ b/src/fcmp_pp/fcmp_pp_types.cpp
@@ -0,0 +1,108 @@
+// Copyright (c) 2024, The Monero Project
+//
+// 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.
+
+#include "fcmp_pp_types.h"
+
+#include "misc_log_ex.h"
+
+namespace fcmp_pp
+{
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+// Helpers
+//----------------------------------------------------------------------------------------------------------------------
+OutputTuple output_tuple_from_bytes(const crypto::ec_point &O, const crypto::ec_point &I, const crypto::ec_point &C)
+{
+ OutputTuple output_tuple;
+
+ static_assert(sizeof(output_tuple.O) == sizeof(O), "unexpected sizeof O");
+ static_assert(sizeof(output_tuple.I) == sizeof(I), "unexpected sizeof I");
+ static_assert(sizeof(output_tuple.C) == sizeof(C), "unexpected sizeof C");
+
+ memcpy(output_tuple.O, &O, sizeof(O));
+ memcpy(output_tuple.I, &I, sizeof(I));
+ memcpy(output_tuple.C, &C, sizeof(C));
+
+ return output_tuple;
+}
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+const crypto::public_key &output_pubkey_cref(const OutputPair &output_pair)
+{
+ struct output_pair_visitor
+ {
+ const crypto::public_key &operator()(const LegacyOutputPair &o) const
+ { return o.output_pubkey; }
+ const crypto::public_key &operator()(const CarrotOutputPairV1 &o) const
+ { return o.output_pubkey; }
+ };
+
+ return std::visit(output_pair_visitor{}, output_pair);
+}
+//----------------------------------------------------------------------------------------------------------------------
+const crypto::ec_point &commitment_cref(const OutputPair &output_pair)
+{
+ struct output_pair_visitor
+ {
+ const crypto::ec_point &operator()(const LegacyOutputPair &o) const
+ { return o.commitment; }
+ const crypto::ec_point &operator()(const CarrotOutputPairV1 &o) const
+ { return o.commitment; }
+ };
+
+ return std::visit(output_pair_visitor{}, output_pair);
+}
+//----------------------------------------------------------------------------------------------------------------------
+bool output_checked_for_torsion(const OutputPair &output_pair)
+{
+ struct output_pair_visitor
+ {
+ bool operator()(const LegacyOutputPair&) const
+ { return false; }
+ bool operator()(const CarrotOutputPairV1&) const
+ { return true; }
+ };
+
+ return std::visit(output_pair_visitor{}, output_pair);
+}
+//----------------------------------------------------------------------------------------------------------------------
+bool use_biased_hash_to_point(const OutputPair &output_pair)
+{
+ struct output_pair_visitor
+ {
+ bool operator()(const LegacyOutputPair&) const
+ { return true; }
+ bool operator()(const CarrotOutputPairV1&) const
+ { return false; }
+ };
+
+ return std::visit(output_pair_visitor{}, output_pair);
+}
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+}//namespace fcmp_pp
diff --git a/src/fcmp_pp/fcmp_pp_types.h b/src/fcmp_pp/fcmp_pp_types.h
new file mode 100644
index 0000000..80a7839
--- /dev/null
+++ b/src/fcmp_pp/fcmp_pp_types.h
@@ -0,0 +1,115 @@
+// Copyright (c) 2024, The Monero Project
+//
+// 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.
+
+#pragma once
+
+#include <cstring>
+#include <type_traits>
+#include <variant>
+
+#include "crypto/crypto.h"
+#include "fcmp_pp_rust/fcmp++.h"
+
+
+namespace fcmp_pp
+{
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+// Rust types
+//----------------------------------------------------------------------------------------------------------------------
+using OutputTuple = ::OutputTuple;
+//----------------------------------------------------------------------------------------------------------------------
+OutputTuple output_tuple_from_bytes(const crypto::ec_point &O, const crypto::ec_point &I, const crypto::ec_point &C);
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+// C++ types
+//----------------------------------------------------------------------------------------------------------------------
+// Curve trees types
+//----------------------------------------------------------------------------------------------------------------------
+// Output pubkey and commitment, ready to be converted to a leaf tuple
+// - From {output_pubkey,commitment} -> {O,C} -> {O,I,C} -> {O.x,O.y,I.x,I.y,C.x,C.y}
+// - Output pairs do NOT necessarily have torsion cleared. We need the output pubkey as it exists in the chain in order
+// to derive the correct I (when deriving {O.x,O.y,I.x,I.y,C.x,C.y}). Torsion clearing O before deriving I from O
+// would enable spending a torsioned output once before FCMP++ fork and again with a different key image via FCMP++.
+template<typename T>
+struct OutputPairTemplate
+{
+ crypto::public_key output_pubkey;
+ // Uses the ec_point type to avoid a circular dep to ringct/rctTypes.h, and to differentiate from output_pubkey
+ crypto::ec_point commitment;
+
+ OutputPairTemplate(const crypto::public_key &_output_pubkey, const crypto::ec_point &_commitment):
+ output_pubkey(_output_pubkey),
+ commitment(_commitment)
+ {};
+
+ OutputPairTemplate():
+ output_pubkey{},
+ commitment{}
+ {};
+
+ // WARNING: not constant time
+ bool operator==(const OutputPairTemplate &other) const
+ {
+ return output_pubkey == other.output_pubkey
+ && commitment == other.commitment;
+ }
+};
+
+// May have torsion, use biased key image generator for I
+struct LegacyOutputPair : public OutputPairTemplate<LegacyOutputPair>{};
+// No torsion, use unbiased key image generator for I
+struct CarrotOutputPairV1 : public OutputPairTemplate<CarrotOutputPairV1>{};
+
+static_assert(sizeof(LegacyOutputPair) == (32+32), "sizeof LegacyOutputPair unexpected");
+static_assert(sizeof(CarrotOutputPairV1) == (32+32), "sizeof CarrotOutputPairV1 unexpected");
+
+static_assert(std::has_unique_object_representations_v<LegacyOutputPair>);
+static_assert(std::has_unique_object_representations_v<CarrotOutputPairV1>);
+
+using OutputPair = std::variant<LegacyOutputPair, CarrotOutputPairV1>;
+
+const crypto::public_key &output_pubkey_cref(const OutputPair &output_pair);
+const crypto::ec_point &commitment_cref(const OutputPair &output_pair);
+
+bool output_checked_for_torsion(const OutputPair &output_pair);
+bool use_biased_hash_to_point(const OutputPair &output_pair);
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+}//namespace fcmp_pp
+
+// WARNING: not constant time
+inline bool operator==(const fcmp_pp::OutputTuple &a, const fcmp_pp::OutputTuple &b)
+{
+ static_assert(sizeof(fcmp_pp::OutputTuple) == (sizeof(a.O) + sizeof(a.I) + sizeof(a.C)),
+ "unexpected sizeof OutputTuple for == implementation");
+ return
+ (memcmp(a.O, b.O, sizeof(a.O)) == 0) &&
+ (memcmp(a.I, b.I, sizeof(a.I)) == 0) &&
+ (memcmp(a.C, b.C, sizeof(a.C)) == 0);
+}
diff --git a/src/fcmp_pp/ffi_api_c_compat.c b/src/fcmp_pp/ffi_api_c_compat.c
new file mode 100644
index 0000000..c92510e
--- /dev/null
+++ b/src/fcmp_pp/ffi_api_c_compat.c
@@ -0,0 +1,3 @@
+#include "fcmp++.h"
+
+int main() { return 0; }
Why this scored 12/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.