zkSync를 사용하여 이더미의 기존 구조기를 시작합니다


zkSyncis 뭐


zkSync는 ZK Rollus를 사용하는 L2 네트워크입니다.현재의 이더머의 축소와 가스비 문제에 대한 유망한 해결책 중 하나다.
이걸 이용하면 기분이 좋다면 예전의 이더리움의 안전성을 유지하고 많은 거래를 처리할 수 있고 가스비도 적당히 싸게 받을 수 있다.대략적인 구조로 계산 처리와 저장 데이터가 zkSync에서 실행되고 이 거래를 총괄하여 이더미움에 보내고 이 거래의 역사만 보존한다.이렇게 하면 가능한 한 이더리움 네트워크의 책임과 의무를 다른 네트워크로 옮기고 거래 횟수를 줄여 부하를 억제하며 여분의 비용을 줄이는 동시에 이더리움 보안을 이용할 수 있다.이런 ZK Rollus를 활용한 솔루션은 zkSync뿐만 아니라 Starkware(의 StarkNet)과Scroll 등 기업의 네트워크도 있다.스타크와 SNARK를 사용하는 기법은 다르지만 기본적인 구조는 같아야 한다.

무슨


말하자면 ZK Rollus의 ZK는 제로 지식 증명(Zero Knowledge Proof이기 때문에 흔히 ZKP라고 약칭)을 말한다.이것에 대해 이론적 부분을 설명하자면 또 다른 보도가 되어 귀찮고 자신도 엄격한 수학적 증명을 완전히 이해하지 못하기 때문에 사랑을 끊는다.개념적인 이해일 뿐이라면 어렵지 않으니 아래 링크를 읽어보세요.
https://blog.goodaudience.com/understanding-zero-knowledge-proofs-through-simple-examples-df673f796d99
스타크랑 SNARK이 뭐예요?만약 그렇다면, 이 보도 따위는.
https://consensys.net/blog/blockchain-explained/zero-knowledge-proofs-starks-vs-snarks/
스타크웨어가 스타크의 수학적 이해에 대해 해설해 주는 해슨의 영상도 있는데, 마음에 드는 사람에게는 이것도 괜찮다.
https://starkware.co/stark-101/

왜 zkSync


ZK Rollus계의 네트워크라면 어느 것이든 상관없지만, zkSync를 사용해 보려고 하는 것은 기존의Solidity가 쓴 설정을 다시 이용할 수 있기 때문이다.스타크넷도 유망한 네트워크지만 cairo라는 전용 언어로 구조기를 써야 한다.zkSync라면 새로운 언어와 생태계를 기억하지 않더라도 이미 보유한 기존 자산을 활용할 수 있다.
https://docs.zksync.io/dev/contracts/

메시지


전 프랜차이즈 트위터를 만든다는 기사를 랩틴에 쓴 적이 있다.이더미움을 API를 재생할 수 있는 단순한 DB로 보고 모든 트위터와 사용자 정보를 저장하는 아키텍처를 만들고, API를 두드려 트위터 스타일의 프런트를 구현했다는 내용이다.
https://zenn.dev/razokulover/articles/067fd5cf55292e
이번에 그곳에서 사용한 프레임을 그대로 zkSync로 옮겨 보았는데, 그 순서를 예로 들면 어떻게 해야 기존 프레임을 옮길 수 있는지 써 보았다.

환경 구조


어쨌든 환경 구축부터 시작해.로컬 환경은 Docker와 docker compose로 일어납니다.L1 노드, L2 노드 및 기타 다양한 노드가 한꺼번에 생성됩니다.
우선 현지 환경에서 사용하는 창고의 클론입니다.
git clone https://github.com/matter-labs/local-setup.git
및 환경의 제작.
cd local-setup
./start.sh
그뿐입니다.

종속성 설치


