Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ public class Address extends DelegatingBytes {
/** The constant BLS12_MAP_FP2_TO_G2. */
public static final Address BLS12_MAP_FP2_TO_G2 = Address.precompiled(0x11);

/** Precompile address for P256_VERIFY. */
public static final Address P256_VERIFY = Address.precompiled(0x0100);

/** The constant ZERO. */
public static final Address ZERO = Address.fromHexString("0x0");

Expand Down Expand Up @@ -214,10 +217,11 @@ public static Address fromHexStringStrict(final String str) {
* @return the address
*/
public static Address precompiled(final int value) {
// Keep it simple while we don't need precompiled above 127.
checkArgument(value < Byte.MAX_VALUE);
// Allow values up to 0x01FF (511) to encompass layer2 precompile address space
checkArgument(value < 0x01FF, "Precompiled value must be <= 0x01FF");
final byte[] address = new byte[SIZE];
address[SIZE - 1] = (byte) value;
address[SIZE - 2] = (byte) (value >>> 8); // High byte
address[SIZE - 1] = (byte) (value & 0xFF); // Low byte
return new Address(Bytes.wrap(address));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
*/
package org.hyperledger.besu.evm.gascalculator;

import static org.hyperledger.besu.datatypes.Address.BLS12_MAP_FP2_TO_G2;
import static org.hyperledger.besu.datatypes.Address.P256_VERIFY;
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
import static org.hyperledger.besu.evm.internal.Words.clampedMultiply;
import static org.hyperledger.besu.evm.internal.Words.clampedToInt;

import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.evm.internal.Words;
import org.hyperledger.besu.evm.precompile.BigIntegerModularExponentiationPrecompiledContract;

Expand All @@ -37,7 +38,7 @@ public class OsakaGasCalculator extends PragueGasCalculator {

/** Instantiates a new Osaka Gas Calculator. */
public OsakaGasCalculator() {
this(BLS12_MAP_FP2_TO_G2.toArrayUnsafe()[19]);
this(P256_VERIFY.getInt(16));
}

/**
Expand All @@ -49,6 +50,25 @@ protected OsakaGasCalculator(final int maxPrecompile) {
super(maxPrecompile);
}

@Override
public boolean isPrecompile(final Address address) {
final byte[] addressBytes = address.toArrayUnsafe();

// First 18 bytes must be zero:
for (int i = 0; i < 18; i++) {
if (addressBytes[i] != 0) {
return false;
}
}

// Interpret last two bytes as big-endian unsigned short.
final int precompileValue =
(Byte.toUnsignedInt(addressBytes[18]) << 8) | Byte.toUnsignedInt(addressBytes[19]);

// values in range [1, 0x01FF] inclusive to include L1 and L2 precompiles:
return precompileValue > 0 && precompileValue <= 0x01FF;
}

@Override
public long modExpGasCost(final Bytes input) {
final long baseLength = BigIntegerModularExponentiationPrecompiledContract.baseLength(input);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
*/
package org.hyperledger.besu.evm.precompile;

import static org.hyperledger.besu.datatypes.Address.P256_VERIFY;

import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;

Expand Down Expand Up @@ -197,8 +199,8 @@ static void populateForOsaka(
// EIP-7823 - Set upper bounds for MODEXP
registry.put(
Address.MODEXP, BigIntegerModularExponentiationPrecompiledContract.osaka(gasCalculator));
// RIP-7212 - secp256r1 P256VERIFY
registry.put(Address.precompiled(100), new P256VerifyPrecompiledContract(gasCalculator));
// EIP-7951 - secp256r1 P256VERIFY
registry.put(P256_VERIFY, new P256VerifyPrecompiledContract(gasCalculator));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,36 +27,43 @@
import com.github.benmanes.caffeine.cache.Caffeine;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.bouncycastle.asn1.sec.SECNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Draft implementation of RIP-7212 / EIP-7951.
*
* <p>TODO: malleable signatures, point at infinity check, modular comparison, caching
* implementation
*/
/** Implementation of EIP-7951. */
public class P256VerifyPrecompiledContract extends AbstractPrecompiledContract {
private static final Logger LOG = LoggerFactory.getLogger(P256VerifyPrecompiledContract.class);
private static final String PRECOMPILE_NAME = "P256VERIFY";
private static final Bytes32 VALID = Bytes32.leftPad(Bytes.of(1), (byte) 0);
private static final Bytes INVALID = Bytes.EMPTY;

private static final X9ECParameters R1_PARAMS = SECNamedCurves.getByName("secp256r1");
private static final BigInteger N = R1_PARAMS.getN();
private static final BigInteger P = R1_PARAMS.getCurve().getField().getCharacteristic();

private final GasCalculator gasCalculator;
private final SignatureAlgorithm signatureAlgorithm;

private static final Cache<Integer, PrecompileInputResultTuple> p256VerifyCache =
Caffeine.newBuilder().maximumSize(1000).build();

/**
* Instantiates a new Abstract precompiled contract.
* Instantiates a new P256Verify precompiled contract.
*
* @param gasCalculator the gas calculator
*/
public P256VerifyPrecompiledContract(final GasCalculator gasCalculator) {
this(gasCalculator, new SECP256R1());
}

/**
* Instantiates a new P256Verify precompiled contract.
*
* @param gasCalculator the gas calculator
* @param signatureAlgorithm the signature algorithm
*/
public P256VerifyPrecompiledContract(
final GasCalculator gasCalculator, final SignatureAlgorithm signatureAlgorithm) {
super(PRECOMPILE_NAME, gasCalculator);
Expand All @@ -78,7 +85,7 @@ public PrecompileContractResult computePrecompile(
input.size());
return PrecompileContractResult.success(INVALID);
}
PrecompileInputResultTuple res;
PrecompileInputResultTuple res = null;
Integer cacheKey = null;
if (enableResultCaching) {
cacheKey = getCacheKey(input);
Expand Down Expand Up @@ -106,25 +113,55 @@ public PrecompileContractResult computePrecompile(
final Bytes rBytes = input.slice(32, 32);
final Bytes sBytes = input.slice(64, 32);
final Bytes pubKeyBytes = input.slice(96, 64);
final BigInteger qx = pubKeyBytes.slice(0, 32).toUnsignedBigInteger();
final BigInteger qy = pubKeyBytes.slice(32, 32).toUnsignedBigInteger();

try {
// Convert r and s to BigIntegers (unsigned)
final BigInteger r = rBytes.toUnsignedBigInteger();
final BigInteger s = sBytes.toUnsignedBigInteger();

// Create the signature; recID is not used in verification - use 0
final SECPSignature signature = signatureAlgorithm.createSignature(r, s, (byte) 0);
// Check r, s in (0, n)
if (r.signum() <= 0 || r.compareTo(N) >= 0 || s.signum() <= 0 || s.compareTo(N) >= 0) {
LOG.trace("Invalid r or s: must satisfy 0 < r,s < n");
res =
new PrecompileInputResultTuple(
enableResultCaching ? input.copy() : input,
PrecompileContractResult.success(INVALID));
}

// Check qx, qy in [0, p)
if (qx.signum() < 0 || qx.compareTo(P) >= 0 || qy.signum() < 0 || qy.compareTo(P) >= 0) {
LOG.trace("Invalid qx or qy: must satisfy 0 <= qx,qy < p");
res =
new PrecompileInputResultTuple(
enableResultCaching ? input.copy() : input,
PrecompileContractResult.success(INVALID));
}

// Construct public key from 64-byte uncompressed format (x || y)
final SECPPublicKey publicKey = signatureAlgorithm.createPublicKey(pubKeyBytes);
// Check point not at infinity (qx, qy ≠ 0,0), and non-trivial infinity encoding
if ((qx.signum() == 0 && qy.signum() == 0)) {
LOG.trace("Invalid public key: point at infinity");
res =
new PrecompileInputResultTuple(
enableResultCaching ? input.copy() : input,
PrecompileContractResult.success(INVALID));
}

// Perform verification TODO: implement bouncycastle malleable
final boolean isValid = signatureAlgorithm.verifyMalleable(messageHash, signature, publicKey);
if (res == null) {
// Create the signature; recID is not used in verification - use 0
final SECPSignature signature = signatureAlgorithm.createSignature(r, s, (byte) 0);
final SECPPublicKey publicKey = signatureAlgorithm.createPublicKey(pubKeyBytes);

final boolean isValid =
signatureAlgorithm.verifyMalleable(messageHash, signature, publicKey);

res =
new PrecompileInputResultTuple(
enableResultCaching ? input.copy() : input,
PrecompileContractResult.success(isValid ? VALID : INVALID));
}

res =
new PrecompileInputResultTuple(
enableResultCaching ? input.copy() : input,
PrecompileContractResult.success(isValid ? VALID : INVALID));
if (enableResultCaching) {
p256VerifyCache.put(cacheKey, res);
}
Expand Down