Skip to main content

binius_circuits/secp256k1/
point.rs

1// Copyright 2025 Irreducible Inc.
2use binius_core::word::Word;
3use binius_frontend::{CircuitBuilder, Wire};
4
5use super::common::{N_LIMBS, coord_zero, coords_gen};
6use crate::bignum::{BigUint, select as select_biguint};
7
8/// Curve point in affine form - a tuple `(x, y)` that satisfies `y^2 = x^3 + 7`,
9/// or `(0, 0)` for additive identity (point at infinity).
10#[derive(Clone)]
11pub struct Secp256k1Affine {
12	pub x: BigUint,
13	pub y: BigUint,
14	pub is_point_at_infinity: Wire,
15}
16
17impl Secp256k1Affine {
18	/// Point at infinity - the identity element.
19	pub fn point_at_infinity(b: &CircuitBuilder) -> Self {
20		let zero = coord_zero(b);
21		Self {
22			x: zero.clone(),
23			y: zero,
24			is_point_at_infinity: b.add_constant(Word::ALL_ONE),
25		}
26	}
27
28	/// Generator basepoint.
29	pub fn generator(b: &CircuitBuilder) -> Self {
30		let (x, y) = coords_gen(b);
31		Self {
32			x,
33			y,
34			is_point_at_infinity: b.add_constant(Word::ZERO),
35		}
36	}
37
38	/// Return point-at-infinity unless the MSB-boolean `cond` is true, then pass the point
39	/// unchanged.
40	pub fn pai_unless(&self, b: &CircuitBuilder, cond: Wire) -> Secp256k1Affine {
41		let is_point_at_infinity =
42			b.select(cond, self.is_point_at_infinity, b.add_constant(Word::ALL_ONE));
43		Secp256k1Affine {
44			x: self.x.clone(),
45			y: self.y.clone(),
46			is_point_at_infinity,
47		}
48	}
49
50	/// Flattens the point into a single wire list: x limbs, then y limbs, then the infinity flag.
51	///
52	/// This is the layout a wire-level multiplexer selects over.
53	///
54	/// # Panics
55	///
56	/// Panics unless both coordinates carry the full limb count.
57	pub(crate) fn to_wires(&self) -> Vec<Wire> {
58		assert_eq!(self.x.limbs.len(), N_LIMBS);
59		assert_eq!(self.y.limbs.len(), N_LIMBS);
60
61		let mut wires = Vec::with_capacity(2 * N_LIMBS + 1);
62		wires.extend_from_slice(&self.x.limbs);
63		wires.extend_from_slice(&self.y.limbs);
64		wires.push(self.is_point_at_infinity);
65		wires
66	}
67
68	/// Rebuilds a point from the flat wire layout, undoing the flattening.
69	///
70	/// # Panics
71	///
72	/// Panics unless the list is exactly two coordinates plus the infinity flag long.
73	pub(crate) fn from_wires(wires: &[Wire]) -> Self {
74		assert_eq!(wires.len(), 2 * N_LIMBS + 1);
75		Self {
76			x: BigUint {
77				limbs: wires[..N_LIMBS].to_vec(),
78			},
79			y: BigUint {
80				limbs: wires[N_LIMBS..2 * N_LIMBS].to_vec(),
81			},
82			is_point_at_infinity: wires[2 * N_LIMBS],
83		}
84	}
85}
86
87/// Conditionally selects between two affine secp256k1 points.
88///
89/// # Arguments
90/// * `builder` - Circuit builder for constraint generation
91/// * `cond` - an MSB-boolean
92/// * `t` - Value to select when cond is true (MSB=1)
93/// * `f` - Value to select when cond is false (MSB=0)
94///
95/// # Return value
96/// Selects `t` if `cond` is true, otherwise selects `f`.
97pub fn select(
98	b: &CircuitBuilder,
99	cond: Wire,
100	pt: &Secp256k1Affine,
101	pf: &Secp256k1Affine,
102) -> Secp256k1Affine {
103	let x = select_biguint(b, cond, &pt.x, &pf.x);
104	let y = select_biguint(b, cond, &pt.y, &pf.y);
105	let is_point_at_infinity = b.select(cond, pt.is_point_at_infinity, pf.is_point_at_infinity);
106	Secp256k1Affine {
107		x,
108		y,
109		is_point_at_infinity,
110	}
111}