Skip to content

Commit

Permalink
FCMP++ seperate verify fn from prove
Browse files Browse the repository at this point in the history
  • Loading branch information
j-berman committed Jan 16, 2025
1 parent 836ba73 commit 67ff387
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 32 deletions.
17 changes: 15 additions & 2 deletions src/fcmp_pp/fcmp_pp_rust/fcmp++.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ using FcmpProveInput = uint8_t *;

using FcmpProveInputSlice = Slice<FcmpProveInput>;

using FcmpPpProofSlice = Slice<uint8_t>;

extern "C" {
HeliosPoint helios_hash_init_point();

Expand Down Expand Up @@ -183,6 +185,8 @@ CResult path_new(OutputSlice leaves,

CResult rerandomize_output(OutputBytes output);

uint8_t *pseudo_out(RerandomizedOutput rerandomized_output);

CResult o_blind(RerandomizedOutput rerandomized_output);
CResult i_blind(RerandomizedOutput rerandomized_output);
CResult i_blind_blind(RerandomizedOutput rerandomized_output);
Expand All @@ -209,9 +213,18 @@ CResult fcmp_prove_input_new(Ed25519ScalarBytes x,
BranchBlindSlice helios_branch_blinds,
BranchBlindSlice selene_branch_blinds);

void prove(const uint8_t *signable_tx_hash,
CResult prove(const uint8_t *signable_tx_hash,
FcmpProveInputSlice fcmp_prove_inputs,
TreeRoot tree_root);
uintptr_t n_tree_layers);

uintptr_t fcmp_pp_proof_size(uintptr_t n_inputs, uintptr_t n_tree_layers);

bool verify(const uint8_t *signable_tx_hash,
FcmpPpProofSlice fcmp_pp_proof_slice,
uintptr_t n_tree_layers,
TreeRoot tree_root,
Slice<const uint8_t *> pseudo_outs,
Slice<const uint8_t *> key_images);

} // extern "C"
}//namespace fcmp_pp_rust
115 changes: 93 additions & 22 deletions src/fcmp_pp/fcmp_pp_rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,18 @@ pub extern "C" fn rerandomize_output(output: OutputBytes) -> CResult<Rerandomize
CResult::ok(rerandomized_output)
}

//---------------------------------------------- PseudoOut

/// # Safety
///
/// This function assumes that the rerandomized output being passed in input was
/// allocated on the heap and returned through a CResult instance.
#[no_mangle]
pub unsafe extern "C" fn pseudo_out(output: *const RerandomizedOutput) -> *const u8 {
let rerandomized_output = unsafe { output.read() };
c_u8_32(rerandomized_output.input().C_tilde().to_bytes())
}

//---------------------------------------------- OBlind

