- Apache 2.0 section 4(b): the patches modify files that are the work of Hyperledger Besu and now say so, with the notice inside the modified files, which is what the licence asks for and what applying the patch produces. - patches/ and precompiles/ now carry the staged versions rather than an older export. The two had drifted in both directions; the only thing the published copy had that the staged one lacked was the word "audited" in front of Bouncy Castle, which we cannot evidence and which the staged version had dropped. - the brand was spelled two ways in the same repository, 62 times one way and 23 the other. It is Aere Network; AERE is the ticker. The 96 AERE_* code identifiers are untouched.
94 lines
3.3 KiB
Java
94 lines
3.3 KiB
Java
/*
|
|
* Copyright contributors to the Aere Network.
|
|
*
|
|
* 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
|
|
*/
|
|
package org.hyperledger.besu.evm.precompile;
|
|
|
|
import org.hyperledger.besu.evm.frame.MessageFrame;
|
|
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
|
|
|
import jakarta.validation.constraints.NotNull;
|
|
import org.apache.tuweni.bytes.Bytes;
|
|
import org.bouncycastle.crypto.digests.SHAKEDigest;
|
|
|
|
/**
|
|
* AERE PQC precompile: SHAKE256 extendable-output function (FIPS 202).
|
|
*
|
|
* <p>Input layout: outLen (32 bytes, big-endian, capped at MAX_OUTPUT) || data (arbitrary length).
|
|
* Output: exactly {@code outLen} bytes of SHAKE256(data).
|
|
*
|
|
* <p>SHAKE256 is the hashing bottleneck inside Falcon, ML-DSA and SLH-DSA; exposing it natively lets
|
|
* on-chain PQC flows offload the hot path to audited Bouncy Castle rather than hand-rolled Solidity.
|
|
*/
|
|
public class SHAKE256PrecompiledContract extends AbstractPrecompiledContract {
|
|
|
|
/** Upper bound on requested output length to keep gas/allocation bounded. */
|
|
static final int MAX_OUTPUT = 1 << 16; // 65536 bytes
|
|
|
|
private static final int BASE_GAS = 60;
|
|
private static final int GAS_PER_WORD = 12;
|
|
|
|
/**
|
|
* Instantiates a new SHAKE256 precompiled contract.
|
|
*
|
|
* @param gasCalculator the gas calculator
|
|
*/
|
|
SHAKE256PrecompiledContract(final GasCalculator gasCalculator) {
|
|
super("AereSHAKE256", gasCalculator);
|
|
}
|
|
|
|
private static int outputLength(final Bytes input) {
|
|
if (input.size() < 32) {
|
|
return 0;
|
|
}
|
|
// Big-endian 32-byte length; only the low 4 bytes are honoured, then capped.
|
|
long v = input.slice(28, 4).toLong() & 0xFFFFFFFFL;
|
|
// If any of the high 28 bytes are non-zero the value is enormous; cap regardless.
|
|
if (!input.slice(0, 28).isZero()) {
|
|
return MAX_OUTPUT;
|
|
}
|
|
if (v > MAX_OUTPUT) {
|
|
return MAX_OUTPUT;
|
|
}
|
|
return (int) v;
|
|
}
|
|
|
|
@Override
|
|
public long gasRequirement(final Bytes input) {
|
|
int outLen = outputLength(input);
|
|
int dataLen = input.size() < 32 ? 0 : input.size() - 32;
|
|
long words = ((long) dataLen + 31) / 32 + ((long) outLen + 31) / 32;
|
|
return BASE_GAS + GAS_PER_WORD * words;
|
|
}
|
|
|
|
@NotNull
|
|
@Override
|
|
public PrecompileContractResult computePrecompile(
|
|
final Bytes input, @NotNull final MessageFrame messageFrame) {
|
|
if (input.size() < 32) {
|
|
return PrecompileContractResult.success(Bytes.EMPTY);
|
|
}
|
|
final int outLen = outputLength(input);
|
|
final byte[] data = input.slice(32).toArrayUnsafe();
|
|
final SHAKEDigest digest = new SHAKEDigest(256);
|
|
if (data.length > 0) {
|
|
digest.update(data, 0, data.length);
|
|
}
|
|
final byte[] out = new byte[outLen];
|
|
if (outLen > 0) {
|
|
digest.doFinal(out, 0, outLen);
|
|
}
|
|
return PrecompileContractResult.success(Bytes.wrap(out));
|
|
}
|
|
}
|