o1-labs / o1js

TypeScript framework for zk-SNARKs and zkApps
https://docs.minaprotocol.com/en/zkapps/how-to-write-a-zkapp
Apache License 2.0
495 stars 108 forks source link

zkApp.fetchEvents() throws Gateway Time-out error on devnet #1777

Open dfstio opened 1 month ago

dfstio commented 1 month ago

On mainnet, both zkApp.fetchEvents() and fetchEvents({ publicKey: contractAddress }) work. On devnet, fetchEvents({ publicKey: contractAddress }) work, while zkApp.fetchEvents() throws Gateway Time-out error.

The code to reproduce:

import { NameContractV2 } from "minanft";
import { PublicKey, Mina, fetchEvents } from "o1js";

const contractAddress =
  "B62qs2NthDuxAT94tTFg6MtuaP1gaBxTZyNv9D3uQiQciy1VsaimNFT";

const Devnet1 = {
  mina: ["https://api.minascan.io/node/devnet/v1/graphql"],
  archive: ["https://api.minascan.io/archive/devnet/v1/graphql"],
};

describe("Should get events", () => {
  it(`should get events`, async () => {
    const networkInstance = Mina.Network({
      mina: "https://api.minascan.io/node/devnet/v1/graphql",
      archive: "https://api.minascan.io/archive/devnet/v1/graphql",
    });
    Mina.setActiveInstance(networkInstance);

    const address = PublicKey.fromBase58(contractAddress);
    const zkApp = new NameContractV2(address);
    console.log("Fetching events for", address.toBase58());
    try {
      const events1 = await fetchEvents({ publicKey: contractAddress });
      console.log("Events1:", events1?.length);
      const events2 = await zkApp.fetchEvents();
      console.log("Events2:", events2?.length);
    } catch (e) {
      console.error("Error fetching events", e);
    }
  });
});

The log:

[2:38:49 PM] Fetching events for B62qs2NthDuxAT94tTFg6MtuaP1gaBxTZyNv9D3uQiQciy1VsaimNFT
[2:38:50 PM] Events1: 145
Error fetching events Error: Gateway Time-out
    at fetchEvents2 (/Users/mike/Documents/DeFi/MinaNFT/minanft-tools/node_modules/o1js/dist/node/index.cjs:22299:11)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at fetchEvents (/Users/mike/Documents/DeFi/MinaNFT/minanft-tools/node_modules/o1js/dist/node/index.cjs:19878:10)
    at NameContractV2.fetchEvents (/Users/mike/Documents/DeFi/MinaNFT/minanft-tools/node_modules/o1js/dist/node/index.cjs:24751:19)
    at Object.<anonymous> (/Users/mike/Documents/DeFi/MinaNFT/minanft-tools/tests/issue.test.ts:26:23)
dfstio commented 1 month ago

The issue seems to be related to the argument start that zkApp.fetchEvents() use inside o1js code: https://github.com/o1-labs/o1js/blob/main/src/lib/mina/zkapp.ts#L1041

The updated example:

import { NameContractV2 } from "minanft";
import { PublicKey, Mina, fetchEvents, Field, UInt32 } from "o1js";

const contractAddress =
  "B62qs2NthDuxAT94tTFg6MtuaP1gaBxTZyNv9D3uQiQciy1VsaimNFT";

describe("Should get events", () => {
  it(`should get events`, async () => {
    const networkInstance = Mina.Network({
      mina: "https://api.minascan.io/node/devnet/v1/graphql",
      archive: "https://api.minascan.io/archive/devnet/v1/graphql",
    });
    Mina.setActiveInstance(networkInstance);

    const address = PublicKey.fromBase58(contractAddress);
    const zkApp = new NameContractV2(address);
    console.log("Fetching events for", address.toBase58());
    try {
      const events1 = await fetchEvents({ publicKey: contractAddress });
      console.log("Events1:", events1?.length);
      const events2 = await Mina.fetchEvents(address, Field(1));
      console.log("Events2:", events2?.length);
      const events3 = await Mina.fetchEvents(address, Field(1), {
        from: UInt32.from(0),
      });
      console.log("Events3:", events3?.length);
      const events4 = await zkApp.fetchEvents();
      console.log("Events4:", events2?.length);
    } catch (e) {
      console.error("Error fetching events", e);
    }
  });
});

The log:

[3:29:47 PM] Fetching events for B62qs2NthDuxAT94tTFg6MtuaP1gaBxTZyNv9D3uQiQciy1VsaimNFT
[3:29:48 PM] Events1: 145
[3:29:49 PM] Events2: 145
Error fetching events Error: Gateway Time-out
    at fetchEvents2 (/Users/mike/Documents/DeFi/MinaNFT/minanft-tools/node_modules/o1js/dist/node/index.cjs:22299:11)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at Object.fetchEvents (/Users/mike/Documents/DeFi/MinaNFT/minanft-tools/node_modules/o1js/dist/node/index.cjs:19878:10)
    at Object.<anonymous> (/Users/mike/Documents/DeFi/MinaNFT/minanft-tools/tests/issue1.test.ts:23:23)
dfstio commented 1 month ago

It can be related to https://github.com/o1-labs/o1js/issues/1426

dfstio commented 1 month ago

I've found a workaround using the method described in https://github.com/o1-labs/o1js/issues/1775 Replacing

const events4 = await zkApp.fetchEvents();

with

const events4 = await zkApp.fetchEvents(UInt32.from(321138));

