Skip to content

Conversation

@siladu
Copy link
Contributor

@siladu siladu commented Aug 11, 2025

In draft awaiting testing.

  • Use LibSecp256k1JNI by default and fallback to SECP256K1 signature algorithm which uses LibSecp256k1 native lib as well as Java fallback. In reality, the backend lib (bitcoin-core-secp256k1) is the same so should always use LibSecp256k1JNI if available.
  • ECRECPrecompiledContractTest and ECRecoverBenchmark only switches between LibSecp256k1JNI and Java (LibSecp256k1 is effectively ignored for this precompile in reality).
  • R1 chains will use SECP256R1 SignatureAlgorithm (native/java) as before.
    - Add --Xecrecover-precompile-native-enabled to disable LibSecp256k1JNI.

If --Xsecp-native-enabled=true (default) and LibSecp256k1JNI is available then use that.
If --Xsecp-native-enabled=false or native lib not available, then use Java.

Removed `--Xecrecover-precompile-native-enabled` in second iteration following review. Expand for out-of-date details

Flag combinations showing interaction between LibSecp256k1JNI used for precompile-only versus SECP256K1 used for precompile fallback as well as other key operations apart from EcRecover. The default for both flags is `true`:

default:

--Xecrecover-precompile-native-enabled=true --Xsecp-native-enabled=true
2025-08-11 13:16:34.899+10:00 | main | INFO  | Besu | Using the native implementation of the signature algorithm
2025-08-11 13:16:35.039+10:00 | main | INFO  | Besu | Using the native implementation of ecrecover precompile

JNI lib used for ecrecover, but Java used for other secp operations:

--Xecrecover-precompile-native-enabled=true --Xsecp-native-enabled=false
2025-08-11 13:16:48.220+10:00 | main | INFO  | Besu | Using the Java implementation of the signature algorithm
2025-08-11 13:16:48.373+10:00 | main | INFO  | Besu | Using the native implementation of ecrecover precompile

Disable JNI lib, fallback to SECP256K1 for ecrecover:

--Xecrecover-precompile-native-enabled=false --Xsecp-native-enabled=true
2025-08-11 13:16:15.395+10:00 | main | INFO  | Besu | Using the native implementation of the signature algorithm
2025-08-11 13:16:15.544+10:00 | main | INFO  | Besu | Using the native secp256k1 signature algorithm implementation of ecrecover

Disable both, use Java for ecrecover:

--Xecrecover-precompile-native-enabled=false --Xsecp-native-enabled=false
2025-08-11 13:16:03.316+10:00 | main | INFO  | Besu | Using the Java implementation of the signature algorithm
2025-08-11 13:16:03.560+10:00 | main | INFO  | Besu | Using the Java secp256k1 implementation of ecrecover

Testing

Screenshot 2025-08-11 at 9 07 53 pm
  • Ongoing: full snap sync of perf-devnet-1
  • Looking into fuzzing.

siladu added 2 commits August 11, 2025 12:33
…k1JNI

Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
- Use LibSecp256k1JNI by default and fallback to SECP256K1 signature algorithm which uses LibSecp256k1 native lib (as well as Java fallback).
- Add --Xecrecover-precompile-native-enabled to disable LibSecp256k1JNI.
- ECRecoverBenchmark switches between LibSecp256k1JNI and Java.

Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
Copy link
Contributor

@garyschulte garyschulte left a comment

Choose a reason for hiding this comment

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

Few comments - nothing major. It would be good to mark the prior native path with deprecation IMO. There isn't a need to keep that code path around once the single-entrypoint-ecrecover is proven.

