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
18 changes: 17 additions & 1 deletion actuator/src/main/java/org/tron/core/utils/ProposalUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,21 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore,
}
break;
}
case ALLOW_OPTIMIZED_BN128: {
if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_8_2)) {
throw new ContractValidateException(
"Bad chain parameter id [ALLOW_OPTIMIZED_BN128]");
}
if (dynamicPropertiesStore.getAllowOptimizedBN128() == 1) {
throw new ContractValidateException(
"[ALLOW_OPTIMIZED_BN128] has been valid, no need to propose again");
}
if (value != 1) {
throw new ContractValidateException(
"This value[ALLOW_OPTIMIZED_BN128] is only allowed to be 1");
}
break;
}
default:
break;
}
Expand Down Expand Up @@ -955,7 +970,8 @@ public enum ProposalType { // current value, value range
CONSENSUS_LOGIC_OPTIMIZATION(88), // 0, 1
ALLOW_TVM_BLOB(89), // 0, 1
PROPOSAL_EXPIRE_TIME(92), // (0, 31536003000)
ALLOW_TVM_SELFDESTRUCT_RESTRICTION(94); // 0, 1
ALLOW_TVM_SELFDESTRUCT_RESTRICTION(94), // 0, 1
ALLOW_OPTIMIZED_BN128(95); // 0, 1

private long code;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
import org.tron.common.crypto.Rsv;
import org.tron.common.crypto.SignUtils;
import org.tron.common.crypto.SignatureInterface;
import org.tron.common.crypto.bn128.BN128Service;
import org.tron.common.crypto.bn128.SocketBN128Client;
import org.tron.common.crypto.zksnark.BN128;
import org.tron.common.crypto.zksnark.BN128Fp;
import org.tron.common.crypto.zksnark.BN128G1;
Expand Down Expand Up @@ -426,6 +428,39 @@ public abstract static class PrecompiledContract {
@Getter
@Setter
private long vmShouldEndInUs;
protected static BN128Service client;
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 18, 2026

Choose a reason for hiding this comment

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

P2: BN128 client field, static initializer (with socket I/O), and shutdown hook are placed on the abstract PrecompiledContract base class, which is the parent of all precompiled contracts. Only the three BN128 subclasses use client. Consider moving this to a dedicated BN128 helper/holder class to avoid coupling all precompiled contracts to BN128 socket infrastructure.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java, line 431:

<comment>BN128 client field, static initializer (with socket I/O), and shutdown hook are placed on the abstract `PrecompiledContract` base class, which is the parent of **all** precompiled contracts. Only the three BN128 subclasses use `client`. Consider moving this to a dedicated BN128 helper/holder class to avoid coupling all precompiled contracts to BN128 socket infrastructure.</comment>

<file context>
@@ -426,6 +428,39 @@ public abstract static class PrecompiledContract {
     @Getter
     @Setter
     private long vmShouldEndInUs;
+    protected static BN128Service client;
+    // BN128 client connection pool size
+    private static final int BN128_CLIENT_POOL_SIZE = 8;
</file context>
Fix with Cubic

// BN128 client connection pool size
private static final int BN128_CLIENT_POOL_SIZE = 8;

static {
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 18, 2026

Choose a reason for hiding this comment

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

P0: The SocketBN128Client is created inside a static {} block that runs exactly once at class-load time. Because VMConfig.ALLOW_OPTIMIZED_BN128 defaults to false and is only set later by ConfigLoader.load() (called per-transaction from VMActuator), the static block will see false and skip client creation. After that, even if the chain parameter is enabled through governance, the static block never re-runs, so client stays null and the optimized path is unreachable.

Consider lazy-initializing the client on first use (double-checked locking or an explicit init() method called after ConfigLoader.load()), so activation through governance works without a node restart.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java, line 435:

<comment>The `SocketBN128Client` is created inside a `static {}` block that runs exactly once at class-load time. Because `VMConfig.ALLOW_OPTIMIZED_BN128` defaults to `false` and is only set later by `ConfigLoader.load()` (called per-transaction from `VMActuator`), the static block will see `false` and skip client creation. After that, even if the chain parameter is enabled through governance, the static block never re-runs, so `client` stays `null` and the optimized path is unreachable.

Consider lazy-initializing the client on first use (double-checked locking or an explicit `init()` method called after `ConfigLoader.load()`), so activation through governance works without a node restart.</comment>

<file context>
@@ -426,6 +428,39 @@ public abstract static class PrecompiledContract {
+    // BN128 client connection pool size
+    private static final int BN128_CLIENT_POOL_SIZE = 8;
+
+    static {
+      if (VMConfig.allowOptimizedBn128()) {
+        try {
</file context>
Fix with Cubic

if (VMConfig.allowOptimizedBn128()) {
try {
int bn128Port = VMConfig.getBN128ClientPort();
int bn128Timeout = VMConfig.getBN128SocketTimeout();
client = new SocketBN128Client(BN128_CLIENT_POOL_SIZE, bn128Port, bn128Timeout);
logger.info("SocketBn128Client initialized with pool size={}, port={}, timeout={}ms",
BN128_CLIENT_POOL_SIZE, bn128Port, bn128Timeout);

// Register shutdown hook for graceful cleanup
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
if (client != null) {
logger.info("Closing SocketBn128Client...");
client.close();
logger.info("SocketBn128Client closed successfully");
}
} catch (Exception e) {
logger.error("Error occurred while closing SocketBn128Client", e);
}
}));
} catch (Exception e) {
logger.warn(
"Failed to initialize SocketBn128Client, falling back to non-optimized mode: {}",
e.getMessage());
client = null;
}
}
}

public abstract long getEnergyForData(byte[] data);

Expand Down Expand Up @@ -757,6 +792,18 @@ public Pair<Boolean, byte[]> execute(byte[] data) {
data = EMPTY_BYTE_ARRAY;
}

if (VMConfig.allowOptimizedBn128() && client != null) {
byte[] input = data.length > 128 ? Arrays.copyOfRange(data, 0, 128) : data;
try {
return client.bn128Add(input);
} catch (Exception e) {
logger.warn(
"Optimized BN128 add failed for input length {}, falling back to non-optimized "
+ "implementation: {}",
data.length, e.getMessage());
}
}

byte[] x1 = parseWord(data, 0);
byte[] y1 = parseWord(data, 1);

Expand Down Expand Up @@ -811,6 +858,18 @@ public Pair<Boolean, byte[]> execute(byte[] data) {
data = EMPTY_BYTE_ARRAY;
}

if (VMConfig.allowOptimizedBn128() && client != null) {
byte[] input = data.length > 96 ? Arrays.copyOfRange(data, 0, 96) : data;
try {
return client.bn128Mul(input);
} catch (Exception e) {
logger.warn(
"Optimized BN128 multiplication failed for input length {}, falling back to "
+ "non-optimized implementation: {}",
data.length, e.getMessage());
}
}

byte[] x = parseWord(data, 0);
byte[] y = parseWord(data, 1);

Expand Down Expand Up @@ -844,7 +903,8 @@ public Pair<Boolean, byte[]> execute(byte[] data) {
public static class BN128Pairing extends PrecompiledContract {

private static final int PAIR_SIZE = 192;

// Limit to 100 pairs (19,200 bytes) in optimized mode to guarantee security
private static final int MAX_PAIR_SIZE_LIMIT = 192 * 100;
@Override
public long getEnergyForData(byte[] data) {
if (VMConfig.allowTvmIstanbul()) {
Expand Down Expand Up @@ -875,6 +935,17 @@ public Pair<Boolean, byte[]> execute(byte[] data) {
return Pair.of(false, EMPTY_BYTE_ARRAY);
}

if (VMConfig.allowOptimizedBn128() && client != null && data.length <= MAX_PAIR_SIZE_LIMIT) {
try {
return client.bn128Pairing(data);
} catch (Exception e) {
logger.warn(
"Optimized BN128 pairing failed for input length {}, falling back to "
+ "non-optimized implementation: {}",
data.length, e.getMessage());
}
}

PairingCheck check = PairingCheck.create();

// iterating over all pairs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ public static void load(StoreFactory storeFactory) {
VMConfig.initDisableJavaLangMath(ds.getConsensusLogicOptimization());
VMConfig.initAllowTvmBlob(ds.getAllowTvmBlob());
VMConfig.initAllowTvmSelfdestructRestriction(ds.getAllowTvmSelfdestructRestriction());
VMConfig.initAllowOptimizedBN128(ds.getAllowOptimizedBN128());
VMConfig.initBN128Port(ds.getBN128Port());
VMConfig.initBN128SocketTimeout(ds.getBN128SocketTimeout());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ public class DynamicPropertiesStore extends TronStoreWithRevoking<BytesCapsule>
private static final byte[] ALLOW_TVM_SELFDESTRUCT_RESTRICTION =
"ALLOW_TVM_SELFDESTRUCT_RESTRICTION".getBytes();

private static final byte[] ALLOW_OPTIMIZED_BN128 =
"ALLOW_OPTIMIZED_BN128".getBytes();

@Autowired
private DynamicPropertiesStore(@Value("properties") String dbName) {
super(dbName);
Expand Down Expand Up @@ -2967,7 +2970,32 @@ public void saveAllowTvmSelfdestructRestriction(long value) {
public boolean allowTvmSelfdestructRestriction() {
return getAllowTvmSelfdestructRestriction() == 1L;
}


public void saveAllowOptimizedBn128(long allowOptimizedBn128) {
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 18, 2026

Choose a reason for hiding this comment

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

P2: Naming inconsistency: save method uses Bn128 but the getter uses BN128. Every other save/get pair in this class uses matching capitalization. Rename to saveAllowOptimizedBN128 for consistency.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java, line 2974:

<comment>Naming inconsistency: save method uses `Bn128` but the getter uses `BN128`. Every other save/get pair in this class uses matching capitalization. Rename to `saveAllowOptimizedBN128` for consistency.</comment>

<file context>
@@ -2967,7 +2970,32 @@ public void saveAllowTvmSelfdestructRestriction(long value) {
   }
-  
+
+  public void saveAllowOptimizedBn128(long allowOptimizedBn128) {
+    this.put(ALLOW_OPTIMIZED_BN128,
+        new BytesCapsule(ByteArray.fromLong(allowOptimizedBn128)));
</file context>
Fix with Cubic

this.put(ALLOW_OPTIMIZED_BN128,
new BytesCapsule(ByteArray.fromLong(allowOptimizedBn128)));
}

public long getAllowOptimizedBN128() {
return Optional.ofNullable(getUnchecked(ALLOW_OPTIMIZED_BN128))
.map(BytesCapsule::getData)
.map(ByteArray::toLong)
.orElse(CommonParameter.getInstance()
.getAllowOptimizedBN128());
}
Comment on lines +2974 to +2985
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Bootstrap ALLOW_OPTIMIZED_BN128 into the store instead of relying on fallback reads.

getAllowOptimizedBN128() falls back to CommonParameter when the key is missing, and there is no constructor path that writes this key on first boot. Until a proposal stores it, the effective value can change across restarts just by editing local config. Persist the bootstrap value once during store initialization.

Suggested constructor fix
     try {
       this.getAllowDynamicEnergy();
     } catch (IllegalArgumentException e) {
       this.saveAllowDynamicEnergy(CommonParameter.getInstance().getAllowDynamicEnergy());
     }
+
+    if (getUnchecked(ALLOW_OPTIMIZED_BN128) == null) {
+      this.saveAllowOptimizedBn128(CommonParameter.getInstance().getAllowOptimizedBN128());
+    }
 
     try {
       this.getDynamicEnergyThreshold();
     } catch (IllegalArgumentException e) {
       this.saveDynamicEnergyThreshold(CommonParameter.getInstance().getDynamicEnergyThreshold());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java`
around lines 2974 - 2985, DynamicPropertiesStore currently reads
ALLOW_OPTIMIZED_BN128 from CommonParameter when the key is absent, but nothing
writes a bootstrap value on first run; update the DynamicPropertiesStore
constructor (or its init path) to check for the presence of
ALLOW_OPTIMIZED_BN128 (use getUnchecked/has/get) and, if missing, call
saveAllowOptimizedBn128(CommonParameter.getInstance().getAllowOptimizedBN128())
to persist the bootstrap value without overwriting an existing stored value;
reference methods: DynamicPropertiesStore constructor, getAllowOptimizedBN128(),
and saveAllowOptimizedBn128().


public boolean allowOptimizedBN128() {
return getAllowOptimizedBN128() == 1L;
}

public int getBN128Port() {
return CommonParameter.getInstance().getBn128ServerPort();
}

public int getBN128SocketTimeout() {
return CommonParameter.getInstance().getBn128SocketTimeout();
}

public void saveProposalExpireTime(long proposalExpireTime) {
this.put(PROPOSAL_EXPIRE_TIME, new BytesCapsule(ByteArray.fromLong(proposalExpireTime)));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,18 @@ public class CommonParameter {
@Getter
@Setter
public long allowTvmBlob;
@Getter
@Setter
public long allowOptimizedBN128;
@Getter
@Setter
public boolean launchBN128Server = false;
@Getter
@Setter
public int bn128ServerPort = 9001;
@Getter
@Setter
public int bn128SocketTimeout = 30;
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 18, 2026

Choose a reason for hiding this comment

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

P1: Default bn128SocketTimeout = 30 translates to 30 milliseconds (per Socket.setSoTimeout() contract). This is extremely short for a socket timeout — even local IPC BN128 pairing operations can exceed 30ms under load. If this is intended to be 30 seconds, the value should be 30000; otherwise, consider a more reasonable default like 3000 (3 seconds).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At common/src/main/java/org/tron/common/parameter/CommonParameter.java, line 646:

<comment>Default `bn128SocketTimeout = 30` translates to 30 milliseconds (per `Socket.setSoTimeout()` contract). This is extremely short for a socket timeout — even local IPC BN128 pairing operations can exceed 30ms under load. If this is intended to be 30 seconds, the value should be `30000`; otherwise, consider a more reasonable default like `3000` (3 seconds).</comment>

<file context>
@@ -632,6 +632,18 @@ public class CommonParameter {
+  public int bn128ServerPort = 9001;
+  @Getter
+  @Setter
+  public int bn128SocketTimeout = 30;
 
   private static double calcMaxTimeRatio() {
</file context>
Suggested change
public int bn128SocketTimeout = 30;
public int bn128SocketTimeout = 30000;
Fix with Cubic


private static double calcMaxTimeRatio() {
return 5.0;
Expand Down
5 changes: 3 additions & 2 deletions common/src/main/java/org/tron/core/config/Parameter.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ public enum ForkBlockVersionEnum {
VERSION_4_7_7(31, 1596780000000L, 80),
VERSION_4_8_0(32, 1596780000000L, 80),
VERSION_4_8_0_1(33, 1596780000000L, 70),
VERSION_4_8_1(34, 1596780000000L, 80);
VERSION_4_8_1(34, 1596780000000L, 80),
VERSION_4_8_2(35, 1596780000000L, 80);
// if add a version, modify BLOCK_VERSION simultaneously

@Getter
Expand Down Expand Up @@ -77,7 +78,7 @@ public class ChainConstant {
public static final int SINGLE_REPEAT = 1;
public static final int BLOCK_FILLED_SLOTS_NUMBER = 128;
public static final int MAX_FROZEN_NUMBER = 1;
public static final int BLOCK_VERSION = 34;
public static final int BLOCK_VERSION = 35;
public static final long FROZEN_PERIOD = 86_400_000L;
public static final long DELEGATE_PERIOD = 3 * 86_400_000L;
public static final long TRX_PRECISION = 1000_000L;
Expand Down
30 changes: 30 additions & 0 deletions common/src/main/java/org/tron/core/vm/config/VMConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ public class VMConfig {

private static boolean ALLOW_TVM_SELFDESTRUCT_RESTRICTION = false;

private static boolean ALLOW_OPTIMIZED_BN128 = false;

private static int BN128_CLIENT_PORT = 9001;

private static int BN128_SOCKET_TIMEOUT = 30;

private VMConfig() {
}

Expand Down Expand Up @@ -172,6 +178,18 @@ public static void initAllowTvmSelfdestructRestriction(long allow) {
ALLOW_TVM_SELFDESTRUCT_RESTRICTION = allow == 1;
}

public static void initAllowOptimizedBN128(long allow) {
ALLOW_OPTIMIZED_BN128 = allow == 1;
}

public static void initBN128Port(int port) {
BN128_CLIENT_PORT = port;
}

public static void initBN128SocketTimeout(int timeout) {
BN128_SOCKET_TIMEOUT = timeout;
}

public static boolean getEnergyLimitHardFork() {
return CommonParameter.ENERGY_LIMIT_HARD_FORK;
}
Expand Down Expand Up @@ -271,4 +289,16 @@ public static boolean allowTvmBlob() {
public static boolean allowTvmSelfdestructRestriction() {
return ALLOW_TVM_SELFDESTRUCT_RESTRICTION;
}

public static boolean allowOptimizedBn128() {
return ALLOW_OPTIMIZED_BN128;
}

public static int getBN128ClientPort() {
return BN128_CLIENT_PORT;
}

public static int getBN128SocketTimeout() {
return BN128_SOCKET_TIMEOUT;
}
}
2 changes: 2 additions & 0 deletions crypto/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ repositories {

dependencies {
api project(":common")
api 'net.java.dev.jna:jna:5.12.1'
api 'com.github.federico2014.besu-native:gnark:1.3.11'
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 18, 2026

Choose a reason for hiding this comment

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

P0: Supply chain risk: This dependency pulls native cryptographic binaries from a personal GitHub fork (federico2014/besu-native) via JitPack instead of using the official Hyperledger-published artifact (org.hyperledger.besu). For a consensus-critical BN128 precompile, a compromised or tampered fork could produce incorrect cryptographic results or introduce backdoors. Consider publishing a vetted fork under the project's own organization with reproducible builds, or using the official org.hyperledger.besu:besu-native-gnark artifact from Maven Central.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crypto/build.gradle, line 16:

<comment>**Supply chain risk**: This dependency pulls native cryptographic binaries from a personal GitHub fork (`federico2014/besu-native`) via JitPack instead of using the official Hyperledger-published artifact (`org.hyperledger.besu`). For a consensus-critical BN128 precompile, a compromised or tampered fork could produce incorrect cryptographic results or introduce backdoors. Consider publishing a vetted fork under the project's own organization with reproducible builds, or using the official `org.hyperledger.besu:besu-native-gnark` artifact from Maven Central.</comment>

<file context>
@@ -12,6 +12,8 @@ repositories {
 dependencies {
     api project(":common")
+    api 'net.java.dev.jna:jna:5.12.1'
+    api 'com.github.federico2014.besu-native:gnark:1.3.11'
 }
 
</file context>
Fix with Cubic

Comment on lines +15 to +16
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify that java-library is applied somewhere in Gradle config
rg -n "id ['\"]java-library['\"]|apply plugin:\\s*['\"]java-library['\"]" --glob "**/*.gradle*"

# Quick scan for potential public API leakage of JNA/gnark types
rg -n --type=java "^\\s*public\\s+.*(com\\.sun\\.jna|gnark|besu-native)"

Repository: Federico2014/java-tron

Length of output: 113


🏁 Script executed:

# First, let's read the crypto/build.gradle file to see the context
cat -n crypto/build.gradle | head -30

Repository: Federico2014/java-tron

Length of output: 842


🏁 Script executed:

# Check if crypto/build.gradle applies java-library plugin
rg -n "apply plugin:|id '" crypto/build.gradle | head -20

Repository: Federico2014/java-tron

Length of output: 81


🏁 Script executed:

# Search for public usage of JNA and gnark types in the crypto module
rg -n "public.*jna|public.*gnark|public.*JNA|public.*Gnark" --type=java crypto/

Repository: Federico2014/java-tron

Length of output: 48


🏁 Script executed:

# Check if the crypto module exports JNA/gnark types in any public interfaces
rg -n "import.*com\.sun\.jna|import.*gnark|import.*besu-native" --type=java crypto/ | head -20

Repository: Federico2014/java-tron

Length of output: 306


🏁 Script executed:

# Check if BN128Executor is a public class and what it exposes
cat -n crypto/src/main/java/org/tron/common/crypto/bn128/BN128Executor.java | head -50

Repository: Federico2014/java-tron

Length of output: 1922


🏁 Script executed:

# Check all public methods/fields in crypto module for JNA/gnark type exposure
rg -n "public\s+(class|interface|record|enum|.*\s+(jna|gnark|JNA|Gnark|IntByReference|LibGnark))" --type=java crypto/src/main/java/ -A 3 | head -40

Repository: Federico2014/java-tron

Length of output: 3198


Switch to implementation for native dependencies.

The crypto module uses the plain java plugin (line 2), which does not provide the api configuration. Lines 15-16 use api for JNA and gnark dependencies, but since these types are internal implementation details (used only within BN128Executor for native calls, not exposed in public method signatures), they should be declared as implementation instead.

Suggested change
-    api 'net.java.dev.jna:jna:5.12.1'
-    api 'com.github.federico2014.besu-native:gnark:1.3.11'
+    implementation 'net.java.dev.jna:jna:5.12.1'
+    implementation 'com.github.federico2014.besu-native:gnark:1.3.11'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
api 'net.java.dev.jna:jna:5.12.1'
api 'com.github.federico2014.besu-native:gnark:1.3.11'
implementation 'net.java.dev.jna:jna:5.12.1'
implementation 'com.github.federico2014.besu-native:gnark:1.3.11'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crypto/build.gradle` around lines 15 - 16, The build declares native libs
with the wrong configuration: change the JNA and gnark dependencies from api to
implementation in the crypto module's Gradle file because these native libraries
are internal to BN128Executor and not part of the module's public API; update
the dependency declarations for 'net.java.dev.jna:jna:5.12.1' and
'com.github.federico2014.besu-native:gnark:1.3.11' to use implementation so they
are not exposed transitively.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 18, 2026

Choose a reason for hiding this comment

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

P1: Supply-chain risk: this pulls a security-critical cryptographic library (BN128 precompiled contracts) from a personal GitHub fork via JitPack rather than from the official Hyperledger Besu artifact.

JitPack builds from the fork's source on the fly, so the fork owner can push arbitrary changes that will silently flow into this project. For a library that implements elliptic-curve operations used in consensus, this is a high-risk dependency.

Consider using the official artifact from Maven Central (e.g., org.hyperledger.besu:besu-native-gnark) or, if a fork is genuinely required, vendoring the fork under the project's own organization with pinned commit verification.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crypto/build.gradle, line 16:

<comment>Supply-chain risk: this pulls a **security-critical** cryptographic library (BN128 precompiled contracts) from a personal GitHub fork via JitPack rather than from the official Hyperledger Besu artifact.

JitPack builds from the fork's source on the fly, so the fork owner can push arbitrary changes that will silently flow into this project. For a library that implements elliptic-curve operations used in consensus, this is a high-risk dependency.

Consider using the official artifact from Maven Central (e.g., `org.hyperledger.besu:besu-native-gnark`) or, if a fork is genuinely required, vendoring the fork under the project's own organization with pinned commit verification.</comment>

<file context>
@@ -12,6 +12,8 @@ repositories {
 dependencies {
     api project(":common")
+    api 'net.java.dev.jna:jna:5.12.1'
+    api 'com.github.federico2014.besu-native:gnark:1.3.11'
 }
 
</file context>
Fix with Cubic

}

jacocoTestReport {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package org.tron.common.crypto.bn128;

import com.sun.jna.ptr.IntByReference;
import java.util.Arrays;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.hyperledger.besu.nativelib.gnark.LibGnarkEIP196;

@Slf4j(topic = "crypto")
public class BN128Executor {

public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];

private static final int PAIR_SIZE = 192;

// Limit to 100 pairs (19,200 bytes) in optimized mode to prevent timeout attacks
private static final int MAX_PAIR_SIZE_LIMIT = 192 * 100;

public static Pair<Boolean, byte[]> add(byte[] data) {
if (data == null) {
data = EMPTY_BYTE_ARRAY;
}

byte[] input = data.length > 128
? Arrays.copyOfRange(data, 0, 128) : data;
Pair<Boolean, byte[]> result = executeEIP196Operation(
LibGnarkEIP196.EIP196_ADD_OPERATION_RAW_VALUE, input);
return result;
}

public static Pair<Boolean, byte[]> mul(byte[] data) {
if (data == null) {
data = EMPTY_BYTE_ARRAY;
}

byte[] input = data.length > 96
? Arrays.copyOfRange(data, 0, 96) : data;
Pair<Boolean, byte[]> result = executeEIP196Operation(
LibGnarkEIP196.EIP196_MUL_OPERATION_RAW_VALUE, input);
return result;
}

public static Pair<Boolean, byte[]> pairing(byte[] data) {
if (data == null) {
data = EMPTY_BYTE_ARRAY;
}

if (data.length > MAX_PAIR_SIZE_LIMIT
|| data.length % PAIR_SIZE > 0) {
return Pair.of(false, EMPTY_BYTE_ARRAY);
}
Pair<Boolean, byte[]> result = executeEIP196Operation(
LibGnarkEIP196.EIP196_PAIR_OPERATION_RAW_VALUE, data);
return result;

}


private static Pair<Boolean, byte[]> executeEIP196Operation(
byte operation, byte[] data) {
if (!LibGnarkEIP196.ENABLED) {
logger.warn("Native BN128 library not available, "
+ "cannot execute optimized path");
return null;
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 18, 2026

Choose a reason for hiding this comment

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

P1: Returning null here will cause a NullPointerException in the callers (add, mul, pairing), since none of them handle a null return. Return Pair.of(false, EMPTY_BYTE_ARRAY) to match the error convention used elsewhere, or throw a descriptive exception.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crypto/src/main/java/org/tron/common/crypto/bn128/BN128Executor.java, line 64:

<comment>Returning `null` here will cause a `NullPointerException` in the callers (`add`, `mul`, `pairing`), since none of them handle a null return. Return `Pair.of(false, EMPTY_BYTE_ARRAY)` to match the error convention used elsewhere, or throw a descriptive exception.</comment>

<file context>
@@ -0,0 +1,92 @@
+    if (!LibGnarkEIP196.ENABLED) {
+      logger.warn("Native BN128 library not available, "
+          + "cannot execute optimized path");
+      return null;
+    }
+
</file context>
Fix with Cubic

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 18, 2026

Choose a reason for hiding this comment

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

P1: executeEIP196Operation returns null when the native library is unavailable, but all three public callers (add, mul, pairing) pass through this return value without a null check. Any caller that doesn't defensively guard for null will get a NullPointerException. Return Pair.of(false, EMPTY_BYTE_ARRAY) to be consistent with the error-handling pattern used elsewhere in this class.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crypto/src/main/java/org/tron/common/crypto/bn128/BN128Executor.java, line 64:

<comment>`executeEIP196Operation` returns `null` when the native library is unavailable, but all three public callers (`add`, `mul`, `pairing`) pass through this return value without a null check. Any caller that doesn't defensively guard for null will get a `NullPointerException`. Return `Pair.of(false, EMPTY_BYTE_ARRAY)` to be consistent with the error-handling pattern used elsewhere in this class.</comment>

<file context>
@@ -0,0 +1,92 @@
+    if (!LibGnarkEIP196.ENABLED) {
+      logger.warn("Native BN128 library not available, "
+          + "cannot execute optimized path");
+      return null;
+    }
+
</file context>
Fix with Cubic

}

final byte[] output =
new byte[LibGnarkEIP196.EIP196_PREALLOCATE_FOR_RESULT_BYTES];
final IntByReference outputLength = new IntByReference();
final byte[] error =
new byte[LibGnarkEIP196.EIP196_PREALLOCATE_FOR_ERROR_BYTES];
final IntByReference errorLength = new IntByReference();

int ret = LibGnarkEIP196.eip196_perform_operation(
operation, data, data.length,
output, outputLength, error, errorLength);

if (ret == 0) {
return Pair.of(true,
subArray(output, 0, outputLength.getValue()));
} else {
return Pair.of(false, EMPTY_BYTE_ARRAY);
}
}

public static byte[] subArray(byte[] input, int start, int end) {
byte[] result = new byte[end - start];
System.arraycopy(input, start, result, 0, end - start);
return result;
}

}
Loading
Loading