UNPKG

@ethereumjs/evm

Version:
262 lines 12.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.NobleBLS = void 0; exports.BLS12_381_FromG1Point = BLS12_381_FromG1Point; exports.BLS12_381_FromG2Point = BLS12_381_FromG2Point; exports.BLS12_381_ToFp2Point = BLS12_381_ToFp2Point; exports.BLS12_381_ToFpPoint = BLS12_381_ToFpPoint; exports.BLS12_381_ToFrPoint = BLS12_381_ToFrPoint; exports.BLS12_381_ToG1Point = BLS12_381_ToG1Point; exports.BLS12_381_ToG2Point = BLS12_381_ToG2Point; const util_1 = require("@ethereumjs/util"); const bls12_381_1 = require("@noble/curves/bls12-381"); const errors_ts_1 = require("../../errors.js"); const constants_ts_1 = require("./constants.js"); const G1_ZERO = bls12_381_1.bls12_381.G1.ProjectivePoint.ZERO; const G2_ZERO = bls12_381_1.bls12_381.G2.ProjectivePoint.ZERO; function BLS12_381_ToFp2Point(fpXCoordinate, fpYCoordinate) { // check if the coordinates are in the field if ((0, util_1.bytesToBigInt)(fpXCoordinate) >= constants_ts_1.BLS_FIELD_MODULUS) { throw new errors_ts_1.EVMError(errors_ts_1.EVMError.errorMessages.BLS_12_381_FP_NOT_IN_FIELD); } if ((0, util_1.bytesToBigInt)(fpYCoordinate) >= constants_ts_1.BLS_FIELD_MODULUS) { throw new errors_ts_1.EVMError(errors_ts_1.EVMError.errorMessages.BLS_12_381_FP_NOT_IN_FIELD); } const fpBytes = (0, util_1.concatBytes)(fpXCoordinate.subarray(16), fpYCoordinate.subarray(16)); const FP = bls12_381_1.bls12_381.fields.Fp2.fromBytes(fpBytes); return FP; } /** * Converts an Uint8Array to a Noble G1 point. Raises errors if the point is not on the curve * and (if activated) if the point is in the subgroup / order check. * @param input Input Uint8Array. Should be 128 bytes * @returns Noble G1 point */ function BLS12_381_ToG1Point(input, verifyOrder = true) { if ((0, util_1.equalsBytes)(input, constants_ts_1.BLS_G1_INFINITY_POINT_BYTES) === true) { return G1_ZERO; } const x = (0, util_1.bytesToBigInt)(input.subarray(16, constants_ts_1.BLS_G1_POINT_BYTE_LENGTH / 2)); const y = (0, util_1.bytesToBigInt)(input.subarray(80, constants_ts_1.BLS_G1_POINT_BYTE_LENGTH)); const G1 = bls12_381_1.bls12_381.G1.ProjectivePoint.fromAffine({ x, y, }); try { G1.assertValidity(); } catch (e) { if (verifyOrder || e.message !== 'bad point: not in prime-order subgroup') throw new errors_ts_1.EVMError(errors_ts_1.EVMError.errorMessages.BLS_12_381_POINT_NOT_ON_CURVE); } return G1; } // input: a Noble G1 point // output: a 128-byte Uint8Array function BLS12_381_FromG1Point(input) { const xBytes = (0, util_1.setLengthLeft)((0, util_1.bigIntToBytes)(input.x), 64); const yBytes = (0, util_1.setLengthLeft)((0, util_1.bigIntToBytes)(input.y), 64); return (0, util_1.concatBytes)(xBytes, yBytes); } /** * Converts an Uint8Array to a Noble G2 point. Raises errors if the point is not on the curve * and (if activated) if the point is in the subgroup / order check. * @param input Input Uint8Array. Should be 256 bytes * @returns Noble G2 point */ function BLS12_381_ToG2Point(input, verifyOrder = true) { if ((0, util_1.equalsBytes)(input, constants_ts_1.BLS_G2_INFINITY_POINT_BYTES) === true) { return G2_ZERO; } const p_x_1 = input.subarray(0, 64); const p_x_2 = input.subarray(64, constants_ts_1.BLS_G2_POINT_BYTE_LENGTH / 2); const p_y_1 = input.subarray(128, 192); const p_y_2 = input.subarray(192, constants_ts_1.BLS_G2_POINT_BYTE_LENGTH); const Fp2X = BLS12_381_ToFp2Point(p_x_1, p_x_2); const Fp2Y = BLS12_381_ToFp2Point(p_y_1, p_y_2); const pG2 = bls12_381_1.bls12_381.G2.ProjectivePoint.fromAffine({ x: Fp2X, y: Fp2Y, }); try { pG2.assertValidity(); } catch (e) { if (verifyOrder || e.message !== 'bad point: not in prime-order subgroup') throw new errors_ts_1.EVMError(errors_ts_1.EVMError.errorMessages.BLS_12_381_POINT_NOT_ON_CURVE); } return pG2; } // input: a Noble G1 point // output: a 128-byte Uint8Array function BLS12_381_FromG2Point(input) { const xBytes1 = (0, util_1.setLengthLeft)((0, util_1.bigIntToBytes)(input.x.c0), 64); const xBytes2 = (0, util_1.setLengthLeft)((0, util_1.bigIntToBytes)(input.x.c1), 64); const yBytes1 = (0, util_1.setLengthLeft)((0, util_1.bigIntToBytes)(input.y.c0), 64); const yBytes2 = (0, util_1.setLengthLeft)((0, util_1.bigIntToBytes)(input.y.c1), 64); return (0, util_1.concatBytes)(xBytes1, xBytes2, yBytes1, yBytes2); } // input: a 32-byte hex scalar Uint8Array // output: a Noble Fr point function BLS12_381_ToFrPoint(input) { const Fr = bls12_381_1.bls12_381.fields.Fr.fromBytes(input); // TODO: This fixes the following two failing tests: // bls_g1mul_random*g1_unnormalized_scalar // bls_g1mul_random*p1_unnormalized_scalar // It should be nevertheless validated if this is (fully) correct, // especially if ">" or ">=" should be applied. // // Unfortunately the scalar in both test vectors is significantly // greater than the ORDER threshold, here are th values from both tests: // // Scalar / Order // 69732848789442042582239751384143889712113271203482973843852656394296700715236n // 52435875175126190479447740508185965837690552500527637822603658699938581184513n // // There should be 4 test cases added to the official test suite: // 1. bls_g1mul_random*g1_unnormalized_scalar within threshold (ORDER (?)) // 2. bls_g1mul_random*g1_unnormalized_scalar outside threshold (ORDER + 1 (?)) // 3. bls_g1mul_random*p1_unnormalized_scalar within threshold (ORDER (?)) // 4. bls_g1mul_random*p1_unnormalized_scalar outside threshold (ORDER + 1 (?)) // return bls12_381_1.bls12_381.fields.Fr.create(Fr); } // input: a 64-byte buffer // output: a Noble Fp point function BLS12_381_ToFpPoint(fpCoordinate) { // check if point is in field if ((0, util_1.bytesToBigInt)(fpCoordinate) >= constants_ts_1.BLS_FIELD_MODULUS) { throw new errors_ts_1.EVMError(errors_ts_1.EVMError.errorMessages.BLS_12_381_FP_NOT_IN_FIELD); } const FP = bls12_381_1.bls12_381.fields.Fp.fromBytes(fpCoordinate.slice(16)); return FP; } /** * Implementation of the `EVMBLSInterface` using the `ethereum-cryptography (`@noble/curves`) * JS library, see https://github.com/ethereum/js-ethereum-cryptography. * * This is the EVM default implementation. */ class NobleBLS { addG1(input) { const p1 = BLS12_381_ToG1Point(input.subarray(0, constants_ts_1.BLS_G1_POINT_BYTE_LENGTH), false); const p2 = BLS12_381_ToG1Point(input.subarray(constants_ts_1.BLS_G1_POINT_BYTE_LENGTH, constants_ts_1.BLS_G1_POINT_BYTE_LENGTH * 2), false); const p = p1.add(p2); const result = BLS12_381_FromG1Point(p); return result; } mulG1(input) { // convert input to G1 points, add them, and convert the output to a Uint8Array. const p = BLS12_381_ToG1Point(input.subarray(0, constants_ts_1.BLS_G1_POINT_BYTE_LENGTH)); const scalar = BLS12_381_ToFrPoint(input.subarray(constants_ts_1.BLS_G1_POINT_BYTE_LENGTH, 160)); if (scalar === util_1.BIGINT_0) { return constants_ts_1.BLS_G1_INFINITY_POINT_BYTES; } const result = p.multiplyUnsafe(scalar); return BLS12_381_FromG1Point(result); } addG2(input) { const p1 = BLS12_381_ToG2Point(input.subarray(0, constants_ts_1.BLS_G2_POINT_BYTE_LENGTH), false); const p2 = BLS12_381_ToG2Point(input.subarray(constants_ts_1.BLS_G2_POINT_BYTE_LENGTH, constants_ts_1.BLS_G2_POINT_BYTE_LENGTH * 2), false); const p = p1.add(p2); const result = BLS12_381_FromG2Point(p); return result; } mulG2(input) { // convert input to G2 point/Fr point, add them, and convert the output to a Uint8Array. const p = BLS12_381_ToG2Point(input.subarray(0, constants_ts_1.BLS_G2_POINT_BYTE_LENGTH)); const scalar = BLS12_381_ToFrPoint(input.subarray(constants_ts_1.BLS_G2_POINT_BYTE_LENGTH, 288)); if (scalar === util_1.BIGINT_0) { return constants_ts_1.BLS_G2_INFINITY_POINT_BYTES; } const result = p.multiplyUnsafe(scalar); return BLS12_381_FromG2Point(result); } mapFPtoG1(input) { // convert input to Fp1 point const FP = BLS12_381_ToFpPoint(input.subarray(0, 64)); const result = bls12_381_1.bls12_381.G1.mapToCurve([FP]).toAffine(); const resultBytes = BLS12_381_FromG1Point(result); return resultBytes; } mapFP2toG2(input) { // convert input to Fp2 point const Fp2Point = BLS12_381_ToFp2Point(input.subarray(0, 64), input.subarray(64, 128)); const result = bls12_381_1.bls12_381.G2.mapToCurve([Fp2Point.c0, Fp2Point.c1]).toAffine(); const resultBytes = BLS12_381_FromG2Point(result); return resultBytes; } msmG1(input) { // Note: This implementation is using the naive "algorithm" of just doing // p1G1*v1F1 + p2G1*v1F1 + ... while the EIP is suggesting to use an optimized // algorithm (Pippenger's algorithm, see https://eips.ethereum.org/EIPS/eip-2537#g1g2-msm). // // While this functionally works the approach is not "gas-cost-competitive" and an // optimization should be considered in the future. const pairLength = 160; const numPairs = input.length / pairLength; let pRes = G1_ZERO; for (let k = 0; k < numPairs; k++) { const pairStart = pairLength * k; const G1 = BLS12_381_ToG1Point(input.subarray(pairStart, pairStart + constants_ts_1.BLS_G1_POINT_BYTE_LENGTH)); const Fr = BLS12_381_ToFrPoint(input.subarray(pairStart + constants_ts_1.BLS_G1_POINT_BYTE_LENGTH, pairStart + pairLength)); let pMul; if (Fr === util_1.BIGINT_0) { pMul = G1_ZERO; } else { pMul = G1.multiplyUnsafe(Fr); } pRes = pRes.add(pMul); } return BLS12_381_FromG1Point(pRes); } msmG2(input) { // Note: This implementation is using the naive "algorithm" of just doing // p1G1*v1F1 + p2G1*v1F1 + ... while the EIP is suggesting to use an optimized // algorithm (Pippenger's algorithm, see https://eips.ethereum.org/EIPS/eip-2537#g1g2-msm). // // While this functionally works the approach is not "gas-cost-competitive" and an // optimization should be considered in the future. const pairLength = 288; const numPairs = input.length / pairLength; let pRes = G2_ZERO; for (let k = 0; k < numPairs; k++) { const pairStart = pairLength * k; const G2 = BLS12_381_ToG2Point(input.subarray(pairStart, pairStart + constants_ts_1.BLS_G2_POINT_BYTE_LENGTH)); const Fr = BLS12_381_ToFrPoint(input.subarray(pairStart + constants_ts_1.BLS_G2_POINT_BYTE_LENGTH, pairStart + pairLength)); let pMul; if (Fr === util_1.BIGINT_0) { pMul = G2_ZERO; } else { pMul = G2.multiplyUnsafe(Fr); } pRes = pRes.add(pMul); } return BLS12_381_FromG2Point(pRes); } pairingCheck(input) { // Extract the pairs from the input const pairLength = 384; const pairs = []; for (let k = 0; k < input.length / pairLength; k++) { const pairStart = pairLength * k; const G1 = BLS12_381_ToG1Point(input.subarray(pairStart, pairStart + constants_ts_1.BLS_G1_POINT_BYTE_LENGTH)); const g2start = pairStart + constants_ts_1.BLS_G1_POINT_BYTE_LENGTH; const G2 = BLS12_381_ToG2Point(input.subarray(g2start, g2start + constants_ts_1.BLS_G2_POINT_BYTE_LENGTH)); pairs.push({ g1: G1, g2: G2 }); } // Filter out infinity pairs const filteredPairs = pairs.filter((pair) => !pair.g1.equals(G1_ZERO) && !pair.g2.equals(G2_ZERO)); const FP12 = bls12_381_1.bls12_381.pairingBatch(filteredPairs, true); if (bls12_381_1.bls12_381.fields.Fp12.eql(FP12, bls12_381_1.bls12_381.fields.Fp12.ONE)) { return constants_ts_1.BLS_ONE_BUFFER; } else { return constants_ts_1.BLS_ZERO_BUFFER; } } } exports.NobleBLS = NobleBLS; //# sourceMappingURL=noble.js.map