logger.info("Using the native implementation of ecrecover precompile");
} else {
ECRECPrecompiledContract.disableNative();
if (SignatureAlgorithmFactory.getInstance().isNative()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a little confusing because disableNative should force isNative to be false AFAICT? is this if statement necessary in that case?

Copy link
Contributor Author

@siladu siladu Aug 12, 2025

Choose a reason for hiding this comment

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

Yeh there's two layers of disabling because SECP256K1 is used in both ecrecover and in transactions.
I agree it's a little odd, but with the benefit of supporting a fallback native option if the new one isn't supported. Maybe doesn't make sense if it's the same native lib backend?

The alternative is to never attempt to use LibSecp256K1 if we wish to disable LibSecp256K1JNI...but I think I'd have to disable that only in the precompile and leave it enabled for the other use cases.

Note, there already exists --Xsecp-native-enabled.
A simpler approach would be to only disable the JNI wrapper if this is set to false, so for K1 ecrecover it's only ever new JNI lib or Java (and remove the new option).

Copy link
Contributor

Choose a reason for hiding this comment

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

maybe a comment here could clarify for future-us why we are double-disabling.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed double layer disabling and now driving LibSecp256K1JNI enable/disable via SignatureAlgorithm.isNative

return useNative;
}

/** Disable native. Note SECP256K1 must additionally be disabled to fully disable native */
Copy link
Contributor

Choose a reason for hiding this comment

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

If we are on an r1 chain, does the same apply?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Might need to chat to you about this.

Is the current expectation for an "r1 chain" that you would use R1 for the precompile as well? Doesn't seem obvious either way to me. If you're transactions are R1, maybe you still want access to a K1 precompile to support various contracts?

IOW, the spec for this precompile is K1, would we want a different precompile for R1?

Can discuss more in the context of the upcoming R1 variant of this PR, but for now I guess it might be safer to only enable the K1JNI lib if the signature algorithm is set to K1?

Copy link
Contributor

Choose a reason for hiding this comment

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

yeah, ecrecover on a secp256r1 chain will need to use an r1-friendly ecrecover

Copy link
Contributor Author

@siladu siladu Aug 12, 2025

Choose a reason for hiding this comment

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

Agree for ecrecover outside the scope of the precompile.
but for the precompile, that would break any public network-based contracts that use k1 precompile though?

Feels like we might need a separate R1 ecrecover precompile?

Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps we can subclass and configure or compose ecrecover precompile for both r1 and k1. I am keen to avoid any complexity with precompile registry per-hardfork specs. For example, ideally "london-is-london" and we don't have to define a separate protocol schedule with different precompile config for an r1 chain.

It might even be worth pushing the optimized signature recovery into SignatureAlgorithm in the same way it was done previously for the native implementation. In essence we leave ECRECPrecompiledContract as-is and choose the native implementation (or not) in SignatureAlgorithm

Copy link
Contributor

Choose a reason for hiding this comment

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

@daniellehrner - can you shed light on the original requirements for ecrecover for r1 chains?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Have isolated these changes to K1 so this PR does not impact R1 chains.

I have not pushed the impl to SignatureAlgorithm as suggested. I attempted it, but you still end up with the same amount of complexity in ECRECPrecompiledContract due to different interface requirements between the precompile and SignatureAlgorithm.

We can revisit when writing the upcoming R1 EcRecover PR, however for it to be worth the complexity, I think we need to redesign the SigAlg ecrecover interface across the board (not just for the precompile).

siladu and others added 6 commits August 12, 2025 11:05
- remove TODOs
- Handle exception

Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
Remove --Xecrecover-precompile-native-enabled
Can now only disable for testing purposes

Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
…improvements

Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
…improvements

Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
final Bytes input, @NotNull final MessageFrame messageFrame) {
final int size = input.size();
final Bytes d = size >= 128 ? input : Bytes.wrap(input, MutableBytes.create(128 - size));
final Bytes32 h = Bytes32.wrap(d, 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

this second wrap to Bytes32 was just not needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's moved both inside computeK1Native and computeDefault, also renamed from h -> messageHash

I could have calculated outside and passed it in but thought the unary method signature of computeX(safeInput) was clearer than computeX(safeInput, messageHash)

Copy link
Contributor

Choose a reason for hiding this comment

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

ok that makes sense


// SECP256K1#PublicKey#recoverFromSignature throws an Illegal argument exception
// when it is unable to recover the key. There is not a straightforward way to
// check the arguments ahead of time to determine if the fail will happen and
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// check the arguments ahead of time to determine if the fail will happen and
// check the arguments ahead of time to determine whether the fail will happen.

Copy link
Contributor

@macfarla macfarla left a comment

Choose a reason for hiding this comment

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

think this looks ok to me if it has synced perf-devnet-1

…improvements

Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
@siladu siladu force-pushed the ecrecoverk1-native-improvements branch from c4c01ee to 03f698d Compare August 28, 2025 05:45
siladu added 2 commits August 29, 2025 10:29
…improvements

Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
@siladu siladu marked this pull request as ready for review August 29, 2025 00:31
@siladu
Copy link
Contributor Author

siladu commented Aug 29, 2025

Testing update: SNAP synced perf-devnet-1 and FULL sync hoodi.

@siladu siladu merged commit c80e23f into hyperledger:main Aug 29, 2025
46 checks passed
@siladu siladu deleted the ecrecoverk1-native-improvements branch August 29, 2025 01:12
jflo pushed a commit to jflo/besu that referenced this pull request Sep 8, 2025
- Use LibSecp256k1JNI by default and fallback to SECP256K1 signature algorithm which uses LibSecp256k1 native lib as well as Java fallback. In reality, the backend lib (bitcoin-core-secp256k1) is the same so should always use LibSecp256k1JNI if either available.
- ECRECPrecompiledContractTest and ECRecoverBenchmark only switches between LibSecp256k1JNI and Java (LibSecp256k1 is effectively ignored for this precompile in reality).
- R1 chains will use SECP256R1 SignatureAlgorithm (native/java) as before.
- If --Xsecp-native-enabled=true (default) and LibSecp256k1JNI is available then use that.
- If --Xsecp-native-enabled=false or native lib not available, then use Java.

---------

Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
Co-authored-by: garyschulte <garyschulte@gmail.com>
jflo pushed a commit to jflo/besu that referenced this pull request Sep 8, 2025
- Use LibSecp256k1JNI by default and fallback to SECP256K1 signature algorithm which uses LibSecp256k1 native lib as well as Java fallback. In reality, the backend lib (bitcoin-core-secp256k1) is the same so should always use LibSecp256k1JNI if either available.
- ECRECPrecompiledContractTest and ECRecoverBenchmark only switches between LibSecp256k1JNI and Java (LibSecp256k1 is effectively ignored for this precompile in reality).
- R1 chains will use SECP256R1 SignatureAlgorithm (native/java) as before.
- If --Xsecp-native-enabled=true (default) and LibSecp256k1JNI is available then use that.
- If --Xsecp-native-enabled=false or native lib not available, then use Java.

---------

Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
Co-authored-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: jflo <justin+github@florentine.us>
georgereuben pushed a commit to georgereuben/besu that referenced this pull request Sep 16, 2025
- Use LibSecp256k1JNI by default and fallback to SECP256K1 signature algorithm which uses LibSecp256k1 native lib as well as Java fallback. In reality, the backend lib (bitcoin-core-secp256k1) is the same so should always use LibSecp256k1JNI if either available.
- ECRECPrecompiledContractTest and ECRecoverBenchmark only switches between LibSecp256k1JNI and Java (LibSecp256k1 is effectively ignored for this precompile in reality).
- R1 chains will use SECP256R1 SignatureAlgorithm (native/java) as before.
- If --Xsecp-native-enabled=true (default) and LibSecp256k1JNI is available then use that.
- If --Xsecp-native-enabled=false or native lib not available, then use Java.

---------

Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
Co-authored-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: georgereuben <reubengeorge101@gmail.com>
georgereuben pushed a commit to georgereuben/besu that referenced this pull request Sep 16, 2025
- Use LibSecp256k1JNI by default and fallback to SECP256K1 signature algorithm which uses LibSecp256k1 native lib as well as Java fallback. In reality, the backend lib (bitcoin-core-secp256k1) is the same so should always use LibSecp256k1JNI if either available.
- ECRECPrecompiledContractTest and ECRecoverBenchmark only switches between LibSecp256k1JNI and Java (LibSecp256k1 is effectively ignored for this precompile in reality).
- R1 chains will use SECP256R1 SignatureAlgorithm (native/java) as before.
- If --Xsecp-native-enabled=true (default) and LibSecp256k1JNI is available then use that.
- If --Xsecp-native-enabled=false or native lib not available, then use Java.

---------

Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
Co-authored-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: georgereuben <reubengeorge101@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants