Skip to content

Commit

Permalink
Eip-2537 - BLS12-381 precompiles (#964)
Browse files Browse the repository at this point in the history
Implement BLS12-381 precompiles via the MatterLabs library.

Signed-off-by: Danno Ferrin <danno.ferrin@gmail.com>
  • Loading branch information
shemnon committed May 22, 2020
1 parent 73074e6 commit 29e668a
Show file tree
Hide file tree
Showing 36 changed files with 2,390 additions and 27 deletions.
1 change: 1 addition & 0 deletions ethereum/core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ dependencies {
implementation 'org.apache.tuweni:tuweni-bytes'
implementation 'org.apache.tuweni:tuweni-units'
implementation 'org.hyperledger.besu:altbn128'
implementation 'org.hyperledger.besu:bls12-381'

runtimeOnly 'org.apache.logging.log4j:log4j-core'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,25 @@ public class Address extends DelegatingBytes implements org.hyperledger.besu.plu
public static final int SIZE = 20;

/** Specific addresses of the "precompiled" contracts. */
public static final Address ECREC = Address.precompiled(1);

public static final Address SHA256 = Address.precompiled(2);
public static final Address RIPEMD160 = Address.precompiled(3);
public static final Address ID = Address.precompiled(4);
public static final Address MODEXP = Address.precompiled(5);
public static final Address ALTBN128_ADD = Address.precompiled(6);
public static final Address ALTBN128_MUL = Address.precompiled(7);
public static final Address ALTBN128_PAIRING = Address.precompiled(8);
public static final Address BLAKE2B_F_COMPRESSION = Address.precompiled(9);
public static final Address ECREC = Address.precompiled(0x01);

public static final Address SHA256 = Address.precompiled(0x02);
public static final Address RIPEMD160 = Address.precompiled(0x03);
public static final Address ID = Address.precompiled(0x04);
public static final Address MODEXP = Address.precompiled(0x05);
public static final Address ALTBN128_ADD = Address.precompiled(0x06);
public static final Address ALTBN128_MUL = Address.precompiled(0x07);
public static final Address ALTBN128_PAIRING = Address.precompiled(0x08);
public static final Address BLAKE2B_F_COMPRESSION = Address.precompiled(0x09);
public static final Address BLS12_G1ADD = Address.precompiled(0xA);
public static final Address BLS12_G1MUL = Address.precompiled(0xB);
public static final Address BLS12_G1MULTIEXP = Address.precompiled(0xC);
public static final Address BLS12_G2ADD = Address.precompiled(0xD);
public static final Address BLS12_G2MUL = Address.precompiled(0xE);
public static final Address BLS12_G2MULTIEXP = Address.precompiled(0xF);
public static final Address BLS12_PAIRING = Address.precompiled(0x10);
public static final Address BLS12_MAP_FP_TO_G1 = Address.precompiled(0x11);
public static final Address BLS12_MAP_FP2_TO_G2 = Address.precompiled(0x12);

// Last address that can be generated for a pre-compiled contract
public static final Integer PRIVACY = Byte.MAX_VALUE - 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@
import org.hyperledger.besu.ethereum.mainnet.precompiles.AltBN128MulPrecompiledContract;
import org.hyperledger.besu.ethereum.mainnet.precompiles.AltBN128PairingPrecompiledContract;
import org.hyperledger.besu.ethereum.mainnet.precompiles.BLAKE2BFPrecompileContract;
import org.hyperledger.besu.ethereum.mainnet.precompiles.BLS12G1AddPrecompiledContract;
import org.hyperledger.besu.ethereum.mainnet.precompiles.BLS12G1MulPrecompiledContract;
import org.hyperledger.besu.ethereum.mainnet.precompiles.BLS12G1MultiExpPrecompiledContract;
import org.hyperledger.besu.ethereum.mainnet.precompiles.BLS12G2AddPrecompiledContract;
import org.hyperledger.besu.ethereum.mainnet.precompiles.BLS12G2MulPrecompiledContract;
import org.hyperledger.besu.ethereum.mainnet.precompiles.BLS12G2MultiExpPrecompiledContract;
import org.hyperledger.besu.ethereum.mainnet.precompiles.BLS12MapFp2ToG2PrecompiledContract;
import org.hyperledger.besu.ethereum.mainnet.precompiles.BLS12MapFpToG1PrecompiledContract;
import org.hyperledger.besu.ethereum.mainnet.precompiles.BLS12PairingPrecompiledContract;
import org.hyperledger.besu.ethereum.mainnet.precompiles.BigIntegerModularExponentiationPrecompiledContract;
import org.hyperledger.besu.ethereum.mainnet.precompiles.ECRECPrecompiledContract;
import org.hyperledger.besu.ethereum.mainnet.precompiles.IDPrecompiledContract;
Expand Down Expand Up @@ -84,35 +93,75 @@ public static PrecompileContractRegistry byzantium(
return registry;
}

public static PrecompileContractRegistry istanbul(
final PrecompiledContractConfiguration precompiledContractConfiguration) {
final PrecompileContractRegistry registry = new PrecompileContractRegistry();
populateForByzantium(
registry, precompiledContractConfiguration.getGasCalculator(), Account.DEFAULT_VERSION);
private static void populateForIstanbul(
final PrecompileContractRegistry registry,
final GasCalculator gasCalculator,
final int accountVersion) {
populateForByzantium(registry, gasCalculator, accountVersion);
registry.put(
Address.ALTBN128_ADD,
Account.DEFAULT_VERSION,
AltBN128AddPrecompiledContract.istanbul(
precompiledContractConfiguration.getGasCalculator()));
AltBN128AddPrecompiledContract.istanbul(gasCalculator));
registry.put(
Address.ALTBN128_MUL,
Account.DEFAULT_VERSION,
AltBN128MulPrecompiledContract.istanbul(
precompiledContractConfiguration.getGasCalculator()));
AltBN128MulPrecompiledContract.istanbul(gasCalculator));
registry.put(
Address.ALTBN128_PAIRING,
Account.DEFAULT_VERSION,
AltBN128PairingPrecompiledContract.istanbul(
precompiledContractConfiguration.getGasCalculator()));
AltBN128PairingPrecompiledContract.istanbul(gasCalculator));
registry.put(
Address.BLAKE2B_F_COMPRESSION,
Account.DEFAULT_VERSION,
new BLAKE2BFPrecompileContract(precompiledContractConfiguration.getGasCalculator()));
new BLAKE2BFPrecompileContract(gasCalculator));
}

