diff --git a/core/vm/contracts.go b/core/vm/contracts.go index f5c33eb03..df33698c5 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -22,11 +22,14 @@ import ( "encoding/binary" //Needed for SHA3-256 FIPS202 "encoding/hex" - "errors" "fmt" + "io" "math/big" + "github.com/consensys/gnark/backend/groth16" "github.com/ethereum/go-ethereum/crypto/blake2b" + groth16bn256 "github.com/harmony-one/harmony/crypto/groth16/bn256" + "github.com/pkg/errors" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" @@ -38,8 +41,6 @@ import ( "golang.org/x/crypto/sha3" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bn254/fr" - gnark "github.com/consensys/gnark/backend/groth16" "github.com/consensys/gnark/backend/witness" //gnarkLog "github.com/consensys/gnark/logger" ) @@ -171,83 +172,79 @@ func (g groth16Verify) RequiredGas(input []byte) uint64 { } func (g groth16Verify) Run(input []byte) ([]byte, error) { - //TODO implement me - panic("implement me") + buf := make([]byte, 2) + r := bytes.NewReader(input) + _, err := io.ReadFull(r, buf) + if err != nil { + return nil, errors.WithMessage(err, "failed to read first input") + } + s := binary.BigEndian.Uint16(buf) + verifyingKey := make([]byte, s) + _, err = io.ReadFull(r, verifyingKey) + if err != nil { + return nil, errors.WithMessagef(err, "failed to read data for first input of size %d", s) + } + _, err = io.ReadFull(r, buf) + if err != nil { + return nil, errors.WithMessage(err, "failed to read second input") + } + s = binary.BigEndian.Uint16(buf) + proof := make([]byte, s) + _, err = io.ReadFull(r, proof) + if err != nil { + return nil, errors.WithMessagef(err, "failed to read data for second input of size %d", s) + } + _, err = io.ReadFull(r, buf) + if err != nil { + return nil, errors.WithMessage(err, "failed to read third input") + } + s = binary.BigEndian.Uint16(buf) + inputs := make([]byte, s) + _, err = io.ReadFull(r, inputs) + if err != nil { + return nil, errors.WithMessagef(err, "failed to read data for third input of size %d", s) + } + + Groth16Verify(verifyingKey, proof, inputs, ecc.BN254) } func Groth16Verify(verifyingKey []byte, proofBytes []byte, inputsBytes []byte, curve ecc.ID) (bool, error) { - var vk gnark.VerifyingKey - var proof gnark.Proof + var vk groth16.VerifyingKey + var proof groth16.Proof switch curve { case ecc.BN254: - bn256vk, err := FromBytesToVerifyingKey(verifyingKey) + bn256vk, err := groth16bn256.FromBytesToVerifyingKey(verifyingKey) if err != nil { return false, err } vk = bn256vk - bn256proof, err := bn256.FromBytesToProof(proofBytes) + bn256proof, err := groth16bn256.FromBytesToProof(proofBytes) if err != nil { return false, err } proof = bn256proof default: - return false, errors.Errorf("unknown eliptic curve") + return false, errors.New("unknown eliptic curve") } - var buf bytes.Buffer - buf.Grow(8 + 4 + len(inputsBytes)) - // Add 8 bytes for correct reading - // Gnark witness has two addition number in the start - // These numbers aren't used for verification - buf.Write(make([]byte, 8)) - err := binary.Write(&buf, binary.BigEndian, uint32(len(inputsBytes)/(fr.Limbs*sizeUint64))) - if err != nil { - return false, err - } - buf.Write(inputsBytes) wit, err := witness.New(curve.ScalarField()) if err != nil { return false, err } - err = wit.UnmarshalBinary(buf.Bytes()) + err = wit.UnmarshalBinary(inputsBytes) if err != nil { return false, err } - err = gnark.Verify(proof, vk, wit) + err = groth16.Verify(proof, vk, wit) if err != nil { return false, nil } return true, nil } -func FromBytesToProof(proofBytes []byte) (gnark.Proof, error) { - var bproof BellmanProofBn256 - proofBytes, err := changeFlagsInProofToGnarkType(proofBytes) - if err != nil { - return nil, err - } - _, err = bproof.ReadFrom(bytes.NewReader(proofBytes)) - if err != nil { - return nil, err - } - - var b bytes.Buffer - _, err = bproof.WriteTo(&b) - if err != nil { - return nil, err - } - - proof := gnark.NewProof(ecc.BN254) - _, err = proof.ReadFrom(bytes.NewReader(b.Bytes())) - if err != nil { - return nil, err - } - return proof, nil -} - func init() { // check that there is no overlap, and panic if there is readOnlyContracts := PrecompiledContractsStaking diff --git a/core/vm/evm.go b/core/vm/evm.go index 55f6f8c93..65437ece0 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -87,6 +87,9 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err precompiles = PrecompiledContractsStaking writeCapablePrecompiles = WriteCapablePrecompiledContractsStaking } + if evm.chainRules.IsGroth16Precompile { + precompiles = PrecompiledContractsGroth16 + } if evm.chainRules.IsCrossShardXferPrecompile { writeCapablePrecompiles = WriteCapablePrecompiledContractsCrossXfer } diff --git a/crypto/groth16/bn254/lib.go b/crypto/groth16/bn256/lib.go similarity index 60% rename from crypto/groth16/bn254/lib.go rename to crypto/groth16/bn256/lib.go index f11cff356..6aa51855f 100644 --- a/crypto/groth16/bn254/lib.go +++ b/crypto/groth16/bn256/lib.go @@ -1,4 +1,4 @@ -package bn254 +package bn256 import ( "bytes" @@ -15,3 +15,12 @@ func FromBytesToVerifyingKey(verifyingKey []byte) (gnark.VerifyingKey, error) { } return vk, nil } + +func FromBytesToProof(proofBytes []byte) (gnark.Proof, error) { + proof := gnark.NewProof(ecc.BN254) + _, err := proof.ReadFrom(bytes.NewReader(proofBytes)) + if err != nil { + return nil, err + } + return proof, nil +} diff --git a/internal/params/config.go b/internal/params/config.go index 15ca7d287..becae8df2 100644 --- a/internal/params/config.go +++ b/internal/params/config.go @@ -879,6 +879,7 @@ type Rules struct { // precompiles IsIstanbul, IsVRF, IsPrevVRF, IsSHA3, IsStakingPrecompile, IsCrossShardXferPrecompile, + IsGroth16Precompile, // eip-155 chain id fix IsChainIdFix bool IsValidatorCodeFix bool