lambdaclass / zksync_era_precompiles

Yul precompile library to speedup elliptic curves operations.
Apache License 2.0
51 stars 19 forks source link

Deploying precompiles on zksync #221

Closed ghost closed 7 months ago

ghost commented 8 months ago

Hello,

We are currently making a use of zkSNARK Pairing library and would like to use the precompiles prior of deployment to zksync.

https://gist.github.com/chriseth/f9be9d9391efc5beb9704255a8e2989d

@ilitteri Would it be possible to deploy them now?

Thanks.

ilitteri commented 8 months ago

Hi! Every precompile that is in main but ModExp is working correctly as precompiles, if you want to deploy them as independent contract you need to add a return(0, 0) to their constructor (we're pushing a PR with this fix today though).

ghost commented 8 months ago

@ilitteri Recently spent a lot of time compiling and deploying contracts to zksync so would be there any convenient ways to deploy yul codes into zksync?

ilitteri commented 8 months ago

There should not be any troubles, let me know if there's any though. We have an example of deploying one in testnet in #203

ghost commented 8 months ago

@ilitteri The static calls and outputs would work exactly as the precompiled ones right?

ilitteri commented 8 months ago

Yep, again, let me know if they don't. One thing it take into account (I don't think this would be your case though), if you use our era-test-node, be aware that the output is modified in our favor (the gas used is at the starting of the output).

ghost commented 8 months ago

@ilitteri Got this strange error while deploying EcPairing.yul, I have added return(0, 0) to the constructor like you told me.

  code: 'CALL_EXCEPTION',
  action: 'estimateGas',
  data: null,
  reason: null,
  transaction: {
    to: '0x0000000000000000000000000000000000008006',
    data: '0x9c4d535b00000000000000000000000000000000000000000000000000000000000000000100309f6271bc3cceacd8564e1cd00d5ac78a9294d283b886813bdf14dd200500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
    from: '0xfDbA3B7Db2253cD099A479d11230057389cCBa0D'
  },
  invocation: null,
  revert: null,
  shortMessage: 'missing revert data',
  info: {
    error: {
      code: 3,
      message: 'exceeds limit for published pubdata',
      data: '0x'
    },
    payload: {
      method: 'eth_estimateGas',
      params: [
        {
          type: '0x71',
          nonce: '0x395',
          from: '0xfdba3b7db2253cd099a479d11230057389ccba0d',
          to: '0x0000000000000000000000000000000000008006',
          data: '0x9c4d535b00000000000000000000000000000000000000000000000000000000000000000100309f6271bc3cceacd8564e1cd00d5ac78a9294d283b886813bdf14dd200500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
          eip712Meta: { gasPerPubdata: '0xc350', factoryDeps: [Array] }
        }
      ],
      id: 82,
      jsonrpc: '2.0'
    }
  }
}

Using default deployment arguments

const {
  ethers,
  zksyncEthers,
} = require('hardhat');
const {
  loadArtifact,
  deployContract,
} = zksyncEthers;

const EcAdd = await deployContract(await loadArtifact('EcAdd'), [], owner);
  await EcAdd.waitForDeployment();

  const EcMul = await deployContract(await loadArtifact('EcMul'), [], owner);
  await EcMul.waitForDeployment();

  const EcPairing = await deployContract(await loadArtifact('EcPairing'), [], owner);
  await EcPairing.waitForDeployment();

  console.log({
    EcAdd: EcAdd.target,
    EcMul: EcMul.target,
    EcPairing: EcPairing.target,
  });

Here is the compiled artifact from zkSolc, I have modified from yours

https://gist.github.com/zerotwodao/d24de3d22ca1c779def4a3c01fdf9f52

ghost commented 8 months ago

@ilitteri Seems like a compiled bytecode size for EcPairing.yul is too big.

Finds out the compiled bytecode for EcPairing exceeds 200kb and the known limit for bytecode size limit is 24kb, would it be impossible to deploy?

ilitteri commented 8 months ago

@ilitteri Seems like a compiled bytecode size for EcPairing.yul is too big.

