Skip to content

Fix GrindKey() key derivation in Go implementation#106

Open
Ehsan-saradar wants to merge 1 commit into
tradeparadex:mainfrom
Ehsan-saradar:fix/grindkey-range-check-102
Open

Fix GrindKey() key derivation in Go implementation#106
Ehsan-saradar wants to merge 1 commit into
tradeparadex:mainfrom
Ehsan-saradar:fix/grindkey-range-check-102

Conversation

@Ehsan-saradar

Copy link
Copy Markdown

Summary

GrindKey() in the Go sample diverged from the reference grind_key (paradex-py / paradex.js), so it produced Stark private keys that did not match the other SDKs for the same Ethereum signature. This fixes the divergence (closes #102) and adds tests pinning the output to reference values.

Bugs fixed

  1. Inverted range check ([BUG] - GrindKey() in Go Implementation #102). The grind loop used key.Cmp(maxAllowedVal) < 0, looping while the key was already in range and accepting out-of-range keys. Rejection sampling requires grinding while the key is out of range, so the condition is now >= 0.

  2. SHA256_EC_MAX_DIGEST was silently zero. It was parsed from a string literal containing spaces, which big.Int.SetString rejects (ok=false); the unchecked return left the value at 0. That 0 masked bug Add example for Paradex onboarding and authentication #1 (a hash is never < 0, so the buggy loop never ran) and would have turned into an infinite loop once the condition was flipped. It is now computed directly as 2^256 via Lsh.

  3. Index hex padding. The index was encoded with %02x, which only guarantees even length up to 255 (e.g. 256 -> "100" vs the reference "0100"). It is now padded to an even number of hex digits, matching padded_hex.

  4. Seed hex padding. The seed was not padded to even length, so an odd-length seed panicked in hex.DecodeString. It is padded the same way.

Tests

Adds go/utils_test.go with golden values generated from the paradex-py reference (grind_key), plus range and 0x-prefix checks. Verified the tests fail against the previous behavior and pass with this fix.

Fixes #102

GrindKey() diverged from the reference grind_key (paradex-py / paradex.js)
in several ways, producing keys that did not match other SDKs:

1. Inverted range check (issue tradeparadex#102): the loop used `< 0`, looping while
   the key was already in range and accepting out-of-range keys. Rejection
   sampling requires grinding *while* the key is out of range, so the
   condition is now `>= 0`.

2. SHA256_EC_MAX_DIGEST was built from a string literal containing spaces,
   which big.Int.SetString rejects (ok=false) while the unchecked return
   left the value at 0. That 0 silently masked bug tradeparadex#1 (a hash is never
   < 0, so the loop never ran) and would have caused an infinite loop once
   the condition was flipped. Compute 2^256 directly instead.

3. The index was hex-encoded with `%02x`, which stops keeping the length
   even past 255 (e.g. 256 -> "100" vs the reference "0100"). Pad to an
   even number of hex digits, matching padded_hex.

4. The seed was not padded to even length, so an odd-length seed panicked
   in hex.DecodeString. Pad it the same way.

Add utils_test.go pinning GrindKey output to golden values generated from
the paradex-py reference, plus range and hex-prefix checks.

Fixes tradeparadex#102
@Ehsan-saradar
Ehsan-saradar force-pushed the fix/grindkey-range-check-102 branch from f48d912 to 1b0c4cb Compare June 16, 2026 09:33
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.

[BUG] - GrindKey() in Go Implementation

1 participant