-
Notifications
You must be signed in to change notification settings - Fork 7
refactor: extract free functions for enum and numeric oracle event operations #130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
8031aa2 to
26ca2d8
Compare
|
Take a look at #104 in case there's anything you can grab from there. I can close 104, pending @bennyhodl |
kwsantiago
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good code quality, excellent tests, solid documentation. Left some comments for silent behavior change from errors to clamping.
| if event_id.is_empty() { | ||
| return Err(Error::InvalidArgument); | ||
| } | ||
| if base != 2 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Base 2 is hardcoded but the function accepts a base parameter.
Either remove the parameter or support other bases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm planning to add support for other bases in a separate PR.
| if base != 2 { | ||
| return Err(Error::InvalidArgument); | ||
| } | ||
| if num_digits == 0 || num_digits > 63 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why 63?
This should be a constant with an explanation of why this number was chosen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because we use i64 as outcome type which uses 63 bits for the value and 1 bit for the sign. Probably it makes sense to use BigInt and BigUint here. But again, this should be done in a separate PR.
kormir/src/lib.rs
Outdated
| ); | ||
|
|
||
| // verify our nonce is the same as the one in the announcement | ||
| debug_assert!(sig.encode()[..32] == announcement.oracle_event.oracle_nonces[0].serialize()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The old code used an iterator while the new code is collecting all nonce keys which could lead to a small performance regression for large nonce sets.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need an iterator to collect 1 nonce.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should at least check that we have a nonce. Validating the announcement will check that we have 1 nonce.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In fact we don't need this line at all. It was added to check if rust-dlc signing algorithm worked as expected. And it did.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved these checks to the unit tests.
| oracle_nonces, | ||
| event_id, | ||
| let nonce_indexes = self.storage.get_next_nonce_indexes(1).await?; | ||
| if nonce_indexes.len() != 1 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check seems defensive but the free function doesn't do it. There is inconsistent validation between Oracle methods and free functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course the free function doesn't do it. The compiler checks it. This was not an inconsistency, but a bug in kormir, which could lead to unexpected panics.
| /// * [Oracle Announcement](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#the-oracle_announcement-type) | ||
| /// * [Oracle Event](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#the-oracle_event-type) | ||
| /// * [Digit Decomposition Event Descriptor](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#digit_decomposition_event_descriptor) | ||
| #[allow(clippy::too_many_arguments)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: consider a builder pattern or config struct instead of suppressing the lint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea for a separate PR.
a9795e5 to
57df97d
Compare
This PR and #104 are orthogonal. #104 creates CLI commands that allow users to connect to a Kormir server instance in order for the server to create announcements and attestations. This PR extracts the core logic of creating announcements and attestations so it can be used in other orcale apps. Imagine a weather web-site, that posts announcements on Twitter; attests them with the data from their internal database and again posts attestations on Twitter; and uses their custom private key setup without exposing |
0cf7156 to
a7c14a9
Compare
bennyhodl
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks 🤙🏼
small nits
kormir/src/lib.rs
Outdated
| ); | ||
|
|
||
| // verify our nonce is the same as the one in the announcement | ||
| debug_assert!(sig.encode()[..32] == announcement.oracle_event.oracle_nonces[0].serialize()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should at least check that we have a nonce. Validating the announcement will check that we have 1 nonce.
| return Err(Error::InvalidArgument); | ||
| } | ||
| if key_pair.x_only_public_key().0 != announcement.oracle_public_key { | ||
| return Err(Error::InvalidAnnouncement); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
InvalidAnnouncement might be the wrong error here. The signature can still be valid for that public key
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't want to attest someone else's announcements. This check validates if it's the case.
a7c14a9 to
00f83b7
Compare
00f83b7 to
75a2b5d
Compare
| // verify our signature | ||
| if secp | ||
| .verify_schnorr(&sig, &msg, &key_pair.x_only_public_key().0) | ||
| .is_err() | ||
| { | ||
| return Err(Error::Internal); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The old code verified that the nonce in the signature matched the announcement's nonce.
Should we add a runtime check to ensure the caller passed the correct nonce key matching the announcement?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! I added an explicit check for the nonces and nonce keys.
kormir/src/lib.rs
Outdated
| &data.announcement, | ||
| outcome, | ||
| &nonce_keys, | ||
| false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't clamp_outcome be exposed as an optional param instead of hard-coding to false? If it's always set to false then the Oracle struct can never use clamping.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It shouldn't be there in the first place. The oracle must be spec compliant. Reverted it to the initial commit.
| /// Kormir error type | ||
| #[derive(Debug, Clone)] | ||
| pub enum Error { | ||
| /// Invalid argument given |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since InvalidArgument was removed, this is a breaking change for anyone using Error::InvalidArgument
May want to add in changelog. The new errors you added are better though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reverted it and made it deprecated. Will be removed in the next minor release.
75a2b5d to
cfc507c
Compare
Summary
This PR extracts the core oracle event creation and signing logic into standalone free functions, improving code reusability and testability while maintaining the existing
OracleAPI.Major Changes
Function Extraction
create_enum_event()- Standalone function for creating enum event announcementssign_enum_event()- Standalone function for signing enum event outcomescreate_numeric_event()- Standalone function for creating numeric event announcementssign_numeric_event()- Standalone function for signing numeric event outcomes with clamping logicget_min_value(),get_max_value(),derive_signing_key()Architectural Benefits
OraclestructOraclemethods or low-level functionsImplementation Details
Oraclemethods now delegate to the extracted free functionsOracleAPI remains unchangedMinor Improvements
Impact
Testing