Finds out the compiled bytecode for EcPairing exceeds 200kb and the known limit for bytecode size limit is 24kb, would it be impossible to deploy?

Hm, that's bad news. I'm not sure if it'd be possible. Are you trying to deploy on testnet or in a local-net?

ilitteri commented 8 months ago

Something that could work (don't know the amount of work it'd involve) is to modularize the precompile (e.g. finite field arithmetics could be part of another library, also the curve arithmetics could be part of another library).

ghost commented 7 months ago

@ilitteri Tried to deploy on testnet, and it seems like it is impossible unless we lift off the https://eips.ethereum.org/EIPS/eip-170 size limit, or like you said we could try modularizing the precompile.

ghost commented 7 months ago

Do you think it would be possible to request zksync team for the deployment of those precompiles on the different addresses for the testing purpose on mainnet and testnet? It would make our contract work eventually

ilitteri commented 7 months ago

Have you tried to compile it with different compilation flags?

ghost commented 7 months ago

@ilitteri Which flags are available?

ilitteri commented 7 months ago

@ilitteri Which flags are available?

If you run zksolc --help you can find this:

-O, --optimization <optimization> Set the optimization parameter -O[0 | 1 | 2 | 3 | s | z]. Use `3` for best performance and `z` for minimal size

In your case I'd use -Oz flag.

ghost commented 7 months ago

@ilitteri Sorry for late reply I have successfully compiled and deployed the precompiles with ~40kb using the flag -oZ ( I don't know why I have used -O3 before ) . After the deployment I have also checked that the precompiles work with the example verifier contract that I have posted the above. Do you know when it will be live on the mainnet?

ghost commented 7 months ago

And seems like it is gas costy than the one available from ethereum mainnet would the gas cost for calling function can be decreased if they are deployed as a precompile?

ilitteri commented 7 months ago

@ilitteri Sorry for late reply I have successfully compiled and deployed the precompiles with ~40kb using the flag -oZ ( I don't know why I have used -O3 before ) . After the deployment I have also checked that the precompiles work with the example verifier contract that I have posted the above.

Amazing! Good luck then with your library.

Do you know when it will be live on the mainnet?

I have no answer for this. The zkSync team is reviewing if it'd be better to have them as circuits instead of as Yul contracts. For the moment, using them as contracts in testnet.

ilitteri commented 7 months ago

Closing the issue as it seems to be solved as said here.

chervyachok commented 6 months ago

@ilitteri Sorry for late reply I have successfully compiled and deployed the precompiles with ~40kb using the flag -oZ ( I don't know why I have used -O3 before ) . After the deployment I have also checked that the precompiles work with the example verifier contract that I have posted the above. Do you know when it will be live on the mainnet?

Could you please share deployment address in zkSync main or testnet

ghost commented 6 months ago

@chervyachok Sure

EcAdd: '0xbF3F280F177Befe658A6973E0E57c38d62d1bCc8'

EcMul: '0x8277B56C128e62356306C5a702E1fC2c3dB7192F'

EcPairing: '0x49a31d9776B80450d7E5D514D9a753C99D800E9d'

chervyachok commented 6 months ago

@zerotwodao I replaced addresses 6, 7, 8 in Pairing contract with your contracts and trying to verify proof in zkSync sepolia. Now staticcall to ecPairing works and not revert with panic as was in case of 8 address. But out[0] always 0 and now revert proof validation at next step.

assembly {
    success := staticcall(
        sub(gas(), 2000),
        0x49a31d9776B80450d7E5D514D9a753C99D800E9d, //8, ecPairing,
        add(input, 0x20),
        mul(inputSize, 0x20),
        out,
        0x20
    )
    // Use "invalid" to make gas estimation work
    switch success
    case 0 {
        invalid()
    }
}
require(success, "pairing-opcode-failed");      

console.log('out[0]', out[0]); // always 0

However same code (with address 8) works as expected in normal evm, and out[0] is 1

Did you managed to make it work ?

ghost commented 6 months ago

@chervyachok Have you tested with the ethereum testnets for your code? With my case with the gist posted above it works as intended