aere-node/patches/0004-aere-basefee-floor.patch

285 lines
12 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aere Network <node@aere.network>
Date: Thu, 10 Sep 2026 22:39:39 +0300
Subject: [PATCH] Aere Network: base-fee floor for chain 2800 (production form)
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.
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).
Regenerated 2026-09-10 from the production tree over the same file set, so that
the public recipe carries the same Apache-2.0 section 4(b) modification notices
that the production sources carry; measured the same day: a build from this
recipe reproduces the fleet's consensus jars class for class.
---
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..e7c8c9724 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,137 +1,252 @@
/*
* 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 contributors to the Aere Network.
+ *
+ * This file was modified by contributors to the Aere Network, as required by section 4(b) of the
+ * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was
+ * found, as section 4(c) requires. The change itself is in patches/0004-aere-basefee-floor.patch.
*/
package org.hyperledger.besu.ethereum.mainnet.feemarket;
import org.hyperledger.besu.config.GenesisConfig;
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, 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;
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();
}
@Override
public Wei computeBaseFee(
final long blockNumber,
final Wei parentBaseFee,
final long parentBlockGasUsed,
final long targetGasUsed) {
if (londonForkBlockNumber == blockNumber) {
- return getInitialBasefee();
+ return applyAereBaseFeeFloor(blockNumber, getInitialBasefee());
}
long gasDelta;
Wei feeDelta, baseFee;
if (parentBlockGasUsed == targetGasUsed) {
- return parentBaseFee;
+ return applyAereBaseFeeFloor(blockNumber, parentBaseFee);
} else if (parentBlockGasUsed > targetGasUsed) {
gasDelta = parentBlockGasUsed - targetGasUsed;
final long denominator = getBasefeeMaxChangeDenominator();
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);
- return baseFee;
+ return applyAereBaseFeeFloor(blockNumber, baseFee);
}
@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;
}
}