/// # Safety
Expand Down Expand Up @@ -595,15 +607,14 @@ pub unsafe extern "C" fn fcmp_prove_input_new(

/// # Safety
///
/// This function assumes that the x and y are 32 byte Ed25519 scalars, and that all other
/// types passed as input were allocated on the heap (via Box::into_raw(Box::new()))
/// This function assumes that the signable_tx_hash is 32 bytes and that the inputs are a slice
/// of inputs returned from fcmp_prove_input_new.
#[no_mangle]
pub unsafe extern "C" fn prove(
signable_tx_hash: *const u8,
inputs: FcmpProveInputSlice,
// TODO: tree_root is only used for verify, remove it
tree_root: *const TreeRoot<Selene, Helios>,
) {
n_tree_layers: usize,
) -> CResult<*const u8, ()> {
let signable_tx_hash = unsafe { core::slice::from_raw_parts(signable_tx_hash, 32) };
let signable_tx_hash: [u8; 32] = signable_tx_hash.try_into().unwrap();

Expand All @@ -612,7 +623,6 @@ pub unsafe extern "C" fn prove(
let inputs: Vec<FcmpProveInput> = inputs.iter().map(|x| unsafe { x.read() }).collect();

// SAL proofs
let mut key_images: Vec<EdwardsPoint> = vec![];
let sal_proofs = inputs
.iter()
.map(|prove_input| {
Expand All @@ -624,14 +634,15 @@ pub unsafe extern "C" fn prove(
let input = rerandomized_output.input();
let opening = OpenedInputTuple::open(rerandomized_output.clone(), &x, &y).unwrap();

#[allow(non_snake_case)]
let (L, spend_auth_and_linkability) =
let (key_image, spend_auth_and_linkability) =
SpendAuthAndLinkability::prove(&mut OsRng, signable_tx_hash, opening);

// assert_eq!(prove_input.path.output.O(), EdwardsPoint((*x * *EdwardsPoint::generator()) + (*y * *EdwardsPoint(T()))));
assert_eq!(prove_input.path.output.I() * x, L);
assert_eq!(
prove_input.path.output.O(),
EdwardsPoint((*x * *EdwardsPoint::generator()) + (*y * *EdwardsPoint(T())))
);
assert_eq!(prove_input.path.output.I() * x, key_image);

key_images.push(L);
(input, spend_auth_and_linkability)
})
.collect();
Expand All @@ -658,7 +669,9 @@ pub unsafe extern "C" fn prove(

assert_eq!(branches.necessary_c1_blinds(), c1_branch_blinds.len());
assert_eq!(branches.necessary_c2_blinds(), c2_branch_blinds.len());

let n_branch_blinds = (c1_branch_blinds.len() + c2_branch_blinds.len()) / inputs.len();
assert_eq!(n_tree_layers, n_branch_blinds + 1);

let blinded_branches = branches
.blind(output_blinds, c1_branch_blinds, c2_branch_blinds)
Expand All @@ -670,34 +683,92 @@ pub unsafe extern "C" fn prove(
// Combine SAL proofs and membership proof
let fcmp_plus_plus = FcmpPlusPlus::new(sal_proofs, fcmp);

// let mut buf = vec![];
// fcmp_plus_plus.write(&mut buf).unwrap();
let mut buf = vec![];
fcmp_plus_plus.write(&mut buf).unwrap();
assert_eq!(fcmp_pp_proof_size(inputs.len(), n_tree_layers), buf.len());

let mut ed_verifier = multiexp::BatchVerifier::new(1);
let mut c1_verifier = generalized_bulletproofs::Generators::batch_verifier();
let mut c2_verifier = generalized_bulletproofs::Generators::batch_verifier();
// Leak the buf into a ptr that the C++ can handle
let ptr = buf.leak().as_ptr();
CResult::ok(ptr)
}

// TODO: cache a static global table for proof lens by n_inputs and n_tree_layers bc the calc is slow
#[no_mangle]
pub extern "C" fn fcmp_pp_proof_size(n_inputs: usize, n_tree_layers: usize) -> usize {
FcmpPlusPlus::proof_size(n_inputs, n_tree_layers)
}

/// # Safety
///
/// This function assumes that the signable tx hash is 32 bytes, the tree root is heap
/// allocated via a CResult, and pseudo outs and key images are 32 bytes each
#[no_mangle]
pub unsafe extern "C" fn verify(
signable_tx_hash: *const u8,
proof: Slice<u8>,
n_tree_layers: usize,
tree_root: *const TreeRoot<Selene, Helios>,
pseudo_outs: Slice<*const u8>,
key_images: Slice<*const u8>,
) -> bool {
// Early checks
let n_inputs = pseudo_outs.len;
if n_inputs == 0 || n_inputs != key_images.len {
return false;
}
if proof.len != fcmp_pp_proof_size(n_inputs, n_tree_layers) {
return false;
}

let signable_tx_hash = unsafe { core::slice::from_raw_parts(signable_tx_hash, 32) };
let signable_tx_hash: [u8; 32] = signable_tx_hash.try_into().unwrap();

let mut proof: &[u8] = proof.into();

// 32 byte pseudo outs
let pseudo_outs: &[*const u8] = pseudo_outs.into();
let pseudo_outs: Vec<[u8; 32]> = pseudo_outs
.iter()
.map(|&x| {
let x = unsafe { core::slice::from_raw_parts(x, 32) };
let mut pseudo_out = [0u8; 32];
pseudo_out.copy_from_slice(x);
pseudo_out
})
.collect();

// Read the FCMP++ proof
let fcmp_plus_plus = FcmpPlusPlus::read(&pseudo_outs, n_tree_layers, &mut proof).unwrap();

let tree_root: TreeRoot<Selene, Helios> = unsafe { tree_root.read() };

let n_layers = 1 + n_branch_blinds;
// Collect de-compressed key images into a Vec
let key_images: &[*const u8] = key_images.into();
let key_images: Vec<EdwardsPoint> = key_images
.iter()
.map(|&x| ed25519_point_from_bytes(x))
.collect();

let mut ed_verifier = multiexp::BatchVerifier::new(n_inputs);
let mut c1_verifier = generalized_bulletproofs::Generators::batch_verifier();
let mut c2_verifier = generalized_bulletproofs::Generators::batch_verifier();

// TODO: remove verify
fcmp_plus_plus
.verify(
&mut OsRng,
&mut ed_verifier,
&mut c1_verifier,
&mut c2_verifier,
tree_root,
n_layers,
n_tree_layers,
signable_tx_hash,
key_images,
)
.unwrap();

assert!(ed_verifier.verify_vartime());
assert!(SELENE_GENERATORS().verify(c1_verifier));
assert!(HELIOS_GENERATORS().verify(c2_verifier));
ed_verifier.verify_vartime()
&& SELENE_GENERATORS().verify(c1_verifier)
&& HELIOS_GENERATORS().verify(c2_verifier)
}

// https://github.com/rust-lang/rust/issues/79609
Expand Down
67 changes: 64 additions & 3 deletions src/fcmp_pp/tower_cycle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,16 @@ RerandomizedOutput rerandomize_output(const OutputBytes output)
return (RerandomizedOutput) result.value;
}

rct::key pseudo_out(const RerandomizedOutput rerandomized_output)
{
uint8_t * res_ptr = fcmp_pp_rust::pseudo_out(rerandomized_output);
rct::key res;
static_assert(sizeof(rct::key) == 32, "unexpected size of rct::key");
memcpy(&res, res_ptr, sizeof(rct::key));
free(res_ptr);
return res;
}

static Blind handle_blind_res(const std::string func, const fcmp_pp_rust::CResult &res)
{
if (res.err != nullptr)
Expand Down Expand Up @@ -435,11 +445,62 @@ FcmpProveInput fcmp_prove_input_new(const Ed25519Scalar x,
return (FcmpProveInput) res.value;
}

void prove(const crypto::hash &tx_hash,
FcmpPpProof prove(const crypto::hash &signable_tx_hash,
const FcmpProveInputs fcmp_prove_inputs,
const TreeRoot tree_root)
std::size_t n_tree_layers)
{
fcmp_pp_rust::prove(reinterpret_cast<const uint8_t*>(&tx_hash), fcmp_prove_inputs, tree_root);
auto res = fcmp_pp_rust::prove(reinterpret_cast<const uint8_t*>(&signable_tx_hash),
fcmp_prove_inputs,
n_tree_layers);

if (res.err != nullptr)
{
free(res.err);
throw std::runtime_error("failed to construct FCMP++ proof");
}

const std::size_t proof_size = fcmp_pp_rust::fcmp_pp_proof_size(fcmp_prove_inputs.len, n_tree_layers);

// res.value is a void * pointing to a uint8_t *, so cast as a double pointer
uint8_t **buf = (uint8_t**) res.value;

static_assert(sizeof(std::size_t) >= sizeof(uint8_t), "unexpected size of size_t");
FcmpPpProof proof{
.n_tree_layers = (uint8_t) n_tree_layers,
.buf = {*buf, *buf + proof_size}
};

// Free both pointers
free(*buf);
free(res.value);

return proof;
}

bool verify(const crypto::hash &signable_tx_hash,
const FcmpPpProof &fcmp_pp_proof,
const TreeRoot tree_root,
const std::vector<rct::key> &pseudo_outs,
const std::vector<crypto::key_image> &key_images)
{
std::vector<const uint8_t *> pseudo_outs_ptrs;
pseudo_outs_ptrs.reserve(pseudo_outs.size());
for (const auto &po : pseudo_outs)
pseudo_outs_ptrs.emplace_back((const uint8_t *)&po.bytes);

std::vector<const uint8_t *> key_images_ptrs;
key_images_ptrs.reserve(key_images.size());
for (const auto &ki : key_images)
key_images_ptrs.emplace_back((const uint8_t *)&ki.data);

return fcmp_pp_rust::verify(
reinterpret_cast<const uint8_t*>(&signable_tx_hash),
{fcmp_pp_proof.buf.data(), fcmp_pp_proof.buf.size()},
fcmp_pp_proof.n_tree_layers,
tree_root,
{pseudo_outs_ptrs.data(), pseudo_outs_ptrs.size()},
{key_images_ptrs.data(), key_images_ptrs.size()}
);
}
//----------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------
Expand Down
19 changes: 17 additions & 2 deletions src/fcmp_pp/tower_cycle.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ TreeRoot helios_tree_root(const Helios::Point &point);
// TODO: consider putting this section somewhere better than tower_cycle
RerandomizedOutput rerandomize_output(const OutputBytes output);

rct::key pseudo_out(const RerandomizedOutput rerandomized_output);

Blind o_blind(const RerandomizedOutput rerandomized_output);
Blind i_blind(const RerandomizedOutput rerandomized_output);
Blind i_blind_blind(const RerandomizedOutput rerandomized_output);
Expand Down Expand Up @@ -242,9 +244,22 @@ FcmpProveInput fcmp_prove_input_new(const Ed25519Scalar x,
const BranchBlinds &helios_branch_blinds,
const BranchBlinds &selene_branch_blinds);

void prove(const crypto::hash &tx_hash,
struct FcmpPpProof final
{
uint8_t n_tree_layers;
std::vector<uint8_t> buf;
};

FcmpPpProof prove(const crypto::hash &signable_tx_hash,
const FcmpProveInputs fcmp_prove_inputs,
const TreeRoot tree_root);
const std::size_t n_tree_layers);

bool verify(const crypto::hash &signable_tx_hash,
const FcmpPpProof &fcmp_pp_proof,
const TreeRoot tree_root,
const std::vector<rct::key> &pseudo_outs,
const std::vector<crypto::key_image> &key_images);

//----------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------
}//namespace tower_cycle
Expand Down
28 changes: 25 additions & 3 deletions tests/unit_tests/fcmp_pp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ TEST(fcmp_pp, prove)
std::vector<fcmp_pp::tower_cycle::BranchBlind> selene_branch_blinds;

std::vector<fcmp_pp::tower_cycle::FcmpProveInput> fcmp_prove_inputs;
std::vector<crypto::key_image> key_images;
std::vector<rct::key> pseudo_outs;

// Create proof for every leaf in the tree
for (std::size_t leaf_idx = 0; leaf_idx < global_tree.get_n_leaf_tuples(); ++leaf_idx)
Expand Down Expand Up @@ -154,6 +156,13 @@ TEST(fcmp_pp, prove)

const auto rerandomized_output = fcmp_pp::tower_cycle::rerandomize_output(output_bytes[output_idx]);

pseudo_outs.emplace_back(fcmp_pp::tower_cycle::pseudo_out(rerandomized_output));

key_images.emplace_back();
crypto::generate_key_image(rct::rct2pk(path.leaves[output_idx].O),
new_outputs.x_vec[leaf_idx],
key_images.back());

// helios scalars from selene points
std::vector<std::vector<fcmp_pp::tower_cycle::HeliosScalar>> helios_scalars;
std::vector<fcmp_pp::tower_cycle::Helios::Chunk> helios_chunks;
Expand Down Expand Up @@ -237,12 +246,25 @@ TEST(fcmp_pp, prove)
continue;

LOG_PRINT_L1("Constructing proof");
fcmp_pp::tower_cycle::prove(
crypto::hash{},
const crypto::hash tx_hash{};
const auto proof = fcmp_pp::tower_cycle::prove(
tx_hash,
{fcmp_prove_inputs.data(), fcmp_prove_inputs.size()},
tree_root
1 + tree_depth
);

bool verify = fcmp_pp::tower_cycle::verify(
tx_hash,
proof,
tree_root,
pseudo_outs,
key_images
);
ASSERT_TRUE(verify);

fcmp_prove_inputs.clear();
pseudo_outs.clear();
key_images.clear();
}
}
//----------------------------------------------------------------------------------------------------------------------

0 comments on commit 67ff387

Please sign in to comment.