From 1e35719fb1eb4b0dce9d9fab2d67dd9da6b437f7 Mon Sep 17 00:00:00 2001 From: LesterEvSe Date: Fri, 14 Aug 2026 17:49:45 +0300 Subject: [PATCH] feat: add ct functions and new operations to secp256k1 --- simf/ct_compile_check.simf | 38 ++++++++++++++ simf/lib/ct/commitment.simf | 73 +++++++++++++++++++++++++++ simf/lib/ct/relations.simf | 79 ++++++++++++++++++++++++++++++ simf/lib/secp256k1/operations.simf | 9 +++- 4 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 simf/ct_compile_check.simf create mode 100644 simf/lib/ct/commitment.simf create mode 100644 simf/lib/ct/relations.simf diff --git a/simf/ct_compile_check.simf b/simf/ct_compile_check.simf new file mode 100644 index 0000000..7de723b --- /dev/null +++ b/simf/ct_compile_check.simf @@ -0,0 +1,38 @@ +use crate::lib::ct::relations::{ + zero, add, sub, add_scaled, sub_scaled, is_balanced, assert_balanced, + is_scaled_eq, assert_scaled_eq, is_value_eq, assert_value_eq, + assert_ratio_eq, assert_sum_eq +}; +use crate::lib::ct::commitment::{ + asset_generator, value_commitment, + assert_asset_generator, assert_value_commitment, assert_opens_to +}; +use crate::lib::secp256k1::operations::point_to_ge; + +fn main() { + let p: Point = witness::P; + let q: Point = witness::Q; + let s: Scalar = witness::S; + let id: u256 = witness::ID; + let asset: Asset1 = witness::ASSET; + let amount: Amount1 = witness::AMOUNT; + + let acc: Gej = sub_scaled(add_scaled(sub(add(zero(), p), q), 2, p), 3, q); + assert!(is_balanced(acc, s)); + assert_balanced(acc, s); + + assert!(is_scaled_eq(q, 2, p, s)); + assert_scaled_eq(q, 2, p, s); + assert!(is_value_eq(p, q, s)); + assert_value_eq(p, q, s); + assert_ratio_eq(3, p, 5, q, s); + assert_sum_eq(p, q, p, s); + + let h: Gej = asset_generator(id, s); + let c: Gej = value_commitment(7, h, s); + assert!(jet::gej_is_on_curve(c)); + + assert_asset_generator(point_to_ge(p), id, s); + assert_value_commitment(point_to_ge(p), 7, point_to_ge(q), s); + assert_opens_to(asset, amount, id, 7, s, s); +} diff --git a/simf/lib/ct/commitment.simf b/simf/lib/ct/commitment.simf new file mode 100644 index 0000000..9a099c9 --- /dev/null +++ b/simf/lib/ct/commitment.simf @@ -0,0 +1,73 @@ +/* + * Confidential Transactions: commitment builders and openings. + * + * H_a = hash_to_curve(asset_id) + abf*G (asset generator) + * C = v*H_a + vbf*G (value commitment) + * + * Every assert in this module takes blinding factors, which means the value or + * asset id it checks becomes PUBLIC to anyone reading the witness. + * For checks that constrain commitments while keeping the values sealed, use + * `crate::lib::ct::relations`. + */ + +use crate::lib::asserts::{assert_eq_64, assert_eq_256}; +use crate::lib::secp256k1::operations::point_to_ge; +use crate::lib::u64::u64_into_u256; + +/// Builds the asset generator `H_a = hash_to_curve(asset_id) + abf*G`. +pub fn asset_generator(asset_id: u256, abf: Scalar) -> Gej { + jet::gej_ge_add(jet::generate(abf), jet::hash_to_curve(asset_id)) +} + +/// Builds the value commitment `C = v*asset_gen + vbf*G`. +pub fn value_commitment(v: u64, asset_gen: Gej, vbf: Scalar) -> Gej { + jet::linear_combination_1((u64_into_u256(v), asset_gen), vbf) +} + +/// Asserts that `h == hash_to_curve(asset_id) + abf*G`. +pub fn assert_asset_generator(h: Ge, asset_id: u256, abf: Scalar) { + jet::linear_verify_1(((1, jet::hash_to_curve(asset_id)), abf), h); +} + +/// Asserts that `c == v*h + vbf*G`, where `h` is the asset generator that `c` +/// commits against. +pub fn assert_value_commitment(c: Ge, v: u64, h: Ge, vbf: Scalar) { + jet::linear_verify_1(((u64_into_u256(v), h), vbf), c); +} + +/// Asserts that an `(asset, amount)` pair opens to `expected_asset_id` and +/// `expected_amount`. +pub fn assert_opens_to( + asset: Asset1, + amount: Amount1, + expected_asset_id: u256, + expected_amount: u64, + abf: Scalar, + vbf: Scalar, +) { + match asset { + Left(conf_asset: Point) => { + let h: Ge = point_to_ge(conf_asset); + assert_asset_generator(h, expected_asset_id, abf); + + match amount { + Left(conf_amount: Point) => { + assert_value_commitment(point_to_ge(conf_amount), expected_amount, h, vbf); + }, + Right(explicit_amount: u64) => assert_eq_64(explicit_amount, expected_amount), + }; + }, + Right(explicit_asset: u256) => { + assert_eq_256(explicit_asset, expected_asset_id); + + match amount { + // An explicit asset commits against the unblinded generator. + Left(conf_amount: Point) => { + let h: Ge = jet::hash_to_curve(expected_asset_id); + assert_value_commitment(point_to_ge(conf_amount), expected_amount, h, vbf); + }, + Right(explicit_amount: u64) => assert_eq_64(explicit_amount, expected_amount), + }; + }, + }; +} diff --git a/simf/lib/ct/relations.simf b/simf/lib/ct/relations.simf new file mode 100644 index 0000000..dde3981 --- /dev/null +++ b/simf/lib/ct/relations.simf @@ -0,0 +1,79 @@ +/* + * Confidential Transactions: relations between commitments. + * + * Nothing here takes a blinding factor, so nothing here discloses a committed + * value. For checks that do reveal a value or an asset id, see + * `crate::lib::ct::commitment`. + * + * A value commitment expands to + * + * C = v*H_a + vbf*G = v*H_0 + (v*abf + vbf)*G + * + */ + +use crate::lib::secp256k1::operations::{point_to_ge, point_to_gej}; + +/// The empty accumulator (the point at infinity). +pub fn zero() -> Gej { + jet::gej_infinity() +} + +/// `acc + c` +pub fn add(acc: Gej, c: Point) -> Gej { + jet::gej_ge_add(acc, point_to_ge(c)) +} + +/// `acc - c` +pub fn sub(acc: Gej, c: Point) -> Gej { + jet::gej_ge_add(acc, jet::ge_negate(point_to_ge(c))) +} + +/// `acc + k*c` +pub fn add_scaled(acc: Gej, k: Scalar, c: Point) -> Gej { + jet::gej_add(acc, jet::scale(k, point_to_gej(c))) +} + +/// `acc - k*c` +pub fn sub_scaled(acc: Gej, k: Scalar, c: Point) -> Gej { + add_scaled(acc, jet::scalar_negate(k), c) +} + +/// Returns true iff `acc == s*G`. +pub fn is_balanced(acc: Gej, s: Scalar) -> bool { + jet::gej_equiv(acc, jet::generate(s)) +} + +/// Asserts that `acc == s*G`. +pub fn assert_balanced(acc: Gej, s: Scalar) { + assert!(is_balanced(acc, s)); +} + +/// Returns true iff `q == k*p + s*G`. +pub fn is_scaled_eq(q: Point, k: Scalar, p: Point, s: Scalar) -> bool { + jet::gej_ge_equiv(jet::linear_combination_1((k, point_to_gej(p)), s), point_to_ge(q)) +} + +/// Asserts that `q == k*p + s*G`. +pub fn assert_scaled_eq(q: Point, k: Scalar, p: Point, s: Scalar,) { + jet::point_verify_1(((k, p), s), q); +} + +/// Returns true iff `p == q + s*G`. +pub fn is_value_eq(p: Point, q: Point, s: Scalar) -> bool { + is_scaled_eq(q, 1, p, s) +} + +/// Asserts that `p == q + s*G`, i.e. that two commitments hold the same value. +pub fn assert_value_eq(p: Point, q: Point, s: Scalar) { + assert_scaled_eq(q, 1, p, s); +} + +/// Asserts that `a*x == b*y + s*G`. +pub fn assert_ratio_eq(a: Scalar, x: Point, b: Scalar, y: Point, s: Scalar) { + assert_balanced(sub_scaled(add_scaled(zero(), a, x), b, y), s); +} + +/// Asserts that `c1 + c2 == c_sum + s*G`. +pub fn assert_sum_eq(c1: Point, c2: Point, c_sum: Point, s: Scalar) { + assert_balanced(sub(add(add(zero(), c1), c2), c_sum), s); +} diff --git a/simf/lib/secp256k1/operations.simf b/simf/lib/secp256k1/operations.simf index 0cfabbe..8c9d095 100644 --- a/simf/lib/secp256k1/operations.simf +++ b/simf/lib/secp256k1/operations.simf @@ -11,9 +11,16 @@ pub fn ge_to_point(p: Ge) -> Point { } } +/// Decompress a `Point` into affine coordinates. +/// Panics if `jet::decompress(p)` returns `None`. +pub fn point_to_ge(p: Point) -> Ge { + unwrap(jet::decompress(p)) +} + /// Decompress a `Point` into a Jacobian point with `z = 1`. +/// Panics if `jet::decompress(p)` returns `None`. pub fn point_to_gej(p: Point) -> Gej { - (unwrap(jet::decompress(p)), 1) + (point_to_ge(p), 1) } /// Convert the point into affine coordinates.