Skip to content

Commit b63e333

Browse files
committed
fix(core): preserve transcript commitment order
The SDK correlates returned hash openings with the input Commit by index. Preserve builder insertion order while continuing to suppress duplicate commitments. Assisted-by: GPT-5 Signed-off-by: Wondertan <hlibwondertan@gmail.com>
1 parent 0fe3c32 commit b63e333

1 file changed

Lines changed: 40 additions & 5 deletions

File tree

crates/core/src/transcript/commit.rs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Transcript commitments.
22
3-
use std::{collections::HashSet, fmt};
3+
use std::fmt;
44

55
use rangeset::iter::{FromRangeIterator, IntoRangeIterator};
66
use serde::{Deserialize, Serialize};
@@ -68,6 +68,8 @@ impl TranscriptCommitConfig {
6868
}
6969

7070
/// Returns an iterator over the hash commitment indices.
71+
///
72+
/// Commitments are returned in the order they were added to the builder.
7173
pub fn iter_hash(&self) -> impl Iterator<Item = (&(Direction, RangeSet<usize>), &HashAlgId)> {
7274
self.commits.iter().map(|(idx, kind)| match kind {
7375
TranscriptCommitmentKind::Hash { alg } => (idx, alg),
@@ -90,7 +92,7 @@ impl TranscriptCommitConfig {
9092
pub struct TranscriptCommitConfigBuilder<'a> {
9193
transcript: &'a Transcript,
9294
default_kind: TranscriptCommitmentKind,
93-
commits: HashSet<((Direction, RangeSet<usize>), TranscriptCommitmentKind)>,
95+
commits: Vec<((Direction, RangeSet<usize>), TranscriptCommitmentKind)>,
9496
}
9597

9698
impl<'a> TranscriptCommitConfigBuilder<'a> {
@@ -101,7 +103,7 @@ impl<'a> TranscriptCommitConfigBuilder<'a> {
101103
default_kind: TranscriptCommitmentKind::Hash {
102104
alg: HashAlgId::BLAKE3,
103105
},
104-
commits: HashSet::default(),
106+
commits: Vec::default(),
105107
}
106108
}
107109

@@ -145,7 +147,10 @@ impl<'a> TranscriptCommitConfigBuilder<'a> {
145147
));
146148
}
147149

148-
self.commits.insert(((direction, idx), kind));
150+
let commit = ((direction, idx), kind);
151+
if !self.commits.contains(&commit) {
152+
self.commits.push(commit);
153+
}
149154

150155
Ok(self)
151156
}
@@ -203,7 +208,7 @@ impl<'a> TranscriptCommitConfigBuilder<'a> {
203208
/// Builds the configuration.
204209
pub fn build(self) -> Result<TranscriptCommitConfig, TranscriptCommitConfigBuilderError> {
205210
Ok(TranscriptCommitConfig {
206-
commits: Vec::from_iter(self.commits),
211+
commits: self.commits,
207212
})
208213
}
209214
}
@@ -279,4 +284,34 @@ mod tests {
279284
assert!(builder.commit_sent(&(10..15)).is_err());
280285
assert!(builder.commit_recv(&(10..15)).is_err());
281286
}
287+
288+
#[test]
289+
fn test_commitment_order_matches_insertion_order() {
290+
let transcript = Transcript::new([0; 12], [0; 12]);
291+
let mut builder = TranscriptCommitConfigBuilder::new(&transcript);
292+
293+
builder.commit_recv(&(8..10)).unwrap();
294+
builder.commit_sent(&(1..3)).unwrap();
295+
builder.commit_recv(&(4..6)).unwrap();
296+
builder.commit_recv(&(8..10)).unwrap();
297+
298+
let config = builder.build().unwrap();
299+
let commits = config
300+
.iter_hash()
301+
.map(|((direction, idx), alg)| (*direction, idx.clone(), *alg))
302+
.collect::<Vec<_>>();
303+
304+
assert_eq!(
305+
commits,
306+
vec![
307+
(
308+
Direction::Received,
309+
RangeSet::from(8..10),
310+
HashAlgId::BLAKE3
311+
),
312+
(Direction::Sent, RangeSet::from(1..3), HashAlgId::BLAKE3),
313+
(Direction::Received, RangeSet::from(4..6), HashAlgId::BLAKE3),
314+
]
315+
);
316+
}
282317
}

0 commit comments

Comments
 (0)