Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update falcon precompiled interface #4

Closed
wants to merge 1 commit into from
Closed
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 @@ -26,6 +26,7 @@
import com.google.common.base.Suppliers;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.bouncycastle.crypto.digests.SHAKEDigest;

/** Various utilities for providing hashes (digests) of arbitrary data. */
public abstract class Hash {
Expand Down Expand Up @@ -105,4 +106,19 @@ public static Bytes ripemd160(final Bytes input) {
public static Bytes blake2bf(final Bytes input) {
return Bytes.wrap(digestUsingAlgorithm(input, BLAKE2BF_SUPPLIER));
}

/**
* Digest using Shake256
*
* @param input The input bytes to produce the digest for
* @param outputLength the number of bytes to produce for the output length
* @return A digest
*/
public static Bytes shake256(final Bytes input, final int outputLength) {
SHAKEDigest digest = new SHAKEDigest(256);
digest.update(input.toArray(), 0, input.size());
byte[] output = new byte[32];
digest.doFinal(output, 0, outputLength);
return Bytes.wrap(output);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ public class FrontierGasCalculator implements GasCalculator {

private static final long SELF_DESTRUCT_REFUND_AMOUNT = 24_000L;

private static final long FREC_PRECOMPILED_GAS_COST = 2350L;

@Override
public long transactionIntrinsicGasCost(final Bytes payload, final boolean isContractCreate) {
int zeros = 0;
Expand Down Expand Up @@ -158,6 +160,11 @@ public long getEcrecPrecompiledContractGasCost() {
return ECREC_PRECOMPILED_GAS_COST;
}

@Override
public long getFrecPrecompiledContractGasCost() {
return FREC_PRECOMPILED_GAS_COST;
}

@Override
public long sha256PrecompiledContractGasCost(final Bytes input) {
return SHA256_PRECOMPILED_WORD_GAS_COST * Words.numWords(input)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@ long callOperationGasCost(
Wei transferValue,
Account recipient,
Address contract);
/**
* Gas cost to execute the {@link FalconPrecompiledContract}.
*
* @return the gas cost to execute the Falcon Signature verification precompiled contract
*/
long getFrecPrecompiledContractGasCost();

/**
* Gets additional call stipend.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
import static java.nio.charset.StandardCharsets.UTF_8;

import org.hyperledger.besu.crypto.Hash;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;

import java.util.Optional;
import javax.annotation.Nonnull;

import org.apache.tuweni.bytes.Bytes;
Expand All @@ -32,10 +34,10 @@

public class FalconPrecompiledContract extends AbstractPrecompiledContract {

private static final Logger LOG = LoggerFactory.getLogger(AbstractBLS12PrecompiledContract.class);
private static final Logger LOG = LoggerFactory.getLogger(FalconPrecompiledContract.class);

private static final Bytes METHOD_ABI =
Hash.keccak256(Bytes.of("verify(bytes,bytes,bytes)".getBytes(UTF_8))).slice(0, 4);
Hash.keccak256(Bytes.of("verify(bytes,bytes,bytes32)".getBytes(UTF_8))).slice(0, 4);
private static final String SIGNATURE_ALGORITHM = "Falcon-512";

private final FalconSigner falconSigner = new FalconSigner();
Expand All @@ -46,7 +48,7 @@ public FalconPrecompiledContract(final GasCalculator gasCalculator) {

@Override
public long gasRequirement(final Bytes input) {
return gasCalculator().sha256PrecompiledContractGasCost(input);
return gasCalculator().getFrecPrecompiledContractGasCost();
}

@Nonnull
Expand All @@ -55,23 +57,32 @@ public PrecompileContractResult computePrecompile(
final Bytes methodInput, @Nonnull final MessageFrame messageFrame) {
Bytes methodAbi = methodInput.slice(0, METHOD_ABI.size());
if (!methodAbi.xor(METHOD_ABI).isZero()) {
throw new IllegalArgumentException("Unexpected method ABI: " + methodAbi.toHexString());
LOG.trace("Unexpected method ABI: " + methodAbi.toHexString());
return PrecompileContractResult.halt(
null, Optional.of(ExceptionalHaltReason.PRECOMPILE_ERROR));
}
int pubKeyLength;
int pubKeyOffset;
Bytes input = methodInput.slice(METHOD_ABI.size());
int signatureOffset = input.slice(0, 32).trimLeadingZeros().toInt();
int pubKeyOffset = input.slice(32, 32).trimLeadingZeros().toInt();
int dataOffset = input.slice(64, 32).trimLeadingZeros().toInt();
Bytes signatureSlice;
Bytes pubKeySlice;
Bytes dataSlice;
try {
int signatureOffset = input.slice(0, 32).trimLeadingZeros().toInt();
pubKeyOffset = input.slice(32, 32).trimLeadingZeros().toInt();

int signatureLength = input.slice(signatureOffset, 32).trimLeadingZeros().toInt();
int pubKeyLength = input.slice(pubKeyOffset, 32).trimLeadingZeros().toInt();
int dataLength = input.slice(dataOffset, 32).trimLeadingZeros().toInt();
int signatureLength = input.slice(signatureOffset, 32).trimLeadingZeros().toInt();
pubKeyLength = input.slice(pubKeyOffset, 32).trimLeadingZeros().toInt();

Bytes signatureSlice = input.slice(signatureOffset + 32, signatureLength);
Bytes pubKeySlice =
input.slice(
pubKeyOffset + 32 + 1,
pubKeyLength - 1); // BouncyCastle omits the first byte since it is always zero
Bytes dataSlice = input.slice(dataOffset + 32, dataLength);
signatureSlice = input.slice(signatureOffset + 32, signatureLength);
pubKeySlice = input.slice(pubKeyOffset + 32 + 1, pubKeyLength - 1);
int dataLength = 32;
dataSlice = input.slice(64, dataLength);
} catch (Exception e) {
LOG.trace("Error executing Falcon-512 precompiled contract: '{}'", "invalid input");
return PrecompileContractResult.halt(
null, Optional.of(ExceptionalHaltReason.PRECOMPILE_ERROR));
}

if (LOG.isTraceEnabled()) {
LOG.trace(
Expand All @@ -86,13 +97,13 @@ public PrecompileContractResult computePrecompile(
falconSigner.init(false, falconPublicKeyParameters);
final boolean verifies =
falconSigner.verifySignature(dataSlice.toArray(), signatureSlice.toArray());

if (verifies) {
LOG.debug("Signature is VALID");
return PrecompileContractResult.success(Bytes32.leftPad(Bytes.of(0)));
Bytes digest = Hash.shake256(input.slice(pubKeyOffset + 32, pubKeyLength), 32);
return PrecompileContractResult.success(Bytes32.leftPad(digest));
} else {
LOG.debug("Signature is INVALID");
return PrecompileContractResult.success(Bytes32.leftPad(Bytes.of(1)));
return PrecompileContractResult.success(Bytes32.leftPad(Bytes.of(0)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import com.google.common.collect.ImmutableMap;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.bouncycastle.util.encoders.Hex;

public class Benchmarks {

Expand Down Expand Up @@ -613,6 +614,48 @@ private static void benchBLS12MapFP2TOG2() {
(int) gasSpent, contract.gasRequirement(arg));
}

private static void benchFalcon512() {
final FalconPrecompiledContract contract =
new FalconPrecompiledContract(new IstanbulGasCalculator());
byte[] bytesSig =
Hex.decode(
"3933b3c07507e4201748494d832b6ee2a6c93bff9b0ee343b550d1f85a3d0de0d704c6d1784295130960fbe956c6851e141ab3c09a5475bdda2d81ee113f1e16e2ddd8e408bbb3f5b06a555d8f1ac072e5be2f9a365b33ace3d8254709c185b6f3d3db4b50baf14536779f24b5e843fb36b5a00ca475d1801a964d09961c1959dad0f80f0615476c75f58e4ae9a9f8eb93e65f1a4753ca75a62269d74a49ddc75f539bfecd60fe3c8966e77b01d9c69a965a39907862b2de754ad319644d21134fccee194278b326aa4e94359f1d4a08814cad9b27d62687cc31cfcf057490cea0512e02b3b3834e6803c30679f2f2ae2bf4d4e670b283f5105dad272da0f8b95168ea1080b184d98787ba335c22a0a40fd5ba4a70d905c373304c2759e695214f741b98629184789ffdaf63731b43188bda77ed49924da34968ee347b973b525ce0b40af92889b479e5e8717c05c923d76f0bf4689747de1821c1c42968eb164a5fe7c25ef75629511ce220c9d71c4fdb4d60b927b1e473dcda691062b6977338e8666b8c82b8f0f118eefeb5e66e408e63aa9cf876a0a4249688aae132955f5ea98fff2bb4f565b4ec4db39d414dd9c9d3edd089ac74af2f9f3feffa72332982d5e470a74bb2b3b7462639ed89319a327e8a11aef2e3cc391789b4c616b1aef335e9b47a294a47d2c42e7daca30c119e8098b6895aa32bb989a675aa72e84dc1ccc8b9f1a6cd274adbb86fc5ca9c2744cd73f064eba715e06dce542ef0646c94f41d3f47bb7c1a6c4e91ee55996dec4cbbc931f0047e3ba251f71b0bbc7e8633cc7479b1361697a131436d5958ee6fd1ccc1a5d31a9a8391bde2d075cce140de8f1dc58a093310821ae1d26917bc2a17dec09225374ee078d977f901825955ec8623304459a51e5cf7fe60c68b1680"); // signer.generateSignature(shake256Length32Message);
byte[] bytesPublicKey =
Hex.decode(
"096BA86CB658A8F445C9A5E4C28374BEC879C8655F68526923240918074D0147C03162E4A49200648C652803C6FD7509AE9AA799D6310D0BD42724E0635920186207000767CA5A8546B1755308C304B84FC93B069E265985B398D6B834698287FF829AA820F17A7F4226AB21F601EBD7175226BAB256D8888F009032566D6383D68457EA155A94301870D589C678ED304259E9D37B193BC2A7CCBCBEC51D69158C44073AEC9792630253318BC954DBF50D15028290DC2D309C7B7B02A6823744D463DA17749595CB77E6D16D20D1B4C3AAD89D320EBE5A672BB96D6CD5C1EFEC8B811200CBB062E473352540EDDEF8AF9499F8CDD1DC7C6873F0C7A6BCB7097560271F946849B7F373640BB69CA9B518AA380A6EB0A7275EE84E9C221AED88F5BFBAF43A3EDE8E6AA42558104FAF800E018441930376C6F6E751569971F47ADBCA5CA00C801988F317A18722A29298925EA154DBC9024E120524A2D41DC0F18FD8D909F6C50977404E201767078BA9A1F9E40A8B2BA9C01B7DA3A0B73A4C2A6B4F518BBEE3455D0AF2204DDC031C805C72CCB647940B1E6794D859AAEBCEA0DEB581D61B9248BD9697B5CB974A8176E8F910469CAE0AB4ED92D2AEE9F7EB50296DAF8057476305C1189D1D9840A0944F0447FB81E511420E67891B98FA6C257034D5A063437D379177CE8D3FA6EAF12E2DBB7EB8E498481612B1929617DA5FB45E4CDF893927D8BA842AA861D9C50471C6D0C6DF7E2BB26465A0EB6A3A709DE792AAFAAF922AA95DD5920B72B4B8856C6E632860B10F5CC08450003671AF388961872B466400ADB815BA81EA794945D19A100622A6CA0D41C4EA620C21DC125119E372418F04402D9FA7180F7BC89AFA54F8082244A42F46E5B5ABCE87B50A7D6FEBE8D7BBBAC92657CBDA1DB7C25572A4C1D0BAEA30447A865A2B1036B880037E2F4D26D453E9E913259779E9169B28A62EB809A5C744E04E260E1F2BBDA874F1AC674839DDB47B3148C5946DE0180148B7973D63C58193B17CD05D16E80CD7928C2A338363A23A81C0608C87505589B9DA1C617E7B70786B6754FBB30A5816810B9E126CFCC5AA49326E9D842973874B6359B5DB75610BA68A98C7B5E83F125A82522E13B83FB8F864E2A97B73B5D544A7415B6504A13939EAB1595D64FAF41FAB25A864A574DE524405E878339877886D2FC07FA0311508252413EDFA1158466667AFF78386DAF7CB4C9B850992F96E20525330599AB601D454688E294C8C3E");
byte[] messageToSign =
Hex.decode("3d474d06cef2d8da1c97afd7d4993ba792297ca4e161967578494141cab04202");

final int sigLen = bytesSig.length; // ~658;
final int pubKeyLen = bytesPublicKey.length; // 897;

final Bytes signatureLength = Bytes32.leftPad(Bytes.ofUnsignedInt(sigLen));
final Bytes pubKeyLength = Bytes32.leftPad(Bytes.ofUnsignedInt(pubKeyLen));
// 4 bytes method signature | 32 bytes signature offset | 32 bytes public key offset | 32 bytes
// dataOffset |
// signature (len+slice) | public key (len+slice) | data (len+slice)
final int sigOff = 96;
final int pubKeyOff = sigOff + 32 + sigLen;
final Bytes signatureOffset = Bytes32.leftPad(Bytes.ofUnsignedInt(sigOff));
final Bytes publicKeyOffset = Bytes32.leftPad(Bytes.ofUnsignedInt(pubKeyOff));
final Bytes signature = Bytes.concatenate(signatureLength, Bytes.wrap(bytesSig));
final Bytes publicKey = Bytes.concatenate(pubKeyLength, Bytes.wrap(bytesPublicKey));

final Bytes input =
Bytes.concatenate(
Hash.keccak256(Bytes.of("verify(bytes,bytes,bytes32)".getBytes(UTF_8))).slice(0, 4),
signatureOffset,
publicKeyOffset,
Bytes.wrap(messageToSign),
signature,
publicKey);
final double gasSpent = runBenchmark(input, contract);

System.out.printf(
"Falcon-512 for %,d gas. Charging %,d gas.%n",
(int) gasSpent, contract.gasRequirement(input));
}

private static double runBenchmark(final Bytes arg, final PrecompiledContract contract) {
if (contract.computePrecompile(arg, fakeFrame).getOutput() == null) {
throw new RuntimeException("Input is Invalid");
Expand Down Expand Up @@ -650,5 +693,6 @@ public static void main(final String[] args) {
benchBLS12Pair();
benchBLS12MapFPTOG1();
benchBLS12MapFP2TOG2();
benchFalcon512();
}
}
Loading
Loading