필요한 의존 관계를 단숨에 설치하다.
yarn add -D typescript ts-node ethers zksync-web3 hardhat @matterlabs/[email protected] @matterlabs/[email protected] @nomiclabs/hardhat-waffle chai-as-promised @nomiclabs/hardhat-ethers @types/chai-as-promised
zkSync용 구조기 컴파일링과 디버깅에 필요한 패키지는 우선 다음과 같은 두 가지가 있다.개발 환경인 하드하트가 추천되기 때문에 하드하트도 필수다.

  • @matterlabs/hardhat-zksync-solc
  • 컴파일용

  • @matterlabs/hardhat-zksync-deploy
  • depro용
  • 또한, zksync-web3은 Geth와 구조기 교환web3의 zkSync 버전이다.
    기타 Hardhat-XXXX 계 확장 테스트에 필요한 Hardhat.
    typescript/ts-node/@types에 대해 원하는 환경에 따라 조정합니다.

    Hardhat 설정

    hardhat.config.ts zkSync를 사용하도록 수정되었습니다.특히 zkSyncDeploy.NODE_ENVtest일 때 로컬에서 세운 노드를 해제 처리합니다.이렇게 하면 현지 환경에서 구조 테스트를 진행할 수 있다.
    hardhat.config.ts
    import { config as dotEnvConfig } from "dotenv";
    dotEnvConfig();
    
    import "@nomiclabs/hardhat-waffle";
    import "@matterlabs/hardhat-zksync-deploy";
    import "@matterlabs/hardhat-zksync-solc";
    
    const zkSyncDeploy =
      process.env.NODE_ENV == "test"
        ? {
            zkSyncNetwork: "http://localhost:3050",
            ethNetwork: "http://localhost:8545",
          }
        : {
            zkSyncNetwork: "https://zksync2-testnet.zksync.dev",
            ethNetwork: "goerli",
          };
    
    module.exports = {
      zksolc: {
        version: "0.1.0",
        compilerSource: "docker",
        settings: {
          optimizer: {
            enabled: true,
          },
          experimental: {
            dockerImage: "matterlabs/zksolc",
          },
        },
      },
      zkSyncDeploy,
      solidity: {
        version: "0.8.10",
      },
      networks: {
        hardhat: {
          zksync: true,
        },
      },
    };
    

    테스트


    준비가 다 되었기 때문에 기존의 프레임을 로컬에서 zkSync로 컴파일하여 테스트가 통과되었는지 확인하고 싶습니다. (테스트가 없는 프레임 같은 것은 존재하지 않습니다.)
    이번 테스트의 프레임은주요 내용은 트위터의 API와 데이터를 보존하는 것이다.
    원래 테스트는이 테스트를 다음 테스트로 덮어쓰고 실행합니다.
    재작성 테스트(길이)
    import * as hre from "hardhat";
    import chai from "chai";
    import chaiAsPromised from "chai-as-promised";
    import { Wallet, Provider } from "zksync-web3";
    import { Deployer } from "@matterlabs/hardhat-zksync-deploy";
    chai.use(chaiAsPromised);
    
    const { expect } = chai;
    const RICH_WALLET_PK =
      "0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110";
    
    async function setup() {
      const provider = Provider.getDefaultProvider();
      const wallet = new Wallet(RICH_WALLET_PK, provider);
      const deployer = new Deployer(hre, wallet);
      const artifact = await deployer.loadArtifact("TwitterV1");
      const deployed = await deployer.deploy(artifact, []);
      return { twitter: deployed, owner: wallet };
    }
    
    describe("Twitter", function () {
      describe("setTweet", function () {
        it("Should return error", async function () {
          const { twitter } = await setup();
    
          expect(twitter.setTweet("     ")).to.eventually.be.rejected;
        });
      });
    
      describe("getTimeline", function () {
        it("Should return the tweet", async function () {
          const { twitter, owner } = await setup();
    
          const tx = await twitter.setTweet("Hello, world!", "");
          await tx.wait();
          const tweets = await twitter.getTimeline(0, 10);
          const tweet = tweets[0];
    
          expect(tweet.content).to.equal("Hello, world!");
          expect(tweet.author).to.equal(owner.address);
        });
      });
    
      describe("getUserTweets", function () {
        it("Should return the tweets order by timestamp desc", async function () {
          const { twitter, owner } = await setup();
    
          let tx = await twitter.setTweet("Hello, world!", "");
          await tx.wait();
          tx = await twitter.setTweet("Hello, new world!", "");
          await tx.wait();
    
          const tweets = await twitter.getUserTweets(owner.address);
          const tweet = tweets[0];
          expect(tweet.content).to.equal("Hello, new world!");
          expect(tweet.author).to.equal(owner.address);
        });
    
        it("Should return the tweet", async function () {
          const { twitter, owner } = await setup();
    
          let tx = await twitter.setTweet(
            "Hello, world!",
            "data:image/png;base64,XXXX"
          );
          await tx.wait();
    
          const tweets = await twitter.getUserTweets(owner.address);
          const tweet = tweets[0];
          expect(tweet.content).to.equal("Hello, world!");
          expect(tweet.author).to.equal(owner.address);
          expect(tweet.attachment).to.equal("data:image/png;base64,XXXX");
        });
      });
    
      describe("getTweet", function () {
        it("Should return the tweet", async function () {
          const { twitter, owner } = await setup();
    
          let tx = await twitter.setTweet("Hello, world!", "");
          await tx.wait();
    
          const tweet = await twitter.getTweet(1);
          expect(tweet.content).to.equal("Hello, world!");
          expect(tweet.author).to.equal(owner.address);
        });
      });
    
      describe("follow", function () {
        it("Should follow user", async function () {
          const { twitter, owner } = await setup();
          const [_, user] = await hre.ethers.getSigners();
    
          let tx = await twitter.follow(user.address);
          await tx.wait();
    
          const followings = await twitter.getFollowings(owner.address);
          const following = followings[0];
          expect(following.id).to.equal(user.address);
    
          const followers = await twitter.getFollowers(user.address);
          const follower = followers[0];
          expect(follower.id).to.equal(owner.address);
        });
      });
    
      describe("getFollowings", function () {
        it("Should unfollow user", async function () {
          const { twitter, owner } = await setup();
          const [_, user, user2] = await hre.ethers.getSigners();
    
          let tx = await twitter.follow(user.address);
          await tx.wait();
          tx = await twitter.follow(user2.address);
          await tx.wait();
    
          let followings = await twitter.getFollowings(owner.address);
          expect(followings.length).to.equal(2);
          let followers = await twitter.getFollowers(user.address);
          expect(followers.length).to.equal(1);
          followers = await twitter.getFollowers(user2.address);
          expect(followers.length).to.equal(1);
    
          tx = await twitter.unfollow(user.address);
          await tx.wait();
          followings = await twitter.getFollowings(owner.address);
          expect(followings.length).to.equal(1);
          followers = await twitter.getFollowers(user.address);
          expect(followers.length).to.equal(0);
          followers = await twitter.getFollowers(user2.address);
          expect(followers.length).to.equal(1);
        });
      });
    
      describe("isFollowing", function () {
        it("Should true if follow the address", async function () {
          const { twitter, owner } = await setup();
          const [_, user] = await hre.ethers.getSigners();
    
          let tx = await twitter.follow(user.address);
          await tx.wait();
    
          const following = await twitter.isFollowing(user.address);
          expect(following).to.equal(true);
        });
      });
    
      describe("addLike", function () {
        it("Should add a like to the tweet", async function () {
          const { twitter, owner } = await setup();
    
          let tx = await twitter.setTweet("Hello, world!", "");
          await tx.wait();
    
          let tweets = await twitter.getUserTweets(owner.address);
          let tweet = tweets[0];
          expect(tweet.likes.includes(owner.address)).to.be.false;
    
          tx = await twitter.addLike(tweet.tokenId);
          await tx.wait();
          tweets = await twitter.getUserTweets(owner.address);
          tweet = tweets[0];
          expect(tweet.likes.includes(owner.address)).to.be.true;
        });
      });
    
      describe("getLikes", function () {
        it("Should return liked tweets", async function () {
          const { twitter, owner } = await setup();
    
          let tx = await twitter.setTweet("Hello, world!", "");
          await tx.wait();
    
          let tweets = await twitter.getLikes(owner.address);
          expect(tweets.length).to.equal(0);
    
          tweets = await twitter.getUserTweets(owner.address);
          let tweet = tweets[0];
    
          tx = await twitter.addLike(tweet.tokenId);
          await tx.wait();
    
          tweets = await twitter.getLikes(owner.address);
          tweet = tweets[0];
          expect(tweet.likes.includes(owner.address)).to.be.true;
        });
      });
    
      describe("changeIconUrl/getUserIcon", function () {
        it("Should change icon url", async function () {
          const { twitter, owner } = await setup();
    
          let url = await twitter.getUserIcon(owner.address);
          expect(url).to.equal("");
    
          let tx = await twitter.changeIconUrl("https://example.com/icon.png");
          await tx.wait();
    
          url = await twitter.getUserIcon(owner.address);
          expect(url).to.equal("https://example.com/icon.png");
        });
      });
    
      describe("setComment/getComments", function () {
        it("Should add the comment", async function () {
          const { twitter, owner } = await setup();
    
          let tx = await twitter.setTweet("Hello, world!", "");
          await tx.wait();
    
          tx = await twitter.setComment("Hello, comment!", 1);
          await tx.wait();
    
          const comments = await twitter.getComments(1);
          const comment = comments[0];
          expect(comment.content).to.equal("Hello, comment!");
          expect(comment.author).to.equal(owner.address);
        });
      });
    
      describe("addRetweet", function () {
        it("Should add the rt", async function () {
          const { twitter, owner } = await setup();
    
          let tx = await twitter.setTweet("Hello, world!", "");
          await tx.wait();
    
          tx = await twitter.addRetweet(1);
          await tx.wait();
    
          let tweets = await twitter.getTimeline(0, 2);
          expect(tweets[1].retweets.includes(owner.address)).to.be.true;
          expect(tweets[1].retweetedBy).to.eq(
            "0x0000000000000000000000000000000000000000"
          );
          expect(tweets[0].retweets.includes(owner.address)).to.be.true;
          expect(tweets[0].retweetedBy).to.eq(owner.address);
        });
      });
    
      describe("tokenURI", function () {
        it("Should return base64 encoded string", async function () {
          const { twitter, owner } = await setup();
    
          let tx = await twitter.setTweet("Hello, world!", "");
          await tx.wait();
    
          const tokenURI = await twitter.tokenURI(1);
          expect(tokenURI).to.eq(
            "data:application/json;base64,eyJuYW1lIjoiVHdlZXQgIzEiLCAiZGVzY3JpcHRpb24iOiJIZWxsbywgd29ybGQhIiwgImltYWdlIjogImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5QjRiV3h1Y3owaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNQzl6ZG1jaUlIQnlaWE5sY25abFFYTndaV04wVW1GMGFXODlJbmhOYVc1WlRXbHVJRzFsWlhRaUlIWnBaWGRDYjNnOUlqQWdNQ0F6TlRBZ016VXdJajQ4Y21WamRDQjNhV1IwYUQwaU1UQXdKU0lnYUdWcFoyaDBQU0l4TURBbElpQm1hV3hzUFNJallXRmlPR015SWo0OEwzSmxZM1ErUEhOM2FYUmphRDQ4Wm05eVpXbG5iazlpYW1WamRDQjRQU0l3SWlCNVBTSXdJaUIzYVdSMGFEMGlNVEF3SlNJZ2FHVnBaMmgwUFNJeE1EQWxJajQ4Y0NCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TVRrNU9TOTRhSFJ0YkNJZ1ptOXVkQzF6YVhwbFBTSXhNbkI0SWlCemRIbHNaVDBpWm05dWRDMXphWHBsT2pFd2NIZzdjR0ZrWkdsdVp6bzFjSGc3SWo1VWQyVmxkQ014UEdKeUx6NUlaV3hzYnl3Z2QyOXliR1FoUEdKeUx6NDhhVzFuSUhOeVl6MGlJaTgrUEM5d1Bqd3ZabTl5WldsbmJrOWlhbVZqZEQ0OEwzTjNhWFJqYUQ0OEwzTjJaejQ9In0="
          );
        });
      });
    });
    
    주요 변경점은 @matterlabs/hardhat-zksync-deployzksync-web3zkSync를 사용하여 구조기를 컴파일하고 디자인하는 것이다.
    import * as hre from "hardhat";
    import chai from "chai";
    import chaiAsPromised from "chai-as-promised";
    import { Wallet, Provider } from "zksync-web3";
    import { Deployer } from "@matterlabs/hardhat-zksync-deploy";
    chai.use(chaiAsPromised);
    
    const { expect } = chai;
    const RICH_WALLET_PK =
      "0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110";
      
    async function setup() {
      const provider = Provider.getDefaultProvider();
      const wallet = new Wallet(RICH_WALLET_PK, provider);
      const deployer = new Deployer(hre, wallet);
      const artifact = await deployer.loadArtifact("TwitterV1");
      const deployed = await deployer.deploy(artifact, []);
      return { twitter: deployed, owner: wallet };
    }
    
    이렇게 하면 zkSync가 사용하는 프레임으로 테스트를 진행할 수 있다.특별히 막히지 않고 무사히 통과할 겁니다.

    편역하다


    테스트망에 디버깅을 하기 전에 구조기를 컴파일하세요.시험에 통과하면 다음과 같은 내용만 집행하면 된다.
    yarn hardhat compile
    

    프로그램 설계


    스크립트는 다음과 같은 내용을 사용합니다.시험 때와 마찬가지로 @matterlabs/hardhat-zksync-deployzksync-web3 캐리어 구조기를 사용하여 설계했습니다.zkSync의 테스트망은 https://zksync2-testnet.zksync.dev이기 때문에 Provider의network로 지정됩니다.PRIVATE_KEY 자신의 Metamask의privvate 키를 지정합니다.deployer.zkWallet.deposit일반적인 이더 eum과는 상황이 다르지만, 여기서 zkSync에서 거래를 하기 위해서는 영패가 필요하기 때문에 L1에서 돈을 조금 송금할 뿐이다.
    또 테스트망에서 Goerli를 L1로 사용하기 때문에 사전에 Goerli의 faucet 등으로부터 ETH와 USDC(중요)를 받는다.
    const { Wallet, Provider, utils } = require("zksync-web3");
    const { Deployer } = require("@matterlabs/hardhat-zksync-deploy");
    const functionName = "TwitterV1";
    
    module.exports = async function (hre) {
      console.log("Start deploy!");
      const provider = new Provider("https://zksync2-testnet.zksync.dev");
      const wallet = new Wallet(`0x${process.env.PRIVATE_KEY}`).connect(provider);
      const deployer = new Deployer(hre, wallet);
      const artifact = await deployer.loadArtifact(functionName);
      
      const depositAmount = ethers.utils.parseEther("0.001");
      const depositHandle = await deployer.zkWallet.deposit({
        to: deployer.zkWallet.address,
        token: utils.ETH_ADDRESS,
        amount: depositAmount,
      });
      await depositHandle.wait();
      
      const deployed = await deployer.deploy(artifact, []);
    
      const contractAddress = deployed.address;
      console.log(`${functionName} deployed to:`, contractAddress);
    };
    
    이 단계를 수행할 수 있다면 다음 절차를 따르십시오.
    yarn hardhat deploy-zksync
    
    이렇게 5분 정도 기다렸다가 디버깅을 끝냅니다.
    ZkScan 같은 explorer의 테스트 네트워크 버전이 있기 때문에 이곳에서 자신의 지갑 주소를 검색하면 zkSync에 대한 depuro의 상황을 확인할 수 있을 것입니다.
    https://zksync2-testnet.zkscan.io/

    프런트엔드와 병합


    구조기의 디자인이 끝났기 때문에 전단 측의 기존 코드를 zkSync가 디자인한 구조기에 연결할 수 있습니다.
    요점은 두 개다.이것만 주의하면 대체로 문제가 없을 것이다.

    zkSync 테스트망을 위한 Metamask 설정


    참조Connecting to Metamask & bridging tokens to zkSyncwallet에 네트워크를 추가합니다.앞으로 프런트엔드에서 기본적으로 이 zkSync 네트워크에서 connect 거래 등을 진행할 겁니다.
    시도할 때는 고어리의 네트워크에 잘못 접속해 거래해도 왜 잘 진행되는지 알 수 없다.

    zksync-web3 사용하기


    기존 전단을 설정에 연결된 고객 부분을 zksync-web3로 변경합니다.예를 들면 이런 느낌.
    import { utils } from "ethers";
    import { Contract, Web3Provider, Provider, Wallet } from "zksync-web3";
    import ABI from "resources/contract-abi.json";
    
    export const contractClient = async (library: any, isSigner: boolean) => {
      const inteface = new utils.Interface(ABI.abi);
      const signer = new Web3Provider(library.provider).getSigner();
      return new Contract(
        `${process.env.NEXT_PUBLIC_TWITTER_ETH_CONTRACT_ID}`,
        inteface,
        signer
      );
    };
    
    export const contractProvider = (library: any) => {
      return new Provider("https://zksync2-testnet.zksync.dev");
    };
    

    가장 적합한 곳


    구조가 어디서 설계되고 어디서 실행되는지, 이더미움과 zkSync가 어떤 관계인지 이해하지 못하면 자신이 무엇을 하고 있는지 알기 쉬우니 정리하는 것이 좋다.구조는 zkSync에서 진행되고 거래의 집행과 저장 데이터의 저장은 zkSync에서 이루어지기 때문에 반드시 zkSync에서 거래하는 비용을 지불해야 하기 때문에 이더리움 eum에서 약간의 자금을 연결해야 한다이더럼은 트렉션의 역사만 담았기 때문에 이용자들에게는 별다른 관계가 없는 것 같다.
    또한 zkSync의 EVM이 호환되는 zkEVM은 완전히 호환되지 않는다는 점도 유의해야 한다.자신의 열렬한 예는 오픈즈eppelin_safeMint을 구조기로 사용하지만, 이 방법은 내부에서 사용되고 있다판정 처리 설정EXTCODESIZE,zkEVM에서는 대응하지 않는다.그래서 사용할 수 없습니다_safeMint._mint로 바꾸면 이동이 가능합니다.zkSync의 discord에도 쓰여 있지만 비교적 흔한 포인트인 것 같습니다.

    최후


    이렇게 하면 zkSync의 설정을 컴파일/디버깅하고 전단과 통합할 수 있다.
    데모를 여기서 확인할 수 있어요.
    코드는 아래에 놓으세요.
    반한 부분이 여럿 있지만, 기존 소일디티의 프레임은 기본적으로 그렇게 능동적인 zkEVM이 품위 있게 느껴진다.zkSync2.0은 Testnet만 있지만 메일넷이 시작되면 가져오는 프로토콜도 늘어나겠죠.
    어쨌든 한번 움직여 보고 싶은 사람은 Tutorial Hello World부터 시작하는 게 좋을 것 같아요.
    https://v2-docs.zksync.io/dev/guide/hello-world.html
    솔직히 자신이 아직 모르는 zkSync의 기능과 zkEVM의 문제 등도 상당한 분위기가 있지만, 앞으로도 그의 움직임을 지켜볼 것으로 보인다.

    링크


    https://zksync.io/
    https://v2-docs.zksync.io/dev/
    https://docs.zksync.io/userdocs/tech.html#zk-rollup-architecture
    https://v2-docs.zksync.io/api/hardhat/testing.html
    https://zksync2-testnet.zkscan.io/
    https://blog.goodaudience.com/understanding-zero-knowledge-proofs-through-simple-examples-df673f796d99
    https://consensys.net/blog/blockchain-explained/zero-knowledge-proofs-starks-vs-snarks/
    https://starkware.co/stark-101/

    좋은 웹페이지 즐겨찾기