0004 regenerated from the production tree: the base-fee floor reads its fork height and value from the documented properties instead of hard-coded constants; with it the public recipe reproduces LondonFeeMarket and all three consensus jars class for class (measured 2026-09-05)

This commit is contained in:
Aere Network 2026-09-05 13:06:49 +03:00
parent 3d51da9747
commit 75be84f043
2 changed files with 218 additions and 106 deletions

View File

@ -83,7 +83,12 @@ There is a second Aere change to the client, and it is not in patch `0001`:
`patches/0004-aere-basefee-floor.patch`, one file, `LondonFeeMarket.java`. From block **10,141,734**
chain 2800 clamps the EIP-1559 base fee to a floor of 1 Gwei. The patch is fork gated by two JVM
system properties and is a byte behaviour no-op when they are unset, so a node that applies it
without setting them is stock Besu:
without setting them is stock Besu. Regenerated 2026-09-05 from the production tree: the previous
revision of this patch hard-coded both values as constants, so on a node built from this recipe the
two properties were dead (the floor was still 1 Gwei from 10,141,734, so the computed base fee was
the same, but the sentence above was not true of the code). Found by comparing the fleet's jars
with a build of this recipe class by class; the three consensus jars were already identical, this
one class was not.
```
-Daere.basefee.floor.forkBlock=10141734 -Daere.basefee.floor.value=1000000000

View File

@ -1,129 +1,209 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aere Network <node@aere.network>
Date: Mon, 3 Aug 2026 04:00:00 +0300
Subject: [PATCH] Aere Network: fork-gated consensus floor for the EIP-1559
base fee
Date: Sat, 05 Sep 2026 09:53:25 +0000
Subject: [PATCH] Aere Network: base-fee floor for chain 2800 (production form)
Adds a real consensus minimum base fee to LondonFeeMarket, gated on a fork
block. When the block number reaches aere.basefee.floor.forkBlock, the computed
EIP-1559 base fee is clamped to max(computed, aere.basefee.floor.value).
Apache License 2.0, section 4(b): the files this patch modifies are the work of
Hyperledger Besu and carry its copyright notice; the modifications are by
contributors to the Aere Network under the same licence.
computeBaseFee is used for BOTH block production and block validation, so this
is a genuine consensus rule and not a display trick: every node must carry the
change, and a node without it computes the old, lower base fee and rejects the
floored block.
Default forkBlock is Long.MAX_VALUE, so with no configuration the behaviour is
identical to the unmodified file and pre-fork blocks validate unchanged.
ACTIVE ON AERE NETWORK CHAIN 2800 since block 10,141,734, with a floor of
1,000,000,000 wei, delivered to every node as system properties
(-Daere.basefee.floor.forkBlock=10141734 -Daere.basefee.floor.value=1000000000,
see RUN-A-NODE.md). A node without this patch, or with the properties unset,
computes a lower base fee from that height on and rejects every block.
Upstream-Status: Inappropriate [Aere Network specific consensus rule]
Modified upstream files. One file touched by this patch is a modified copy of a
Hyperledger Besu source, not new work by Aere Network:
ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/LondonFeeMarket.java
Aere Network changed it, against upstream commit
d2032017bb3b8cb215a97303980a1e4a643f7180. Its original "Copyright ConsenSys AG"
header is kept unchanged, which is what Apache License 2.0 section 4(c)
requires, and this patch adds below it a separate "Modifications Copyright"
block naming exactly what was changed. That in-file block is the notice
required by Apache License 2.0 section 4(b). It travels inside the diff, so a
tree with this patch applied carries the notice whether you used git am or git
apply. This patch creates no new files.
Note that the copyright holder on this file is ConsenSys AG, not "contributors
to Hyperledger Besu" as on the files touched by patches 0001 and 0002. Both
holders are named in NOTICE, as Apache License 2.0 section 4(d) requires.
Regenerated 2026-09-05 from the production tree, so that the public recipe
reproduces the live binary class for class: the floor and its fork height are
read from the system properties aere.basefee.floor.forkBlock and
aere.basefee.floor.value (or the AERE_BASEFEE_FLOOR_* environment variables),
absent = disarmed, exactly as RUN-A-NODE.md tells an operator to set them.
The previous revision of this patch hard-coded both as constants, so the
operator's property was dead on a node built from the public recipe (found
2026-09-05 by comparing the fleet's jars with the public build class by class).
---
.../mainnet/feemarket/LondonFeeMarket.java | 48 +++++++++++++++++--
1 file changed, 45 insertions(+), 3 deletions(-)
diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/LondonFeeMarket.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/LondonFeeMarket.java
index 0ee4f7409..83a639c1b 100644
index 0ee4f7409..558076166 100644
--- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/LondonFeeMarket.java
+++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/LondonFeeMarket.java
@@ -1,17 +1,35 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
+
+/*
+ * Modifications Copyright 2026 Aere Network.
+ *
+ * This file was changed by Aere Network: a fork-gated consensus minimum for the
+ * EIP-1559 base fee was added. Two system properties configure it
+ * (aere.basefee.floor.forkBlock, aere.basefee.floor.value), a private helper
+ * applyAereBaseFeeFloor was added, and the three return paths of computeBaseFee
+ * now pass through that helper. The default fork block is Long.MAX_VALUE, so an
+ * unconfigured node behaves exactly as the unmodified file does. Nothing upstream
+ * was removed or rewritten.
+ *
+ * The unmodified original is Hyperledger Besu commit
+ * d2032017bb3b8cb215a97303980a1e4a643f7180. The upstream copyright header above is
+ * left exactly as it was found.
+ *
+ * This notice is required by Apache License 2.0 section 4(b).
+ */
package org.hyperledger.besu.ethereum.mainnet.feemarket;
@@ -17,121 +17,230 @@ package org.hyperledger.besu.ethereum.mainnet.feemarket;
import org.hyperledger.besu.config.GenesisConfig;
@@ -34,6 +52,19 @@ public class LondonFeeMarket implements BaseFeeMarket {
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.feemarket.TransactionPriceCalculator;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256s;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LondonFeeMarket implements BaseFeeMarket {
private static final Logger LOG = LoggerFactory.getLogger(LondonFeeMarket.class);
static final Wei DEFAULT_BASEFEE_INITIAL_VALUE = GenesisConfig.BASEFEE_AT_GENESIS_DEFAULT_VALUE;
static final long DEFAULT_BASEFEE_MAX_CHANGE_DENOMINATOR = 8L;
static final long DEFAULT_SLACK_COEFFICIENT = 2L;
private static final Wei DEFAULT_BASEFEE_FLOOR = Wei.of(7L);
+ // === AERE base-fee floor fork (fork-gated, system-property configured) ===
+ // A real consensus minimum base fee, not a display trick. When the running block
+ // number reaches AERE_BASEFEE_FLOOR_FORK_BLOCK, the EIP-1559 base fee is clamped to
+ // max(computed, AERE_BASEFEE_FLOOR_VALUE). Because computeBaseFee is used for BOTH
+ // block production AND block validation, this is a genuine consensus rule: every node
+ // must agree, and a node without this change computes the old (lower) base fee and
+ // rejects the floored block. Default forkBlock = Long.MAX_VALUE => never active =>
+ // byte-identical behavior to stock Besu (pre-fork blocks are unchanged).
+ private static final long AERE_BASEFEE_FLOOR_FORK_BLOCK =
+ Long.getLong("aere.basefee.floor.forkBlock", Long.MAX_VALUE);
+ private static final Wei AERE_BASEFEE_FLOOR_VALUE =
+ Wei.of(Long.getLong("aere.basefee.floor.value", 1_000_000_000L));
+ // AERE base-fee-floor fork (fork-gated, no-op until an operator activates it).
+ //
+ // Raises the consensus EIP-1559 base-fee floor from the emergent 7-wei integer-division floor
+ // to a configurable value (default 1 Gwei) at and after a chosen fork block, so the public RPC
+ // can serve a TRUE, block-hash-verifiable baseFeePerGas instead of a proxy-faked constant.
+ //
+ // Activation is read once at fee-market construction (node start) from a JVM system property
+ // with an environment-variable fallback, mirroring how the AERE PQC/Block-STM fork reads its
+ // own JVM-level configuration:
+ // -Daere.basefee.floor.forkBlock=<n> (env AERE_BASEFEE_FLOOR_FORKBLOCK) default Long.MAX_VALUE (UNSET => no-op)
+ // -Daere.basefee.floor.value=<wei> (env AERE_BASEFEE_FLOOR_VALUE) default 1_000_000_000 (1 Gwei)
+ //
+ // UNSET (default) means the fork block is Long.MAX_VALUE, so no real block ever reaches it and
+ // every code path below is byte-identical to upstream London behaviour (7-wei emergent floor).
+ static final long DEFAULT_AERE_BASEFEE_FLOOR_VALUE = 1_000_000_000L; // 1 Gwei in wei
+ private static final String AERE_FLOOR_FORK_BLOCK_PROPERTY = "aere.basefee.floor.forkBlock";
+ private static final String AERE_FLOOR_FORK_BLOCK_ENV = "AERE_BASEFEE_FLOOR_FORKBLOCK";
+ private static final String AERE_FLOOR_VALUE_PROPERTY = "aere.basefee.floor.value";
+ private static final String AERE_FLOOR_VALUE_ENV = "AERE_BASEFEE_FLOOR_VALUE";
+
protected final Wei baseFeeInitialValue;
private final long londonForkBlockNumber;
private final TransactionPriceCalculator txPriceCalculator;
@@ -83,6 +114,17 @@ public class LondonFeeMarket implements BaseFeeMarket {
private final Wei baseFeeFloor;
+ // Activation block for the AERE base-fee floor. Long.MAX_VALUE == UNSET == no-op.
+ private final long aereBaseFeeFloorForkBlock;
+ // Floor value applied at and after aereBaseFeeFloorForkBlock (wei).
+ private final long aereBaseFeeFloorValue;
+
LondonFeeMarket(final long londonForkBlockNumber, final Optional<Wei> baseFeePerGasOverride) {
this(TransactionPriceCalculator.eip1559(), londonForkBlockNumber, baseFeePerGasOverride);
}
LondonFeeMarket(
final TransactionPriceCalculator txPriceCalculator,
final long londonForkBlockNumber,
final Optional<Wei> baseFeePerGasOverride) {
+ // Production path: resolve the AERE base-fee-floor fork activation from JVM configuration.
+ this(
+ txPriceCalculator,
+ londonForkBlockNumber,
+ baseFeePerGasOverride,
+ readAereFloorForkBlockConfig(),
+ readAereFloorValueConfig());
+ }
+
+ // Package-private constructor taking explicit AERE floor-fork parameters. Used by the
+ // production constructor above (after resolving JVM config) and by unit tests so the fork can be
+ // exercised deterministically without mutating global JVM system properties / environment.
+ LondonFeeMarket(
+ final TransactionPriceCalculator txPriceCalculator,
+ final long londonForkBlockNumber,
+ final Optional<Wei> baseFeePerGasOverride,
+ final long aereBaseFeeFloorForkBlock,
+ final long aereBaseFeeFloorValue) {
this.txPriceCalculator = txPriceCalculator;
this.londonForkBlockNumber = londonForkBlockNumber;
this.baseFeeInitialValue = baseFeePerGasOverride.orElse(DEFAULT_BASEFEE_INITIAL_VALUE);
this.baseFeeFloor = baseFeeInitialValue.isZero() ? Wei.ZERO : DEFAULT_BASEFEE_FLOOR;
+ this.aereBaseFeeFloorForkBlock = aereBaseFeeFloorForkBlock;
+ this.aereBaseFeeFloorValue = aereBaseFeeFloorValue;
+ }
+
+ private static long readAereFloorForkBlockConfig() {
+ return readLongConfig(
+ AERE_FLOOR_FORK_BLOCK_PROPERTY, AERE_FLOOR_FORK_BLOCK_ENV, Long.MAX_VALUE);
+ }
+
+ private static long readAereFloorValueConfig() {
+ return readLongConfig(
+ AERE_FLOOR_VALUE_PROPERTY, AERE_FLOOR_VALUE_ENV, DEFAULT_AERE_BASEFEE_FLOOR_VALUE);
+ }
+
+ private static long readLongConfig(
+ final String systemProperty, final String envVar, final long defaultValue) {
+ String raw = System.getProperty(systemProperty);
+ if (raw == null || raw.isBlank()) {
+ raw = System.getenv(envVar);
+ }
+ if (raw == null || raw.isBlank()) {
+ return defaultValue;
+ }
+ try {
+ final long parsed = Long.parseLong(raw.trim());
+ if (parsed < 0L) {
+ LOG.warn(
+ "Ignoring negative {}/{} value '{}', using default {}",
+ systemProperty,
+ envVar,
+ raw,
+ defaultValue);
+ return defaultValue;
+ }
+ return parsed;
+ } catch (final NumberFormatException e) {
+ LOG.warn(
+ "Ignoring unparseable {}/{} value '{}', using default {}",
+ systemProperty,
+ envVar,
+ raw,
+ defaultValue);
+ return defaultValue;
+ }
+ }
+
+ // Clamp a computed baseFee up to the active AERE floor at and after the fork block. Before the
+ // fork block (and when UNSET, since Long.MAX_VALUE is never reached) this returns baseFee
+ // unchanged, keeping pre-fork behaviour byte-identical to upstream.
+ private Wei applyAereBaseFeeFloor(final long blockNumber, final Wei baseFee) {
+ if (blockNumber >= aereBaseFeeFloorForkBlock) {
+ return UInt256s.max(baseFee, Wei.of(aereBaseFeeFloorValue));
+ }
+ return baseFee;
+ }
+
+ // Effective tx-pool minimum-fee floor. Pre-fork (UNSET) this is exactly the legacy baseFeeFloor
+ // (7 wei, or 0 on a zero-basefee chain), so tx-pool acceptance is byte-identical. Once an
+ // operator activates the AERE base-fee-floor fork, the tx-pool floor rises to the active AERE
+ // floor so tx-pool pricing agrees with the >= 1 Gwei header baseFee produced by computeBaseFee.
+ // A zero-basefee chain (baseFeeFloor == 0) is never raised.
+ private Wei effectiveBaseFeeFloor() {
+ if (baseFeeFloor.isZero() || aereBaseFeeFloorForkBlock == Long.MAX_VALUE) {
+ return baseFeeFloor;
+ }
+ return UInt256s.max(baseFeeFloor, Wei.of(aereBaseFeeFloorValue));
}
@Override
public long getBasefeeMaxChangeDenominator() {
return DEFAULT_BASEFEE_MAX_CHANGE_DENOMINATOR;
}
@Override
public Wei getInitialBasefee() {
return baseFeeInitialValue;
}
@Override
public long getSlackCoefficient() {
return DEFAULT_SLACK_COEFFICIENT;
}
@Override
public TransactionPriceCalculator getTransactionPriceCalculator() {
return txPriceCalculator;
}
@Override
public boolean satisfiesFloorTxFee(final Transaction txn) {
// ensure effective baseFee is at least above floor
return txn.getGasPrice()
.map(Optional::of)
.orElse(txn.getMaxFeePerGas())
- .filter(fee -> fee.greaterOrEqualThan(baseFeeFloor))
+ .filter(fee -> fee.greaterOrEqualThan(effectiveBaseFeeFloor()))
.isPresent();
}
+ // AERE base-fee floor fork: clamp the computed base fee to the floor once the fork
+ // block is reached. No-op before the fork block (and always a no-op when the fork is
+ // unset, i.e. forkBlock = Long.MAX_VALUE), so pre-fork blocks validate byte-identically.
+ private Wei applyAereBaseFeeFloor(final long blockNumber, final Wei fee) {
+ if (blockNumber >= AERE_BASEFEE_FLOOR_FORK_BLOCK
+ && !fee.greaterOrEqualThan(AERE_BASEFEE_FLOOR_VALUE)) {
+ return AERE_BASEFEE_FLOOR_VALUE;
+ }
+ return fee;
+ }
+
@Override
public Wei computeBaseFee(
final long blockNumber,
@@ -90,13 +132,13 @@ public class LondonFeeMarket implements BaseFeeMarket {
final Wei parentBaseFee,
final long parentBlockGasUsed,
final long targetGasUsed) {
if (londonForkBlockNumber == blockNumber) {
@ -139,7 +219,20 @@ index 0ee4f7409..83a639c1b 100644
} else if (parentBlockGasUsed > targetGasUsed) {
gasDelta = parentBlockGasUsed - targetGasUsed;
final long denominator = getBasefeeMaxChangeDenominator();
@@ -117,7 +159,7 @@ public class LondonFeeMarket implements BaseFeeMarket {
feeDelta =
UInt256s.max(
parentBaseFee.multiply(gasDelta).divide(targetGasUsed).divide(denominator), Wei.ONE);
baseFee = parentBaseFee.add(feeDelta);
} else {
gasDelta = targetGasUsed - parentBlockGasUsed;
final long denominator = getBasefeeMaxChangeDenominator();
feeDelta = parentBaseFee.multiply(gasDelta).divide(targetGasUsed).divide(denominator);
baseFee = parentBaseFee.subtract(feeDelta);
}
LOG.trace(
"block #{} parentBaseFee: {} parentGasUsed: {} parentGasTarget: {} baseFee: {}",
blockNumber,
parentBaseFee,
parentBlockGasUsed,
targetGasUsed,
baseFee);
@ -148,3 +241,17 @@ index 0ee4f7409..83a639c1b 100644
}
@Override
public ValidationMode baseFeeValidationMode(final long blockNumber) {
return londonForkBlockNumber == blockNumber ? ValidationMode.INITIAL : ValidationMode.ONGOING;
}
@Override
public ValidationMode gasLimitValidationMode(final long blockNumber) {
return londonForkBlockNumber == blockNumber ? ValidationMode.INITIAL : ValidationMode.ONGOING;
}
@Override
public boolean isBeforeForkBlock(final long blockNumber) {
return londonForkBlockNumber > blockNumber;
}
}