Skip to content

smite: drive commitment tests from JSON vectors - #245

Open
NishantBansal2003 wants to merge 2 commits into
lnfuzz:masterfrom
NishantBansal2003:refactor-commitment-tests
Open

smite: drive commitment tests from JSON vectors#245
NishantBansal2003 wants to merge 2 commits into
lnfuzz:masterfrom
NishantBansal2003:refactor-commitment-tests

Conversation

@NishantBansal2003

Copy link
Copy Markdown
Contributor

The first commit moves all the tests in commitment.rs into a submodule, and the second migrates the commitment signature verification tests to JSON verification.

Pros:

  • Removes repetitive code and makes it easier to extend commitment tests with new channel types or HTLC support.
  • Makes it easier to generate oracles from LDK/Eclair and print them directly in this schema.

Cons:

  • If more than one test vector fails, we only see the first test failure instead of all of them.

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 erickcestari left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

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",
);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants