smite: drive commitment tests from JSON vectors - #245
Open
NishantBansal2003 wants to merge 2 commits into
Open
Conversation
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
Each BOLT 3 vector was a hand-written test repeating the same channel setup and the same three signature assertions. Move the vectors into JSON files under test_vectors/, split by channel type (Appendix C legacy, Appendix F anchors, plus custom files for edge cases the appendices miss), and run them through a shared harness. Adding a vector is now a JSON entry, not a new test. Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
erickcestari
approved these changes
Sep 11, 2026
Comment on lines
+130
to
+178
| /// Runs every commitment vector in a test vector file. | ||
| /// | ||
| /// Note: local is the opener. | ||
| pub fn run_commitment_vectors(json: &str) { | ||
| let file: TestVectorFile = serde_json::from_str(json).expect("valid test vector file"); | ||
| assert!( | ||
| !file.tests.is_empty(), | ||
| "{}: no test vectors", | ||
| file.description | ||
| ); | ||
|
|
||
| let opener_holder = file.build_holder_identity(Side::Opener); | ||
| let acceptor_holder = file.build_holder_identity(Side::Acceptor); | ||
|
|
||
| for vector in &file.tests { | ||
| let context = format!("{}: {}", file.description, vector.name); | ||
| let channel_config = file.build_channel_config(vector); | ||
| let commitment_state = file.build_commitment_state(vector); | ||
|
|
||
| // Opener signs own commitment. | ||
| assert_eq!( | ||
| channel_config.sign_holder_commitment(&commitment_state, &opener_holder), | ||
| vector.local_signature, | ||
| "{context}: local signature mismatch", | ||
| ); | ||
|
|
||
| // Acceptor signs opener's commitment. | ||
| assert!( | ||
| channel_config.verify_counterparty_signature( | ||
| &commitment_state, | ||
| &opener_holder, | ||
| &vector.remote_signature, | ||
| ), | ||
| "{context}: remote signature does not verify", | ||
| ); | ||
|
|
||
| // Opener signs the acceptor's commitment, then the acceptor verifies it. | ||
| let acceptor_commit_sig = | ||
| channel_config.sign_counterparty_commitment(&commitment_state, &opener_holder); | ||
| assert!( | ||
| channel_config.verify_counterparty_signature( | ||
| &commitment_state, | ||
| &acceptor_holder, | ||
| &acceptor_commit_sig, | ||
| ), | ||
| "{context}: acceptor commitment signature does not verify", | ||
| ); | ||
| } | ||
| } |
Contributor
There was a problem hiding this comment.
nit: We could collect all the mismatches and log them later.
Suggested change
| /// Runs every commitment vector in a test vector file. | |
| /// | |
| /// Note: local is the opener. | |
| pub fn run_commitment_vectors(json: &str) { | |
| let file: TestVectorFile = serde_json::from_str(json).expect("valid test vector file"); | |
| assert!( | |
| !file.tests.is_empty(), | |
| "{}: no test vectors", | |
| file.description | |
| ); | |
| let opener_holder = file.build_holder_identity(Side::Opener); | |
| let acceptor_holder = file.build_holder_identity(Side::Acceptor); | |
| for vector in &file.tests { | |
| let context = format!("{}: {}", file.description, vector.name); | |
| let channel_config = file.build_channel_config(vector); | |
| let commitment_state = file.build_commitment_state(vector); | |
| // Opener signs own commitment. | |
| assert_eq!( | |
| channel_config.sign_holder_commitment(&commitment_state, &opener_holder), | |
| vector.local_signature, | |
| "{context}: local signature mismatch", | |
| ); | |
| // Acceptor signs opener's commitment. | |
| assert!( | |
| channel_config.verify_counterparty_signature( | |
| &commitment_state, | |
| &opener_holder, | |
| &vector.remote_signature, | |
| ), | |
| "{context}: remote signature does not verify", | |
| ); | |
| // Opener signs the acceptor's commitment, then the acceptor verifies it. | |
| let acceptor_commit_sig = | |
| channel_config.sign_counterparty_commitment(&commitment_state, &opener_holder); | |
| assert!( | |
| channel_config.verify_counterparty_signature( | |
| &commitment_state, | |
| &acceptor_holder, | |
| &acceptor_commit_sig, | |
| ), | |
| "{context}: acceptor commitment signature does not verify", | |
| ); | |
| } | |
| } | |
| /// Runs every commitment vector in a test vector file. | |
| /// | |
| /// Every vector is checked before failing, so one run reports all mismatches. | |
| /// | |
| /// Note: local is the opener. | |
| pub fn run_commitment_vectors(json: &str) { | |
| let file: TestVectorFile = serde_json::from_str(json).expect("valid test vector file"); | |
| assert!( | |
| !file.tests.is_empty(), | |
| "{}: no test vectors", | |
| file.description | |
| ); | |
| let failures: Vec<Vec<String>> = file | |
| .tests | |
| .iter() | |
| .map(|vector| file.check_vector(vector)) | |
| .filter(|failures| !failures.is_empty()) | |
| .collect(); | |
| assert!( | |
| failures.is_empty(), | |
| "{}: {} of {} vectors failed\n{}", | |
| file.description, | |
| failures.len(), | |
| file.tests.len(), | |
| failures.concat().join("\n"), | |
| ); | |
| } | |
| impl TestVectorFile { | |
| /// Checks one vector and returns a message per failed assertion. | |
| fn check_vector(&self, vector: &CommitmentVector) -> Vec<String> { | |
| let opener_holder = self.build_holder_identity(Side::Opener); | |
| let acceptor_holder = self.build_holder_identity(Side::Acceptor); | |
| let channel_config = self.build_channel_config(vector); | |
| let commitment_state = self.build_commitment_state(vector); | |
| let mut failures = Vec::new(); | |
| // Opener signs own commitment. | |
| let local_signature = | |
| channel_config.sign_holder_commitment(&commitment_state, &opener_holder); | |
| if local_signature != vector.local_signature { | |
| failures.push(format!( | |
| "{}: local signature mismatch\n expected: {}\n actual: {}", | |
| vector.name, vector.local_signature, local_signature, | |
| )); | |
| } | |
| // Acceptor signs opener's commitment. | |
| if !channel_config.verify_counterparty_signature( | |
| &commitment_state, | |
| &opener_holder, | |
| &vector.remote_signature, | |
| ) { | |
| failures.push(format!("{}: remote signature does not verify", vector.name)); | |
| } | |
| // Opener signs the acceptor's commitment, then the acceptor verifies it. | |
| let acceptor_commit_sig = | |
| channel_config.sign_counterparty_commitment(&commitment_state, &opener_holder); | |
| if !channel_config.verify_counterparty_signature( | |
| &commitment_state, | |
| &acceptor_holder, | |
| &acceptor_commit_sig, | |
| ) { | |
| failures.push(format!( | |
| "{}: acceptor commitment signature does not verify", | |
| vector.name | |
| )); | |
| } | |
| failures | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The first commit moves all the tests in
commitment.rsinto a submodule, and the second migrates the commitment signature verification tests to JSON verification.Pros:
Cons: