Skip to content

Feat/mul div - #54

Merged
Hrom131 merged 15 commits into
devfrom
feat/mul-div
Sep 11, 2026
Merged

Hrom131 merged 15 commits into
devfrom
feat/mul-div

Conversation

@aritkulova

@aritkulova aritkulova commented Sep 4, 2026 •

Copy link
Copy Markdown
Collaborator
  • This PR suggests a bug fix and I've added the necessary tests.
  • This PR introduces a new feature and I've discussed the update in an Issue or with the team.
  • This PR is just a minor change like a typo fix.

For now, mul_div functions return 0 on division by zero instead of panicking. A more general approach to this topic will be discussed later.

@aritkulova aritkulova self-assigned this Sep 4, 2026
@aritkulova
aritkulova requested a review from Hrom131 September 4, 2026 07:53
Comment thread simf/lib/u128/math.simf Outdated
Comment thread simf/lib/u128/math.simf
Comment thread simf/lib/u128/mul_div.simf
/// Panics if the result overflows a u256
pub fn mul_div_256(a: u256, b: u256, denominator: u256) -> u256 {
match is_zero_256(denominator) {
true => 0,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Shouldn't we panic if we divide by 0?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I agree that panicking on division by zero would be less error-prone, but the jet's division functions don't panic on division by zero, so here it feels more like the default behavior

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.

We had a discussion on a call with @roconnor-blockstream about this behavior, which other people found unusual or surprising. I think it has to do with some functional programming conventions related to wanting functions to be total when possible. I don't know that he necessarily felt that all library functions written using the jet needed to replicate the behavior, but I don't remember his exact position about this.

I think @apoelstra is also familiar with this discussion and might be able to weigh in.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

For now, mul_div functions will return 0 on division by zero instead of panicking. A more general approach to this topic will be discussed later

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arguably we should return an error here so that the user can choose what to do (unwrap to get panicking behavior, unwrap_or(0) to get "just use 0" behavior).

I understand there is some mathematical reason to support division by 0 being defined to be 0, which simplifies formal reasoning in some cases.

I nonetheless think that returning surprising or arbitrary values from a contract is a very bad idea. This reminds me very much of the "x mod 0 = 0" bug in Ethereum from 2014 or so that allowed signature validation to be entirely bypassed.

So if we are not going to return an error, we should panic.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, I wholeheartedly agree. This behaviour will be fixed in the upcoming PR, I'll send you a link.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@apoelstra PR is open: #57

moved arithmetic section for u128 and u256 to be consistent with other sections

@LesterEvSe LesterEvSe left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Also let's add more tests to cover new u512 division functionality.

  1. Force entry into algorithm_d_512_256:
a = generate_u256(2^128, U256::MAX)
b = generate_u256(2^128, U256::MAX)

result_high = high 256 bits of a.full_mul(b)
c = generate_u256(max(2^128, result_high + 1), U256::MAX)

Guarantees result_high != 0 and denom_high != 0.

  1. Add two normalize_to_threshold_512_127 tests.
    2.1. norm == 1. Forces the divisor's top bit to already be set:
a = generate_u256(2^128, U256::MAX)
b = generate_u256(2^128, U256::MAX)

result_high = high 256 bits of a.full_mul(b)
c = generate_u256(max(2^255, result_high + 1), U256::MAX)

2.2. norm > 1. Forces the divisor's top bit to be clear:

a = generate_u256(2^128, U256::MAX)
b = generate_u256(2^128, 2^192)

result_high = high 256 bits of a.full_mul(b)   // < 2^192, well under the 2^255 ceiling
c = generate_u256(max(2^128, result_high + 1), 2^255 - 1)
  1. Same a, b as (1), but c = result_high + 1.

  2. Exact division with remainder == 0. Set a = c for some c in range [2^128, U256::MAX], pick b as a*b >= 2^256.

  3. Minimal denom_high:

a = generate_u256(2^128, 2^129)
b = generate_u256(2^128, 2^129)

result_high = high 256 bits of a.full_mul(b)   // in {1, 2, 3}
c = generate_u256(max(2^128, result_high + 1), 2^129 - 1)

Comment thread tests/u32_mul_div_test.rs Outdated
Comment thread tests/u64_mul_div_test.rs Outdated
Comment thread tests/u128_mul_div_test.rs Outdated
Comment thread tests/u256_mul_div_test.rs
Comment thread simf/lib/u256/math.simf
Comment thread tests/u256_test_div.rs

@LesterEvSe LesterEvSe left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM!

But worth mentioning, from time to time I get this error on the test u256_test_mul_div_256_algorithm_d_512_256_c_is_res_high:

Error: Broadcast failed with HTTP 400 for http://127.0.0.1:41877/tx: sendrawtransaction RPC error -26:
non-mandatory-script-verify-flag (Program's execution cost could exceed budget)

Probably worth researching on the simplex side to see how we can solve it

@aritkulova

Copy link
Copy Markdown
Collaborator Author

from time to time I get this error on the test u256_test_mul_div_256_algorithm_d_512_256_c_is_res_high

Yeah, me too, and only when I run tests with --test-threads option

@Hrom131 Hrom131 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM!

@Hrom131
Hrom131 merged commit 60fd264 into dev Sep 11, 2026
1 check passed
@Hrom131
Hrom131 deleted the feat/mul-div branch September 11, 2026 12:35
LesterEvSe pushed a commit that referenced this pull request Sep 11, 2026
* added mul_div functions with tests

* added zero checks for div_128 and div_256 to be consistent with jet's `divide`

* added tests for new helper functions

* typo in a test name

* utilized convert and split functions

* added mul_div docs;
moved arithmetic section for u128 and u256 to be consistent with other sections

* linting

* added docs for new functions

* fixed typo

* fixed bug in normalize_to_threshold funcs

* added more tests to cover new u512 division functionality

* bumped simplex version

* optimization for calculate_normalizer_base_128

* typo fixes

* a couple of new tests for calculate_normalizer_base_128
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.

5 participants