public static PrecompileContractRegistry istanbul(
final PrecompiledContractConfiguration precompiledContractConfiguration) {
final PrecompileContractRegistry registry = new PrecompileContractRegistry();
populateForIstanbul(
registry, precompiledContractConfiguration.getGasCalculator(), Account.DEFAULT_VERSION);
return registry;
}

private static void populateForBerlin(
final PrecompileContractRegistry registry,
final GasCalculator gasCalculator,
final int accountVersion) {
populateForIstanbul(registry, gasCalculator, accountVersion);
registry.put(Address.BLS12_G1ADD, Account.DEFAULT_VERSION, new BLS12G1AddPrecompiledContract());
registry.put(Address.BLS12_G1MUL, Account.DEFAULT_VERSION, new BLS12G1MulPrecompiledContract());
registry.put(
Address.BLS12_G1MULTIEXP,
Account.DEFAULT_VERSION,
new BLS12G1MultiExpPrecompiledContract());
registry.put(Address.BLS12_G2ADD, Account.DEFAULT_VERSION, new BLS12G2AddPrecompiledContract());
registry.put(Address.BLS12_G2MUL, Account.DEFAULT_VERSION, new BLS12G2MulPrecompiledContract());
registry.put(
Address.BLS12_G2MULTIEXP,
Account.DEFAULT_VERSION,
new BLS12G2MultiExpPrecompiledContract());
registry.put(
Address.BLS12_PAIRING, Account.DEFAULT_VERSION, new BLS12PairingPrecompiledContract());
registry.put(
Address.BLS12_MAP_FP_TO_G1,
Account.DEFAULT_VERSION,
new BLS12MapFpToG1PrecompiledContract());
registry.put(
Address.BLS12_MAP_FP2_TO_G2,
Account.DEFAULT_VERSION,
new BLS12MapFp2ToG2PrecompiledContract());
}

public static PrecompileContractRegistry berlin(
final PrecompiledContractConfiguration precompiledContractConfiguration) {
final PrecompileContractRegistry registry = new PrecompileContractRegistry();
populateForBerlin(
registry, precompiledContractConfiguration.getGasCalculator(), Account.DEFAULT_VERSION);
return registry;
}