makes this code work

45930 commented 3 weeks ago

@dfstio, can you share your project config for running this test? I ran npm i minafnt in a new zkapp-cli project and tried to run your example code, but I got this error:

SyntaxError: Unexpected token 'export'

      at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1796:14)
      at Object.<anonymous> (src/common/transactions.ts:16:1)
      at Object.<anonymous> (src/common/common.ts:6:1)
      at Object.<anonymous> (src/node/index.ts:1:1)
      at Object.<anonymous> (node_modules/minanft/lib/ts/src/storage/arweave.js:7:35)
      at Object.<anonymous> (node_modules/minanft/lib/ts/src/minanft.js:20:19)
      at Object.<anonymous> (node_modules/minanft/lib/ts/src/index.js:22:14)

If you could share your ts config and package json, or just link me to the repo so I can play around, I'd appreciate it!

45930 commented 3 weeks ago

I am able to run the file by converting it from a test to a simple script, but I am getting a different error before I reach the timeout:

Fetching events for B62qs2NthDuxAT94tTFg6MtuaP1gaBxTZyNv9D3uQiQciy1VsaimNFT
Events1: 167
Error fetching events Error: Must call Mina.setActiveInstance first
    at Object.noActiveInstance (/Users/coby/Projects/scratch_projects/o1js-1777/node_modules/o1js/dist/node/index.cjs:20309:9)
    at fetchEvents (/Users/coby/Projects/scratch_projects/o1js-1777/node_modules/o1js/dist/node/index.cjs:20333:31)
    at NameContractV2.fetchEvents (/Users/coby/Projects/scratch_projects/o1js-1777/node_modules/o1js/dist/node/index.cjs:25357:25)
    at main (file:///Users/coby/Projects/scratch_projects/o1js-1777/build/src/repro.js:20:37)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async file:///Users/coby/Projects/scratch_projects/o1js-1777/build/src/repro.js:27:1

Of course, I've kept the line: Mina.setActiveInstance(networkInstance);.

45930 commented 3 weeks ago

Ok, I am able to reproduce this issue by removing the minanft library dependency. I tried normalizing all o1js versions to 1.4.0, but I continued to have issues from multiple versions of the library.

Simplified reproduction script:

import { PublicKey, Mina, fetchEvents, SmartContract, state, Field, State } from "o1js";

class NFTContractV2 extends SmartContract { 
  @state(Field) name = State<Field>();
}

const contractAddress =
"B62qs2NthDuxAT94tTFg6MtuaP1gaBxTZyNv9D3uQiQciy1VsaimNFT";

const Devnet1 = {
  mina: ["https://api.minascan.io/node/devnet/v1/graphql"],
  archive: ["https://api.minascan.io/archive/devnet/v1/graphql"],
};

const networkInstance = Mina.Network({
    mina: "https://api.minascan.io/node/devnet/v1/graphql",
    archive: "https://api.minascan.io/archive/devnet/v1/graphql",
});
Mina.setActiveInstance(networkInstance);

const address = PublicKey.fromBase58(contractAddress);
const zkApp = new NFTContractV2(address);

console.log("Fetching events for", address.toBase58());
try {
    const events1 = await fetchEvents({ publicKey: contractAddress });
    console.log("Events1:", events1?.length);
    const events2 = await zkApp.fetchEvents();
    console.log("Events2:", events2?.length);
} catch (e) {
    console.error("Error fetching events", e);
}
45930 commented 3 weeks ago

Digging deeper into the implementation, this seems to be a difference in default parameters between fetch.fetchEvents and zkapp.fetchEvents, where the latter adds extra parameters.

The difference in final graphQL query looks like this:

Fetching events for B62qs2NthDuxAT94tTFg6MtuaP1gaBxTZyNv9D3uQiQciy1VsaimNFT
Filter options:  {}
Query:  events(input: { address: "B62qs2NthDuxAT94tTFg6MtuaP1gaBxTZyNv9D3uQiQciy1VsaimNFT", tokenId: "wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf" }) {
Events1: 165
Filter options:  { from: UInt32 { value: Field { value: [Array] } }, to: undefined }
Query:  events(input: { address: "B62qs2NthDuxAT94tTFg6MtuaP1gaBxTZyNv9D3uQiQciy1VsaimNFT", tokenId: "wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf", from: 0 }) {
Error fetching events Error: Gateway Time-out
45930 commented 3 weeks ago

I will open a quick PR to make the default behavior the same, and raise this issue further with our node team to understand why the difference between nothing and 0 is causing timeouts.

dfstio commented 3 weeks ago

@dfstio, can you share your project config for running this test? I ran npm i minafnt in a new zkapp-cli project and tried to run your example code, but I got this error:

SyntaxError: Unexpected token 'export'

      at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1796:14)
      at Object.<anonymous> (src/common/transactions.ts:16:1)
      at Object.<anonymous> (src/common/common.ts:6:1)
      at Object.<anonymous> (src/node/index.ts:1:1)
      at Object.<anonymous> (node_modules/minanft/lib/ts/src/storage/arweave.js:7:35)
      at Object.<anonymous> (node_modules/minanft/lib/ts/src/minanft.js:20:19)
      at Object.<anonymous> (node_modules/minanft/lib/ts/src/index.js:22:14)

If you could share your ts config and package json, or just link me to the repo so I can play around, I'd appreciate it!

minanft is a library and you can use yarn to install it and run the tests: yarn