Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/rs-dpp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ jsonschema = { git = "https://github.com/dashpay/jsonschema-rs", branch = "confi
"draft202012",
], optional = true }
lazy_static = { version = "1.4" }
libm = "0.2"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Pin libm exactly — a caret range undermines the determinism guarantee this PR is built on.

libm = "0.2"

The entire premise of distribution_function_evaluate_version: 1 is that libm produces bit-identical results everywhere. libm is not a correctly-rounded implementation, and its 0.2.x series has shipped accuracy/algorithm changes in patch releases. A caret range means the guarantee is only "same math" for builds that respect this repo's Cargo.lock — a cargo update, a regenerated lock, or any downstream crate resolving rs-dpp from crates.io can land on a different 0.2.x.

That matters because the result feeds a truncating cast (diff_exp as i128, intermediate.floor()), so a 1-ulp difference is a whole-token difference. Two validators at the same protocol version would then disagree on a claim amount — exactly the divergence this PR removes, but now invisible because both report the same version.

The manifest already pins consensus-critical deps this way two lines down (bincode = { version = "=2.0.1", ... }). Suggest libm = "=0.2.16" (the currently locked version) plus a comment explaining why.

Concrete evidence the last bit really is in play here — on aarch64-darwin, for the exact input in your new polynomial test:

std  powf(125.0, 1.0/3.0) = 4.999999999999999  (bits 4013ffffffffffff) -> as i128 = 4
libm pow (125.0, 1.0/3.0) = 5.0                (bits 4014000000000000) -> as i128 = 5

🤖 Posted autonomously by Claude on behalf of pasta.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

libm = "0.2" should be pinned exactly (= "0.2.16"). The determinism guarantee of the v1 path is "every node runs the same libm code", but a caret range lets any rebuild with a refreshed lock (or a downstream consumer not using this repo's Cargo.lock) resolve a different 0.2.x, and libm has shipped accuracy changes to pow/exp/log in patch releases. A one-ulp difference flips the truncating as i128/as u64 casts and re-creates exactly the consensus divergence this PR exists to fix — now dependent on Cargo resolution instead of the OS. This manifest already pins bincode = "=2.0.1" two lines down for the same class of reason.

Notably, the boundary case the new polynomial test locks in is razor-thin: the exact value of 125^(f64(1/3)) is 4.99999999999999955…, so libm 0.2.16 returning exactly 5.0 is within half an ulp of returning 4.999999999999999 (what Apple/glibc pow return). Any future libm patch that moves pow by one ulp changes consensus results at version 1.

Suggested change
libm = "0.2"
libm = "=0.2.16"

🤖 Posted autonomously by Claude on behalf of pasta.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

libm = "0.2" is a caret range on the crate whose bit-exact output is the entire premise of this fix. libm 0.2.x patch releases have changed pow/exp/log implementations; a regenerated lockfile (or any downstream consumer resolving a different 0.2.x) can shift results by 1 ulp, and the as i128/as u64 truncations in evaluate() turn a 1-ulp difference into a whole-token difference — the same consensus-divergence class this PR fixes, relocated from "OS libm" to "Cargo resolution". This manifest already pins bincode = "=2.0.1" two lines down for the same reason.

Suggested change
libm = "0.2"
libm = "=0.2.16"

🤖 Posted autonomously by Claude on behalf of pasta.

num_enum = "0.7"
bincode = { version = "=2.0.1", features = ["serde"] }
rand = { version = "0.8.5", features = ["small_rng"] }
Expand Down

Large diffs are not rendered by default.

Loading
Loading