static PrecompileContractRegistry appendPrivacy(
static void appendPrivacy(
final PrecompileContractRegistry registry,
final PrecompiledContractConfiguration precompiledContractConfiguration,
final int accountVersion) {
Expand All @@ -131,6 +180,5 @@ static PrecompileContractRegistry appendPrivacy(
new OnChainPrivacyPrecompiledContract(
precompiledContractConfiguration.getGasCalculator(),
precompiledContractConfiguration.getPrivacyParameters()));
return registry;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ static ProtocolSpecBuilder<Void> berlinDefinition(
.evmBuilder(
gasCalculator ->
MainnetEvmRegistries.berlin(gasCalculator, chainId.orElse(BigInteger.ZERO)))
.precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::berlin)
.name("Berlin");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* 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
*
*/
package org.hyperledger.besu.ethereum.mainnet.precompiles;

import static java.nio.charset.StandardCharsets.UTF_8;

import org.hyperledger.besu.ethereum.mainnet.PrecompiledContract;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import org.hyperledger.besu.nativelib.bls12_381.LibEthPairings;

import com.sun.jna.ptr.IntByReference;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tuweni.bytes.Bytes;

public abstract class AbstractBLS12PrecompiledContract implements PrecompiledContract {

private static final Logger LOG = LogManager.getLogger();

static final int[] DISCOUNT_TABLE =
new int[] {
-1, 1_200, 888, 764, 641, 594, 547, 500, 453, 438, 423, 408, 394, 379, 364, 349,
334, 330, 326, 322, 318, 314, 310, 306, 302, 298, 294, 289, 285, 281, 277, 273,
269, 268, 266, 265, 263, 262, 260, 259, 257, 256, 254, 253, 251, 250, 248, 247,
245, 244, 242, 241, 239, 238, 236, 235, 233, 232, 231, 229, 228, 226, 225, 223,
222, 221, 220, 219, 219, 218, 217, 216, 216, 215, 214, 213, 213, 212, 211, 211,
210, 209, 208, 208, 207, 206, 205, 205, 204, 203, 202, 202, 201, 200, 199, 199,
198, 197, 196, 196, 195, 194, 193, 193, 192, 191, 191, 190, 189, 188, 188, 187,
186, 185, 185, 184, 183, 182, 182, 181, 180, 179, 179, 178, 177, 176, 176, 175,
174
};

private final String name;
private final byte operationId;

AbstractBLS12PrecompiledContract(final String name, final byte operationId) {
this.name = name;
this.operationId = operationId;
}

@Override
public String getName() {
return name;
}

@Override
public Bytes compute(final Bytes input, final MessageFrame messageFrame) {
final byte[] result = new byte[LibEthPairings.EIP2537_PREALLOCATE_FOR_RESULT_BYTES];
final byte[] error = new byte[LibEthPairings.EIP2537_PREALLOCATE_FOR_ERROR_BYTES];

final IntByReference o_len =
new IntByReference(LibEthPairings.EIP2537_PREALLOCATE_FOR_RESULT_BYTES);
final IntByReference err_len =
new IntByReference(LibEthPairings.EIP2537_PREALLOCATE_FOR_ERROR_BYTES);
final int errorNo =
LibEthPairings.eip2537_perform_operation(
operationId, input.toArrayUnsafe(), input.size(), result, o_len, error, err_len);
if (errorNo == 0) {
return Bytes.wrap(result, 0, o_len.getValue());
} else {
messageFrame.setRevertReason(Bytes.wrap(error, 0, err_len.getValue()));
LOG.trace(
"Error executing precompiled contract {}: '{}'",
name,
new String(error, 0, err_len.getValue(), UTF_8));
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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
*
*/
package org.hyperledger.besu.ethereum.mainnet.precompiles;

import org.hyperledger.besu.ethereum.core.Gas;
import org.hyperledger.besu.nativelib.bls12_381.LibEthPairings;

import org.apache.tuweni.bytes.Bytes;

public class BLS12G1AddPrecompiledContract extends AbstractBLS12PrecompiledContract {

public BLS12G1AddPrecompiledContract() {
super("BLS12_G1ADD", LibEthPairings.BLS12_G1ADD_OPERATION_RAW_VALUE);
}

@Override
public Gas gasRequirement(final Bytes input) {
return Gas.of(600);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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
*
*/
package org.hyperledger.besu.ethereum.mainnet.precompiles;

import org.hyperledger.besu.ethereum.core.Gas;
import org.hyperledger.besu.nativelib.bls12_381.LibEthPairings;

import org.apache.tuweni.bytes.Bytes;

public class BLS12G1MulPrecompiledContract extends AbstractBLS12PrecompiledContract {

public BLS12G1MulPrecompiledContract() {
super("BLS12_G1MUL", LibEthPairings.BLS12_G1MUL_OPERATION_RAW_VALUE);
}

@Override
public Gas gasRequirement(final Bytes input) {
return Gas.of(12_000);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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
*
*/
package org.hyperledger.besu.ethereum.mainnet.precompiles;

import org.hyperledger.besu.ethereum.core.Gas;
import org.hyperledger.besu.nativelib.bls12_381.LibEthPairings;

import org.apache.tuweni.bytes.Bytes;

public class BLS12G1MultiExpPrecompiledContract extends AbstractBLS12PrecompiledContract {

public BLS12G1MultiExpPrecompiledContract() {
super("BLS12_G1MULTIEXP", LibEthPairings.BLS12_G1MULTIEXP_OPERATION_RAW_VALUE);
}

@Override
public Gas gasRequirement(final Bytes input) {
final int k = input.size() / 160;
// `k * multiplication_cost * discount / multiplier` where `multiplier = 1000`
// multiplication_cost and multiplier are folded into one constant as a long and placed first to
// prevent int32 overflow
return Gas.of(12L * k * DISCOUNT_TABLE[k]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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
*
*/
package org.hyperledger.besu.ethereum.mainnet.precompiles;

import org.hyperledger.besu.ethereum.core.Gas;
import org.hyperledger.besu.nativelib.bls12_381.LibEthPairings;

import org.apache.tuweni.bytes.Bytes;

public class BLS12G2AddPrecompiledContract extends AbstractBLS12PrecompiledContract {

public BLS12G2AddPrecompiledContract() {
super("BLS12_G2ADD", LibEthPairings.BLS12_G2ADD_OPERATION_RAW_VALUE);
}

@Override
public Gas gasRequirement(final Bytes input) {
return Gas.of(4_500);
}
}
Loading

0 comments on commit 29e668a

Please sign in to comment.