From 27c3efabe2741d6eb908bc5986b07de75be2c4a7 Mon Sep 17 00:00:00 2001 From: prathamesh0 <42446521+prathamesh0@users.noreply.github.com> Date: Thu, 22 Dec 2022 13:13:59 +0530 Subject: [PATCH] Add a local stack for ERC721 watcher (#81) * Add a stack for erc721 watcher * Add a demo * Avoid watcher db service and volume name conflicts across watchers * Avoid host port conflicts across watchers * Avoid watcher-db port conflicts --- app/data/container-image-list.txt | 1 + app/data/pod-list.txt | 1 + compose/docker-compose-watcher-erc20.yml | 16 +- compose/docker-compose-watcher-erc721.yml | 49 ++++ config/watcher-erc20/erc20-watcher.toml | 4 +- config/watcher-erc721/erc721-watcher.toml | 56 +++++ .../cerc-watcher-erc721/Dockerfile | 13 ++ container-build/cerc-watcher-erc721/build.sh | 7 + stacks/erc721/README.md | 214 ++++++++++++++++++ stacks/erc721/stack.yml | 18 ++ 10 files changed, 369 insertions(+), 10 deletions(-) create mode 100644 compose/docker-compose-watcher-erc721.yml create mode 100644 config/watcher-erc721/erc721-watcher.toml create mode 100644 container-build/cerc-watcher-erc721/Dockerfile create mode 100755 container-build/cerc-watcher-erc721/build.sh create mode 100644 stacks/erc721/README.md create mode 100644 stacks/erc721/stack.yml diff --git a/app/data/container-image-list.txt b/app/data/container-image-list.txt index 19ee8da6..4c7c052b 100644 --- a/app/data/container-image-list.txt +++ b/app/data/container-image-list.txt @@ -14,6 +14,7 @@ cerc/fixturenet-eth-geth cerc/fixturenet-eth-lighthouse cerc/watcher-mobymask cerc/watcher-erc20 +cerc/watcher-erc721 cerc/test-container cerc/eth-probe cerc/builder-js diff --git a/app/data/pod-list.txt b/app/data/pod-list.txt index 0e02bc4c..42c46acf 100644 --- a/app/data/pod-list.txt +++ b/app/data/pod-list.txt @@ -12,6 +12,7 @@ fixturenet-laconicd fixturenet-eth watcher-mobymask watcher-erc20 +watcher-erc721 test eth-probe keycloak diff --git a/compose/docker-compose-watcher-erc20.yml b/compose/docker-compose-watcher-erc20.yml index 18beacb0..82e76120 100644 --- a/compose/docker-compose-watcher-erc20.yml +++ b/compose/docker-compose-watcher-erc20.yml @@ -2,7 +2,7 @@ version: '3.2' services: - watcher-db: + erc20-watcher-db: restart: unless-stopped image: postgres:14-alpine environment: @@ -12,9 +12,9 @@ services: - POSTGRES_PASSWORD=password volumes: - ../config/postgresql/multiple-postgressql-databases.sh:/docker-entrypoint-initdb.d/multiple-postgressql-databases.sh - - watcher_db_data:/var/lib/postgresql/data + - erc20_watcher_db_data:/var/lib/postgresql/data ports: - - "0.0.0.0:15432:5432" + - "0.0.0.0:15433:5432" healthcheck: test: ["CMD", "nc", "-v", "localhost", "5432"] interval: 20s @@ -27,7 +27,7 @@ services: depends_on: ipld-eth-server: condition: service_healthy - watcher-db: + erc20-watcher-db: condition: service_healthy image: cerc/watcher-erc20:local environment: @@ -36,14 +36,14 @@ services: volumes: - ../config/watcher-erc20/erc20-watcher.toml:/app/packages/erc20-watcher/environments/local.toml ports: - - "0.0.0.0:3001:3001" - - "0.0.0.0:9001:9001" + - "0.0.0.0:3002:3001" + - "0.0.0.0:9002:9001" healthcheck: - test: ["CMD", "nc", "-v", "localhost", "3001"] + test: ["CMD", "nc", "-v", "localhost", "3002"] interval: 20s timeout: 5s retries: 15 start_period: 5s volumes: - watcher_db_data: + erc20_watcher_db_data: diff --git a/compose/docker-compose-watcher-erc721.yml b/compose/docker-compose-watcher-erc721.yml new file mode 100644 index 00000000..cdfcdbc7 --- /dev/null +++ b/compose/docker-compose-watcher-erc721.yml @@ -0,0 +1,49 @@ +version: '3.2' + +services: + + erc721-watcher-db: + restart: unless-stopped + image: postgres:14-alpine + environment: + - POSTGRES_USER=vdbm + - POSTGRES_MULTIPLE_DATABASES=erc721-watcher,erc721-watcher-job-queue + - POSTGRES_EXTENSION=erc721-watcher-job-queue:pgcrypto + - POSTGRES_PASSWORD=password + volumes: + - ../config/postgresql/multiple-postgressql-databases.sh:/docker-entrypoint-initdb.d/multiple-postgressql-databases.sh + - erc721_watcher_db_data:/var/lib/postgresql/data + ports: + - "0.0.0.0:15434:5432" + healthcheck: + test: ["CMD", "nc", "-v", "localhost", "5432"] + interval: 20s + timeout: 5s + retries: 15 + start_period: 10s + + erc721-watcher: + restart: unless-stopped + depends_on: + ipld-eth-server: + condition: service_healthy + erc721-watcher-db: + condition: service_healthy + image: cerc/watcher-erc721:local + environment: + - ETH_RPC_URL=http://go-ethereum:8545 + command: ["sh", "-c", "yarn server"] + volumes: + - ../config/watcher-erc721/erc721-watcher.toml:/app/packages/erc721-watcher/environments/local.toml + ports: + - "0.0.0.0:3009:3009" + - "0.0.0.0:9003:9001" + healthcheck: + test: ["CMD", "nc", "-v", "localhost", "3009"] + interval: 20s + timeout: 5s + retries: 15 + start_period: 5s + +volumes: + erc721_watcher_db_data: diff --git a/config/watcher-erc20/erc20-watcher.toml b/config/watcher-erc20/erc20-watcher.toml index 5b8b4ce5..20773321 100644 --- a/config/watcher-erc20/erc20-watcher.toml +++ b/config/watcher-erc20/erc20-watcher.toml @@ -12,7 +12,7 @@ [database] type = "postgres" - host = "watcher-db" + host = "erc20-watcher-db" port = 5432 database = "erc20-watcher" username = "vdbm" @@ -32,7 +32,7 @@ deleteOnStart = false [jobQueue] - dbConnectionString = "postgres://vdbm:password@watcher-db:5432/erc20-watcher-job-queue" + dbConnectionString = "postgres://vdbm:password@erc20-watcher-db:5432/erc20-watcher-job-queue" maxCompletionLagInSecs = 300 jobDelayInMilliSecs = 100 eventsInBatch = 50 diff --git a/config/watcher-erc721/erc721-watcher.toml b/config/watcher-erc721/erc721-watcher.toml new file mode 100644 index 00000000..8e877411 --- /dev/null +++ b/config/watcher-erc721/erc721-watcher.toml @@ -0,0 +1,56 @@ +[server] + host = "0.0.0.0" + port = 3009 + kind = "lazy" + + # Checkpointing state. + checkpointing = true + + # Checkpoint interval in number of blocks. + checkpointInterval = 2000 + + # Enable state creation + enableState = true + + # Boolean to filter logs by contract. + filterLogs = false + + # Max block range for which to return events in eventsInRange GQL query. + # Use -1 for skipping check on block range. + maxEventsBlockRange = 1000 + +[metrics] + host = "127.0.0.1" + port = 9000 + [metrics.gql] + port = 9001 + +[database] + type = "postgres" + host = "erc721-watcher-db" + port = 5432 + database = "erc721-watcher" + username = "vdbm" + password = "password" + synchronize = true + logging = false + maxQueryExecutionTime = 100 + +[upstream] + [upstream.ethServer] + gqlApiEndpoint = "http://ipld-eth-server:8082/graphql" + rpcProviderEndpoint = "http://ipld-eth-server:8081" + + [upstream.cache] + name = "requests" + enabled = false + deleteOnStart = false + +[jobQueue] + dbConnectionString = "postgres://vdbm:password@erc721-watcher-db:5432/erc721-watcher-job-queue" + maxCompletionLagInSecs = 300 + jobDelayInMilliSecs = 100 + eventsInBatch = 50 + blockDelayInMilliSecs = 2000 + prefetchBlocksInMem = true + prefetchBlockCount = 10 diff --git a/container-build/cerc-watcher-erc721/Dockerfile b/container-build/cerc-watcher-erc721/Dockerfile new file mode 100644 index 00000000..6358df9a --- /dev/null +++ b/container-build/cerc-watcher-erc721/Dockerfile @@ -0,0 +1,13 @@ +FROM node:16.17.1-alpine3.16 + +RUN apk --update --no-cache add git python3 alpine-sdk + +WORKDIR /app + +COPY . . + +RUN echo "Building watcher-ts" && \ + git checkout v0.2.19 && \ + yarn && yarn build + +WORKDIR /app/packages/erc721-watcher diff --git a/container-build/cerc-watcher-erc721/build.sh b/container-build/cerc-watcher-erc721/build.sh new file mode 100755 index 00000000..b9c9c0b8 --- /dev/null +++ b/container-build/cerc-watcher-erc721/build.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# Build cerc/watcher-erc721 + +# See: https://stackoverflow.com/a/246128/1701505 +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +docker build -t cerc/watcher-erc721:local -f ${SCRIPT_DIR}/Dockerfile ${CERC_REPO_BASE_DIR}/watcher-ts diff --git a/stacks/erc721/README.md b/stacks/erc721/README.md new file mode 100644 index 00000000..ed5f9c80 --- /dev/null +++ b/stacks/erc721/README.md @@ -0,0 +1,214 @@ +# ERC721 Watcher + +Instructions to deploy a local ERC721 watcher stack (core + watcher) for demonstration and testing purposes using [laconic-stack-orchestrator](../../README.md#setup) + +## Setup + +* Clone / pull required repositories: + + ```bash + $ laconic-so setup-repositories --include cerc-io/go-ethereum,cerc-io/ipld-eth-db,cerc-io/ipld-eth-server,cerc-io/watcher-ts --pull + ``` + +* Build the core and watcher container images: + + ```bash + $ laconic-so build-containers --include cerc/go-ethereum,cerc/go-ethereum-foundry,cerc/ipld-eth-db,cerc/ipld-eth-server,cerc/watcher-erc721 + ``` + + This should create the required docker images in the local image registry. + +* Deploy the stack: + + ```bash + $ laconic-so deploy-system --include db,go-ethereum-foundry,ipld-eth-server,watcher-erc721 up + ``` + +## Demo + +* Find the watcher container's id using `docker ps` and export it for later use: + + ```bash + $ export CONTAINER_ID= + ``` + +* Deploy an ERC721 token: + + ```bash + $ docker exec $CONTAINER_ID yarn nft:deploy:docker + ``` + + Export the address of the deployed token to a shell variable for later use: + + ```bash + $ export NFT_ADDRESS= + ``` + +* Open `http://localhost:3009/graphql` (GraphQL Playground) in a browser window + +* Connect MetaMask to `http://localhost:8545` (with chain ID `99`) + +* Export your MetaMask account (second account) address to a shell variable for later use: + + ```bash + $ export RECIPIENT_ADDRESS= + ``` + +* To get the primary account's address, run: + + ```bash + $ docker exec $CONTAINER_ID yarn account:docker + ``` + + Export it to shell variable for later use: + + ```bash + $ export PRIMARY_ADDRESS= + ``` + +* To get the current block hash at any time, run: + + ```bash + $ docker exec $CONTAINER_ID yarn block:latest:docker + ``` + +* Fire the following GQL query (uses `eth_call`) in the playground: + + ```graphql + query { + name( + blockHash: "LATEST_BLOCK_HASH" + contractAddress: "NFT_ADDRESS" + ) { + value + proof { + data + } + } + + symbol( + blockHash: "LATEST_BLOCK_HASH" + contractAddress: "NFT_ADDRESS" + ) { + value + proof { + data + } + } + + balanceOf( + blockHash: "LATEST_BLOCK_HASH" + contractAddress: "NFT_ADDRESS" + owner: "PRIMARY_ADDRESS" + ) { + value + proof { + data + } + } + } + ``` + + Balance for the `PRIMARY_ADDRESS` should be `0` as the token is yet to be minted. + +* Fire the following GQL query (uses `storage` calls) in the playground: + + ```graphql + query { + _name( + blockHash: "LATEST_BLOCK_HASH" + contractAddress: "NFT_ADDRESS" + ) { + value + proof { + data + } + } + + _symbol( + blockHash: "LATEST_BLOCK_HASH" + contractAddress: "NFT_ADDRESS" + ) { + value + proof { + data + } + } + + _balances( + blockHash: "LATEST_BLOCK_HASH" + contractAddress: "NFT_ADDRESS" + key0: "PRIMARY_ADDRESS" + ) { + value + proof { + data + } + } + } + ``` + +* Mint the token: + + ```bash + $ docker exec $CONTAINER_ID yarn nft:mint:docker --nft $NFT_ADDRESS --to $PRIMARY_ADDRESS --token-id 1 + ``` + + Fire the GQL query above again with latest block hash. The balance should increase to `1`. + +* Get the latest block hash and run the following GQL query in the playground for `balanceOf` and `ownerOf` (`eth_call`): + + ```graphql + query { + fromBalanceOf: balanceOf( + blockHash: "LATEST_BLOCK_HASH" + contractAddress: "NFT_ADDRESS" + owner: "PRIMARY_ADDRESS" + ) { + value + proof { + data + } + } + + toBalanceOf: balanceOf( + blockHash: "LATEST_BLOCK_HASH" + contractAddress: "NFT_ADDRESS" + owner: "RECIPIENT_ADDRESS" + ) { + value + proof { + data + } + } + + ownerOf( + blockHash: "LATEST_BLOCK_HASH" + contractAddress: "NFT_ADDRESS" + tokenId: 1 + ) { + value + proof { + data + } + } + } + ``` + + Balance should be `1` for the `PRIMARY_ADDRESS`, `0` for the `RECIPIENT_ADDRESS` and owner value of the token should be equal to the `PRIMARY_ADDRESS`. + +* Transfer the token: + + ```bash + $ docker exec $CONTAINER_ID yarn nft:transfer:docker --nft $NFT_ADDRESS --from $PRIMARY_ADDRESS --to $RECIPIENT_ADDRESS --token-id 1 + ``` + + Fire the GQL query above again with the latest block hash. The token should be transferred to the recipient. + +## Clean up + +* To stop all the services running in background: + + ```bash + $ laconic-so deploy-system --include db,go-ethereum-foundry,ipld-eth-server,watcher-erc721 down + ``` diff --git a/stacks/erc721/stack.yml b/stacks/erc721/stack.yml new file mode 100644 index 00000000..85072d4f --- /dev/null +++ b/stacks/erc721/stack.yml @@ -0,0 +1,18 @@ +version: "1.0" +name: erc721-watcher +repos: + - cerc-io/go-ethereum + - cerc-io/ipld-eth-db + - cerc-io/ipld-eth-server + - cerc-io/watcher-ts +containers: + - cerc/go-ethereum + - cerc/go-ethereum-foundry + - cerc/ipld-eth-db + - cerc/ipld-eth-server + - cerc/watcher-erc721 +pods: + - go-ethereum-foundry + - db + - ipld-eth-server + - watcher-erc721