diff --git a/.codecov.yml b/.codecov.yml index 0cafdec118..ef29efa7b2 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -16,6 +16,8 @@ coverage: # from go.work file flags: + # note: go.work flags should be in order of the directory structure + # also, if you're using multiple module names, you're doing something wrong agents: paths: - agents/ diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..54f41a6fb7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +.github +.vscode + +node_modules +**/node_modules diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 02c138595e..789f521d21 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -591,3 +591,4 @@ jobs: remove-labels: 'needs-go-generate-${{matrix.package}}' repo-token: ${{ secrets.GITHUB_TOKEN }} issue-number: ${{ needs.issue_number.outputs.issue_number }} + diff --git a/.github/workflows/goreleaser-actions.yml b/.github/workflows/goreleaser-actions.yml index 446ec1b2a6..e21818bef1 100644 --- a/.github/workflows/goreleaser-actions.yml +++ b/.github/workflows/goreleaser-actions.yml @@ -75,6 +75,9 @@ jobs: push: true file: ./docker/goreleaser/Dockerfile # TODO this needs to be versioned + # Note: this automatically pushes the latest tag for sanguine-goreleaser even on branched workflows. While unlikely, + # this could break local devnets that rely on working versions of this image and as such the latest tag should only be pushed on master + # additionally, tags representing a specific version rather than the hash of the file should be considered for future use. tags: ghcr.io/synapsecns/sanguine-goreleaser:latest,${{ steps.name-export.outputs.TAG_NAME }} cache-from: type=registry,ref=ghcr.io/synapsecns/sanguine-goreleaser:buildcache cache-to: type=registry,ref=ghcr.io/synapsecns/sanguine-goreleaser:buildcache,mode=max diff --git a/.gitignore b/.gitignore index 6f491f2f7f..5bac521f7e 100644 --- a/.gitignore +++ b/.gitignore @@ -123,6 +123,8 @@ codecov/ # fasthttp fastcache.tmp* +#vercel + # ignore all go built files in local dirs main -**/.vercel +.devnet/ diff --git a/.gitmodules b/.gitmodules index 2f3ff51679..49bafc0c9f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -11,3 +11,6 @@ [submodule "services/cctp-relayer/external/synapse-contracts"] path = services/cctp-relayer/external/synapse-contracts url = https://github.com/synapsecns/synapse-contracts +[submodule "packages/contracts-core/lib/solmate"] + path = packages/contracts-core/lib/solmate + url = https://github.com/transmissions11/solmate diff --git a/README.md b/README.md index edecbded3a..e7b5f5b2b7 100644 --- a/README.md +++ b/README.md @@ -111,3 +111,10 @@ Use the above commands to recompile the packages. ## Dealing with submodules This repo make use of [multiple](.gitattributes) [submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules). To avoid issues when checking out different branches, you can use `git submodule update --init --recursive` after switching to a branch or `git checkout feat/branch-name --recurse-submodules` when switching branches. + +# Building Agents Locally + + + + +In order to minimize risks coming from extraneous dependencies or supply chain attacks in a production like enviornment, all distributed images are built as [scratch](https://hub.docker.com/_/scratch) or [distroless](https://github.com/GoogleContainerTools/distroless#distroless-container-images) images. Builder containers are also not used to restrict the build enviornment to the [goreleaser container](https://github.com/synapsecns/sanguine/pkgs/container/sanguine-goreleaser). All production images are kept in the `docker/` file as `[dir].Dockerfile`. Local diff --git a/docker/devnet/README.md b/docker/devnet/README.md new file mode 100644 index 0000000000..ce262082b3 --- /dev/null +++ b/docker/devnet/README.md @@ -0,0 +1,21 @@ +Local contains dockerfiles for local development. These files should be able to be built from the root of the repo without any external dependencies. + +These don't need to be made for every package, just those involved in devnet. + +# Let's keep track of commands here we'll need in our devnet script + +1. cd `docker/devnet` +2. `docker compose build --progress=plain` + +## Default Port Map: + +| Container | Port | +|------------------------|------| +| omnirpc | 9001 | +| chain_a (chain_id: 42) | 8042 | +| chain_b (chain_id: 43) | 8043 | +| chain_c (chain_id: 44) | 8044 | +| scribe | 9002 | + + + diff --git a/docker/devnet/agents.Dockerfile b/docker/devnet/agents.Dockerfile new file mode 100644 index 0000000000..808597f457 --- /dev/null +++ b/docker/devnet/agents.Dockerfile @@ -0,0 +1,25 @@ +FROM ghcr.io/synapsecns/sanguine-goreleaser:latest as builder + +ARG VERSION=v0.0.0 + +COPY ./services /app/services +COPY ./agents /app/agents +COPY ./core /app/core +COPY ./ethergo /app/ethergo +COPY ./tools /app/tools +COPY ./contrib /app/contrib +COPY ./go.work /app/go.work +COPY ./go.work.sum /app/go.work.sum +COPY ./.git /app/.git + +WORKDIR /app/agents + +RUN --mount=type=cache,target=/root/go/pkg/mod GOPROXY=https://proxy.golang.org go mod download -x +RUN --mount=type=cache,target=/root/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build CC=gcc CXX=g++ go build -tags=netgo,osusergo -ldflags="-s -w -extldflags '-static'" -o /app/bin/agents main.go + +FROM ubuntu:latest + +RUN apt update && apt install -y bash sqlite3 htop +COPY --from=builder /app/bin/agents /usr/local/bin + +CMD ["agents"] diff --git a/docker/devnet/config/executor-config.yml b/docker/devnet/config/executor-config.yml new file mode 100644 index 0000000000..fcea0b49c0 --- /dev/null +++ b/docker/devnet/config/executor-config.yml @@ -0,0 +1,53 @@ +# The `db_config` field specifies the database type and the source (either a path or a connection string). +db_config: + # Must be mysql or sqlite. + type: sqlite + # Source is either a path (for sqlite) or a connection string (for mysql). + source: '/config/synapse.db' + +# The base omnirpc url which each chain's collection of RPC's will be proxied through. +base_omnirpc_url: http://omnirpc:9001 +execute_interval: 5 +summit_chain_id: 42 +summit_address: 0x1EC96ab1Fdb92565A0839b12d42c13c8135f6c11 +inbox_address: 0x2eB68A0C21413aA78055F6F7F262De56979aFBEa + +# For each chain (including the Summit chain), specify the origin and destination addresses. +# For remote chains, also specify the light inbox address. +chains: + - chain_id: 42 + origin_address: 0xD755a6D98C4557c66ebbD9D76f1BEbC48e84afa0 + destination_address: 0x7219284B26F44B2A584827034422a33450635f7A + - chain_id: 43 + origin_address: 0xD755a6D98C4557c66ebbD9D76f1BEbC48e84afa0 + destination_address: 0x7219284B26F44B2A584827034422a33450635f7A + light_inbox_address: 0xa6A694d8D2430964DcD11f2E649649Fc9557a56b + - chain_id: 44 + origin_address: 0xD755a6D98C4557c66ebbD9D76f1BEbC48e84afa0 + destination_address: 0x7219284B26F44B2A584827034422a33450635f7A + light_inbox_address: 0xa6A694d8D2430964DcD11f2E649649Fc9557a56b + +# The `unbonded_signer` field specifies the path to the file containing the private key of the signer +unbonded_signer: + type: 'File' + file: '/config/executor-signer.txt' + +submitter_config: + chains: + 42: + supports_eip_1559: true + gas_estimate: 7500000 + 43: + gas_bump_percentage: 40 + is_l2: true + gas_estimate: 7500000 + 44: + gas_bump_percentage: 40 + is_l2: true + gas_estimate: 7500000 + +scribe_config: + type: 'remote' + port: 9002 + url: 'scribe' + diff --git a/docker/devnet/config/executor-signer.txt b/docker/devnet/config/executor-signer.txt new file mode 100644 index 0000000000..1102cbd038 --- /dev/null +++ b/docker/devnet/config/executor-signer.txt @@ -0,0 +1 @@ +103a9ef39d4ced2988f1d5084460ebf8ea3baea2b2ca78265b637e48d99dce82 diff --git a/docker/devnet/config/guard-bonded-signer.txt b/docker/devnet/config/guard-bonded-signer.txt new file mode 100644 index 0000000000..73cd6db892 --- /dev/null +++ b/docker/devnet/config/guard-bonded-signer.txt @@ -0,0 +1 @@ +b31048b0aa87649bdb9016c0ee28c788ddfc45e52cd71cc0da08c47cb4390ae7 diff --git a/docker/devnet/config/guard-config.yml b/docker/devnet/config/guard-config.yml new file mode 100644 index 0000000000..8f7d5047b7 --- /dev/null +++ b/docker/devnet/config/guard-config.yml @@ -0,0 +1,66 @@ +# The `db_config` field specifies the database type and the source (either a path or a connection string). +db_config: + # Must be mysql or sqlite. + type: sqlite + # Source is either a path (for sqlite) or a connection string (for mysql). + source: '/data/synapse.db' + +# The base omnirpc url which each chain's collection of RPC's will be proxied through. +base_omnirpc_url: http://omnirpc:9001 +refresh_interval_seconds: 5 + +# For each chain (domain), specify the necessary contracts. ***** TODO: Update this ***** +domains: + domain_client1: + domain_id: 43 + type: EVM + required_confirmations: 0 + origin_address: 0xD755a6D98C4557c66ebbD9D76f1BEbC48e84afa0 + domain_client2: + domain_id: 42 + type: EVM + required_confirmations: 0 + origin_address: 0xD755a6D98C4557c66ebbD9D76f1BEbC48e84afa0 + summit_address: 0x1EC96ab1Fdb92565A0839b12d42c13c8135f6c11 + inbox_address: 0x2eB68A0C21413aA78055F6F7F262De56979aFBEa + domain_client3: + domain_id: 44 + type: EVM + required_confirmations: 0 + origin_address: 0xD755a6D98C4557c66ebbD9D76f1BEbC48e84afa0 + +# Specify the summit domain id +summit_domain_id: 42 +# Guards have a `domain_id` of 0. +domain_id: 0 + +# The `unbonded_signer` field specifies the path to the file containing the private key of the signer +unbonded_signer: + type: 'File' + file: '/config/guard-bonded-signer.txt' + +# The `bonded_signer` is the account that will post a bond to the Summit contract. Specify its path to +# the file containing the private key of the signer +bonded_signer: + type: 'File' + file: '/config/guard-bonded-signer.txt' + +# The `submitter_config` field specifies how the submitter should submit messages to the chains. +submitter_config: + chains: + 42: + supports_eip_1559: true + gas_estimate: 7500000 + 43: + gas_bump_percentage: 40 + is_l2: true + gas_estimate: 7500000 + 44: + gas_bump_percentage: 40 + is_l2: true + gas_estimate: 7500000 + +scribe_config: + type: 'remote' + port: 9002 + url: 'scribe' diff --git a/docker/devnet/config/notary43-bonded-signer.txt b/docker/devnet/config/notary43-bonded-signer.txt new file mode 100644 index 0000000000..ab3d6b0c0c --- /dev/null +++ b/docker/devnet/config/notary43-bonded-signer.txt @@ -0,0 +1 @@ +d5c561f92921a5d7eb8a91cc81cb392d1877dcc6b856260c1676cb28ef7203b0 diff --git a/docker/devnet/config/notary43-config.yml b/docker/devnet/config/notary43-config.yml new file mode 100644 index 0000000000..e49ccb16af --- /dev/null +++ b/docker/devnet/config/notary43-config.yml @@ -0,0 +1,73 @@ +# The `db_config` field specifies the database type and the source (either a path or a connection string). +db_config: + # Must be mysql or sqlite. + type: sqlite + # Source is either a path (for sqlite) or a connection string (for mysql). + source: '/config/synapse.db' + +# The base omnirpc url which each chain's collection of RPC's will be proxied through. +base_omnirpc_url: http://omnirpc:9001 +refresh_interval_seconds: 5 + +# For each chain (domain), specify the necessary contracts. +# Remotes need: origin_address, destination_address, light_inbox_address, light_manager_address +# Summit needs: origin_address, destination_address, summit_address, inbox_address, bonding_manager_address +domains: + domain_client43: + domain_id: 43 + type: EVM + required_confirmations: 0 + origin_address: 0xD755a6D98C4557c66ebbD9D76f1BEbC48e84afa0 + destination_address: 0x7219284B26F44B2A584827034422a33450635f7A + light_inbox_address: 0xa6A694d8D2430964DcD11f2E649649Fc9557a56b + light_manager_address: 0x807A8d4469DD803B9b2c9e3568D5E83784bec25D + domain_client44: + domain_id: 44 + type: EVM + required_confirmations: 0 + origin_address: 0xD755a6D98C4557c66ebbD9D76f1BEbC48e84afa0 + destination_address: 0x7219284B26F44B2A584827034422a33450635f7A + light_inbox_address: 0xa6A694d8D2430964DcD11f2E649649Fc9557a56b + light_manager_address: 0x807A8d4469DD803B9b2c9e3568D5E83784bec25D + domain_client42: + domain_id: 42 + type: EVM + required_confirmations: 0 + # for some reason, origin and summit addresses are switched on synchain + origin_address: 0xD755a6D98C4557c66ebbD9D76f1BEbC48e84afa0 + summit_address: 0x1EC96ab1Fdb92565A0839b12d42c13c8135f6c11 + inbox_address: 0x2eB68A0C21413aA78055F6F7F262De56979aFBEa + destination_address: 0x7219284B26F44B2A584827034422a33450635f7A + bonding_manager_address: 0x27006519C5786863fAE35612Da9E0f0622ff8c58 + + +# Specify the summit domain id +summit_domain_id: 42 +# A Notary's `domain_id` is the domain id of the chain it has posted a bond for. +domain_id: 43 + +# The `unbonded_signer` field specifies the path to the file containing the private key of the signer +unbonded_signer: + type: 'File' + file: '/config/notary-bonded-signer.txt' + +# The `bonded_signer` is the account that will post a bond to the Summit contract. Specify its path to +# the file containing the private key of the signer +bonded_signer: + type: 'File' + file: '/config/notary-bonded-signer.txt' + +# The `submitter_config` field specifies how the submitter should submit messages to the chains. +submitter_config: + chains: + 42: + supports_eip_1559: true + gas_estimate: 7500000 + 43: + gas_bump_percentage: 40 + is_l2: true + gas_estimate: 7500000 + 44: + gas_bump_percentage: 40 + is_l2: true + gas_estimate: 7500000 diff --git a/docker/devnet/config/notary44-bonded-signer.txt b/docker/devnet/config/notary44-bonded-signer.txt new file mode 100644 index 0000000000..b4cfedc0af --- /dev/null +++ b/docker/devnet/config/notary44-bonded-signer.txt @@ -0,0 +1 @@ +f466f6f4d2d61a11eddd10eb80aae500c7601539d08d1d55f9e5efe25ecf95bc diff --git a/docker/devnet/config/notary44-config.yml b/docker/devnet/config/notary44-config.yml new file mode 100644 index 0000000000..9b9c87313a --- /dev/null +++ b/docker/devnet/config/notary44-config.yml @@ -0,0 +1,73 @@ +# The `db_config` field specifies the database type and the source (either a path or a connection string). +db_config: + # Must be mysql or sqlite. + type: sqlite + # Source is either a path (for sqlite) or a connection string (for mysql). + source: '/config/synapse.db' + +# The base omnirpc url which each chain's collection of RPC's will be proxied through. +base_omnirpc_url: http://omnirpc:9001 +refresh_interval_seconds: 5 + +# For each chain (domain), specify the necessary contracts. +# Remotes need: origin_address, destination_address, light_inbox_address, light_manager_address +# Summit needs: origin_address, destination_address, summit_address, inbox_address, bonding_manager_address +domains: + domain_client43: + domain_id: 43 + type: EVM + required_confirmations: 0 + origin_address: 0xD755a6D98C4557c66ebbD9D76f1BEbC48e84afa0 + destination_address: 0x7219284B26F44B2A584827034422a33450635f7A + light_inbox_address: 0xa6A694d8D2430964DcD11f2E649649Fc9557a56b + light_manager_address: 0x807A8d4469DD803B9b2c9e3568D5E83784bec25D + domain_client44: + domain_id: 44 + type: EVM + required_confirmations: 0 + origin_address: 0xD755a6D98C4557c66ebbD9D76f1BEbC48e84afa0 + destination_address: 0x7219284B26F44B2A584827034422a33450635f7A + light_inbox_address: 0xa6A694d8D2430964DcD11f2E649649Fc9557a56b + light_manager_address: 0x807A8d4469DD803B9b2c9e3568D5E83784bec25D + domain_client42: + domain_id: 42 + type: EVM + required_confirmations: 0 + # for some reason, origin and summit addresses are switched on synchain + origin_address: 0xD755a6D98C4557c66ebbD9D76f1BEbC48e84afa0 + summit_address: 0x1EC96ab1Fdb92565A0839b12d42c13c8135f6c11 + inbox_address: 0x2eB68A0C21413aA78055F6F7F262De56979aFBEa + destination_address: 0x7219284B26F44B2A584827034422a33450635f7A + bonding_manager_address: 0x27006519C5786863fAE35612Da9E0f0622ff8c58 + + +# Specify the summit domain id +summit_domain_id: 42 +# A Notary's `domain_id` is the domain id of the chain it has posted a bond for. +domain_id: 44 + +# The `unbonded_signer` field specifies the path to the file containing the private key of the signer +unbonded_signer: + type: 'File' + file: '/config/notary-bonded-signer.txt' + +# The `bonded_signer` is the account that will post a bond to the Summit contract. Specify its path to +# the file containing the private key of the signer +bonded_signer: + type: 'File' + file: '/config/notary-bonded-signer.txt' + +# The `submitter_config` field specifies how the submitter should submit messages to the chains. +submitter_config: + chains: + 42: + supports_eip_1559: true + gas_estimate: 7500000 + 43: + gas_bump_percentage: 40 + is_l2: true + gas_estimate: 7500000 + 44: + gas_bump_percentage: 40 + is_l2: true + gas_estimate: 7500000 diff --git a/docker/devnet/config/omnirpc.yaml b/docker/devnet/config/omnirpc.yaml new file mode 100644 index 0000000000..7080df1b18 --- /dev/null +++ b/docker/devnet/config/omnirpc.yaml @@ -0,0 +1,17 @@ +chains: + 42: + rpcs: + - http://chain_a:8545/ + confirmations: 1 + 43: + rpcs: + - http://chain_b:8545/ + confirmations: 1 + 44: + rpcs: + - http://chain_c:8545/ + confirmations: 1 +# port to run on +port: 9001 +# expressed in seconds +refreshInterval: 60 diff --git a/docker/devnet/config/prometheus/prometheus.yml b/docker/devnet/config/prometheus/prometheus.yml new file mode 100644 index 0000000000..d4863f95c7 --- /dev/null +++ b/docker/devnet/config/prometheus/prometheus.yml @@ -0,0 +1,17 @@ +global: + scrape_interval: 10s + scrape_timeout: 10s + +scrape_configs: + - job_name: services + metrics_path: /metrics + static_configs: + - targets: + - 'prometheus:9090' + - 'scribe-indexer:8080' + - 'scribe:8080' + - 'omnirpc:8080' + - 'notary-43:8080' + - 'notary-44:8080' + - 'executor:8080' + diff --git a/docker/devnet/config/scribe-indexer-config.yaml b/docker/devnet/config/scribe-indexer-config.yaml new file mode 100644 index 0000000000..6aefac8b38 --- /dev/null +++ b/docker/devnet/config/scribe-indexer-config.yaml @@ -0,0 +1,47 @@ +chains: + - chain_id: 42 + required_confirmations: 0 + contract_sub_chunk_size: 512 + contract_chunk_size: 512 + store_concurrency_threshold: 100000 + contracts: + - address: 0xD755a6D98C4557c66ebbD9D76f1BEbC48e84afa0 + start_block: 1 + - address: 0x1EC96ab1Fdb92565A0839b12d42c13c8135f6c11 + start_block: 1 + - address: 0x2eB68A0C21413aA78055F6F7F262De56979aFBEa + start_block: 1 + - address: 0x7219284B26F44B2A584827034422a33450635f7A + start_block: 1 + - address: 0x27006519C5786863fAE35612Da9E0f0622ff8c58 + start_block: 1 + - chain_id: 43 + required_confirmations: 0 + contract_sub_chunk_size: 512 + contract_chunk_size: 512 + store_concurrency_threshold: 100000 + contracts: + - address: 0xD755a6D98C4557c66ebbD9D76f1BEbC48e84afa0 + start_block: 1 + - address: 0x7219284B26F44B2A584827034422a33450635f7A + start_block: 1 + - address: 0xa6A694d8D2430964DcD11f2E649649Fc9557a56b + start_block: 1 + - address: 0x807A8d4469DD803B9b2c9e3568D5E83784bec25D + start_block: 1 + - chain_id: 44 + required_confirmations: 0 + contract_sub_chunk_size: 512 + contract_chunk_size: 512 + store_concurrency_threshold: 100000 + contracts: + - address: 0xD755a6D98C4557c66ebbD9D76f1BEbC48e84afa0 + start_block: 1 + - address: 0x7219284B26F44B2A584827034422a33450635f7A + start_block: 1 + - address: 0xa6A694d8D2430964DcD11f2E649649Fc9557a56b + start_block: 1 + - address: 0x807A8d4469DD803B9b2c9e3568D5E83784bec25D + start_block: 1 +rpc_url: 'http://omnirpc:9001/confirmations' +refresh_rate: 0 diff --git a/docker/devnet/docker-compose.yml b/docker/devnet/docker-compose.yml new file mode 100644 index 0000000000..26f2056236 --- /dev/null +++ b/docker/devnet/docker-compose.yml @@ -0,0 +1,267 @@ +version: '3.4' + +# This compose file is expected to be used from make and not independently. + +volumes: + chain_a_data: + chain_b_data: + chain_c_data: + guard_data: + notary43_data: + scribe_data: + prometheus-data: + badger-data: + notary44_data: + executor_data: + + +services: + guard: + restart: unless-stopped + entrypoint: agents + depends_on: + - omnirpc + - scribe + build: + context: ../../ + dockerfile: docker/devnet/agents.Dockerfile + volumes: + - '${PWD}/config/guard-config.yml:/config/guard-config.yml' + - '${PWD}/config/guard-bonded-signer.txt:/config/guard-bonded-signer.txt' + - guard_data:/data + command: + - 'guard-run' + - '--config' + - '/config/guard-config.yml' + environment: + METRICS_HANDLER: jaeger + JAEGER_ENDPOINT: 'http://tracing:14268/api/traces' + GOLOG_LOG_LEVEL: debug + + notary-43: + entrypoint: agents + restart: unless-stopped + depends_on: + - omnirpc + build: + context: ../../ + dockerfile: docker/devnet/agents.Dockerfile + volumes: + - '${PWD}/config/notary43-config.yml:/config/notary-config.yml' + - '${PWD}/config/notary43-bonded-signer.txt:/config/notary-bonded-signer.txt' + - notary43_data:/config/synapse.db + command: + - 'notary-run' + - '--config' + - '/config/notary-config.yml' + - '--debug' + environment: + METRICS_HANDLER: jaeger + JAEGER_ENDPOINT: 'http://tracing:14268/api/traces' + GOLOG_LOG_LEVEL: debug + + + notary-44: + entrypoint: agents + restart: unless-stopped + depends_on: + - omnirpc + build: + context: ../../ + dockerfile: docker/devnet/agents.Dockerfile + volumes: + - '${PWD}/config/notary44-config.yml:/config/notary-config.yml' + - '${PWD}/config/notary44-bonded-signer.txt:/config/notary-bonded-signer.txt' + - notary44_data:/config/synapse.db + command: + - 'notary-run' + - '--config' + - '/config/notary-config.yml' + - '--debug' + environment: + METRICS_HANDLER: jaeger + JAEGER_ENDPOINT: 'http://tracing:14268/api/traces' + GOLOG_LOG_LEVEL: debug + + executor: + entrypoint: agents + restart: unless-stopped + depends_on: + - omnirpc + build: + context: ../../ + dockerfile: docker/devnet/agents.Dockerfile + volumes: + - '${PWD}/config/executor-config.yml:/config/executor-config.yml' + - '${PWD}/config/executor-signer.txt:/config/executor-signer.txt' + - executor_data:/config/synapse.db + command: + - 'executor-run' + - '--config' + - '/config/executor-config.yml' + - '--debug' + environment: + METRICS_HANDLER: jaeger + JAEGER_ENDPOINT: 'http://tracing:14268/api/traces' + GOLOG_LOG_LEVEL: debug + + + scribe-indexer: + depends_on: + - omnirpc + build: + context: ../../ + dockerfile: docker/devnet/scribe.Dockerfile + entrypoint: scribe + restart: unless-stopped + command: > + scribe + --config=/config/scribe.yaml + --db=sqlite + --path=/data + volumes: + - '${PWD}/config/scribe-indexer-config.yaml:/config/scribe.yaml' + - scribe_data:/data + environment: + METRICS_HANDLER: jaeger + JAEGER_ENDPOINT: 'http://tracing:14268/api/traces' + GOLOG_LOG_LEVEL: debug + + scribe: + restart: unless-stopped + depends_on: + - scribe-indexer + build: + context: ../../ + dockerfile: docker/devnet/scribe.Dockerfile + entrypoint: scribe + command: > + server + --port=9002 + --db=sqlite + --path=/data + --omnirpc=http://omnirpc:9001 + volumes: + - scribe_data:/data + environment: + METRICS_HANDLER: jaeger + JAEGER_ENDPOINT: 'http://tracing:14268/api/traces' + GOLOG_LOG_LEVEL: debug + ports: + - '9002:9002' + + + omnirpc: + restart: unless-stopped + depends_on: + - chain_a + - chain_b + - chain_c + - tracing + build: + context: ../../ + dockerfile: docker/devnet/omnirpc.Dockerfile + ports: + - '9001:9001' + command: > + omnirpc + server + --config=/config/omnirpc.yaml + volumes: + - '${PWD}/config/omnirpc.yaml:/config/omnirpc.yaml' + environment: + METRICS_HANDLER: jaeger + JAEGER_ENDPOINT: 'http://tracing:14268/api/traces' +# TODO, consider re-enabling. +# healthcheck: +# test: ['CMD', 'curl', '-f', 'http://localhost:8545/health'] +# interval: 1s +# timeout: 20s +# retries: 10 +# start_period: 5s + + chain_a: + restart: unless-stopped + image: 'ghcr.io/foundry-rs/foundry:latest' + entrypoint: anvil + command: > + --host=0.0.0.0 + --chain-id=42 + --allow-origin='*' + --steps-tracing + --mnemonic='tag volcano eight thank tide danger coast health above argue embrace heavy' + --base-fee=1 + --gas-limit=100000000 + --state=/data/ + ports: + - '8042:8545' + volumes: + - chain_a_data:/data + + chain_b: + restart: unless-stopped + image: 'ghcr.io/foundry-rs/foundry:latest' + entrypoint: anvil + command: > + --host=0.0.0.0 + --chain-id=43 + --allow-origin='*' + --steps-tracing + --mnemonic='tag volcano eight thank tide danger coast health above argue embrace heavy' + --base-fee=1 + --gas-limit=100000000 + --state=/data/ + ports: + - '8043:8545' + volumes: + - chain_b_data:/data + + chain_c: + restart: unless-stopped + image: 'ghcr.io/foundry-rs/foundry:latest' + entrypoint: anvil + command: > + --host=0.0.0.0 + --chain-id=44 + --allow-origin='*' + --steps-tracing + --mnemonic='tag volcano eight thank tide danger coast health above argue embrace heavy' + --base-fee=1 + --gas-limit=100000000 + --state=/data/ + ports: + - '8044:8545' + volumes: + - chain_b_data:/data + + tracing: + restart: unless-stopped + image: jaegertracing/all-in-one:latest + ports: + - 6831:6831/udp + - 6832:6832/udp + - 5778:5778 + - 16686:16686 + - 4317:4317 + - 4318:4318 + - 14250:14250 + - 14268:14268 + - 14269:14269 + - 9411:9411 + environment: + SPAN_STORAGE_TYPE: 'badger' + BADGER_EPHEMERAL: 'false' + BADGER_DIRECTORY_VALUE: '/badger/data' + BADGER_DIRECTORY_KEY: '/badger/key' + volumes: + - badger-data:/badger + + prometheus: + restart: unless-stopped + image: prom/prometheus:v2.46.0 + ports: + - 9000:9090 + volumes: + - '${PWD}/config/prometheus:/etc/prometheus' + - prometheus-data:/prometheus + command: --web.enable-lifecycle --config.file=/etc/prometheus/prometheus.yml diff --git a/docker/devnet/omnirpc.Dockerfile b/docker/devnet/omnirpc.Dockerfile new file mode 100644 index 0000000000..2f65f58c70 --- /dev/null +++ b/docker/devnet/omnirpc.Dockerfile @@ -0,0 +1,25 @@ +FROM ghcr.io/synapsecns/sanguine-goreleaser:latest as builder + +ARG VERSION=v0.0.0 + +COPY ./services /app/services +COPY ./agents /app/agents +COPY ./core /app/core +COPY ./ethergo /app/ethergo +COPY ./tools /app/tools +COPY ./contrib /app/contrib +COPY ./go.work /app/go.work +COPY ./go.work.sum /app/go.work.sum +COPY ./.git /app/.git + +WORKDIR /app/services/omnirpc + +RUN --mount=type=cache,target=/root/go/pkg/mod GOPROXY=https://proxy.golang.org go mod download -x +RUN --mount=type=cache,target=/root/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build CC=gcc CXX=g++ go build -tags=netgo,osusergo -ldflags="-s -w -extldflags '-static'" -o /app/bin/omnirpc main.go + +FROM ubuntu:latest + +RUN apt update && apt install -y bash +COPY --from=builder /app/bin/omnirpc /usr/local/bin + +CMD ["omnirpc"] diff --git a/docker/devnet/scribe.Dockerfile b/docker/devnet/scribe.Dockerfile new file mode 100644 index 0000000000..05c96b60f5 --- /dev/null +++ b/docker/devnet/scribe.Dockerfile @@ -0,0 +1,25 @@ +FROM ghcr.io/synapsecns/sanguine-goreleaser:latest as builder + +ARG VERSION=v0.0.0 + +COPY ./services /app/services +COPY ./agents /app/agents +COPY ./core /app/core +COPY ./ethergo /app/ethergo +COPY ./tools /app/tools +COPY ./contrib /app/contrib +COPY ./go.work /app/go.work +COPY ./go.work.sum /app/go.work.sum +COPY ./.git /app/.git + +WORKDIR /app/services/scribe + +RUN --mount=type=cache,target=/root/go/pkg/mod GOPROXY=https://proxy.golang.org go mod download -x +RUN --mount=type=cache,target=/root/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build CC=gcc CXX=g++ go build -tags=netgo,osusergo -ldflags="-s -w -extldflags '-static'" -o /app/bin/scribe main.go + +FROM ubuntu:latest + +RUN apt update && apt install -y bash sqlite3 htop +COPY --from=builder /app/bin/scribe /usr/local/bin + +CMD ["scribe"] diff --git a/make/solidity.Makefile b/make/solidity.Makefile new file mode 100644 index 0000000000..b6d5ed3199 --- /dev/null +++ b/make/solidity.Makefile @@ -0,0 +1,25 @@ +# Note: this file is made to be symlinked to various folders where we use go for builds +# please use libraries if you wish to add folder-specific make functionality + +default: help + +# set variables +GIT_ROOT := $(shell git rev-parse --show-toplevel) +CURRENT_PATH := $(shell pwd) +RELPATH := $(shell realpath --relative-to="$(GIT_ROOT)" "$(CURRENT_PATH)") + +help: ## This help dialog. + @IFS=$$'\n' ; \ + help_lines=(`fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//'`); \ + for help_line in $${help_lines[@]}; do \ + IFS=$$'#' ; \ + help_split=($$help_line) ; \ + help_command=`echo $${help_split[0]} | sed -e 's/^ *//' -e 's/ *$$//'` ; \ + help_info=`echo $${help_split[2]} | sed -e 's/^ *//' -e 's/ *$$//'` ; \ + printf "%-30s %s\n" $$help_command $$help_info ; \ + done + +# TODO: deployer utils requires jq, install + +foundry-install: + @if [ "$(shell which forge)" = "" ]; then curl -L https://foundry.paradigm.xyz | bash && foundryup; fi diff --git a/packages/contracts-core/.example.env b/packages/contracts-core/.example.env index d27b151ead..bccf0ecfa1 100644 --- a/packages/contracts-core/.example.env +++ b/packages/contracts-core/.example.env @@ -6,4 +6,4 @@ RPC_POLYGON=https://polygon.llamarpc.com ETHERSCAN_AVALANCHE_KEY= ETHERSCAN_OPTIMISM_KEY= -ETHERSCAN_POLYGON_KEY= \ No newline at end of file +ETHERSCAN_POLYGON_KEY= diff --git a/packages/contracts-core/Makefile b/packages/contracts-core/Makefile new file mode 100644 index 0000000000..973a998067 --- /dev/null +++ b/packages/contracts-core/Makefile @@ -0,0 +1,32 @@ +include ../../make/solidity.Makefile + +devnet-clean: ## Delete all devnet data and docker containers + rm -rf deployments/chain_a/*.json + rm -rf deployments/chain_b/*.json + rm -rf deployments/chain_c/*.json + + # TODO: this should also delete broadcast/**/42, broadcast/**/43, broadcast/**/44 + rm -rf broadcast/42/* + rm -rf broadcast/43/* + rm -rf broadcast/44/* + + cd ../../docker/devnet && docker-compose down --volumes + +devnet-up: ## This should be run to start the devnet docker-containers. This does not deploy anything. + cd ../../docker/devnet && docker-compose up -d --build + +devnet-deploy: ## This should be run exactly once to deploy the contracts to the devnet. + # backup the .env in case there's real data in there + cp .env /tmp/.env.bak || true + cp devnet.env .env + + forge script script/DeployMessaging003SynChain.s.sol --ffi -f chain_a --private-key 63e21d10fd50155dbba0e7d3f7431a400b84b4c2ac1ee38872f82448fe3ecfb9 --broadcast + forge script script/DeployMessaging003LightChain.s.sol --ffi -f chain_b --private-key 63e21d10fd50155dbba0e7d3f7431a400b84b4c2ac1ee38872f82448fe3ecfb9 --broadcast + forge script script/DeployMessaging003LightChain.s.sol --ffi -f chain_c --private-key 63e21d10fd50155dbba0e7d3f7431a400b84b4c2ac1ee38872f82448fe3ecfb9 --broadcast + + forge script script/DeployClients003.s.sol --ffi -f chain_a --private-key 63e21d10fd50155dbba0e7d3f7431a400b84b4c2ac1ee38872f82448fe3ecfb9 --broadcast + forge script script/DeployClients003.s.sol --ffi -f chain_b --private-key 63e21d10fd50155dbba0e7d3f7431a400b84b4c2ac1ee38872f82448fe3ecfb9 --broadcast + forge script script/DeployClients003.s.sol --ffi -f chain_c --private-key 63e21d10fd50155dbba0e7d3f7431a400b84b4c2ac1ee38872f82448fe3ecfb9 --broadcast + +devnet-logs: + cd ../../docker/devnet && docker-compose logs -f diff --git a/packages/contracts-core/README.md b/packages/contracts-core/README.md index c781183319..0385c4baaa 100644 --- a/packages/contracts-core/README.md +++ b/packages/contracts-core/README.md @@ -39,3 +39,34 @@ root ├── script: Scripts for deploying + interacting with contracts ├── test: Test contracts + + +## Running a devnet + + + +To run a devnet, you can run `make devnet-up` and `make devnet-deploy` from this directory. This will start a local devnet and deploy the contracts to it. RPC endpoints for debugging etc will be availabe at `http://localhost:9001/rpc/[chain_id]`. + +By default, the [`PingPongClient.sol`](contracts/client/PingPongClient.sol) is deployed, so you can interact with it with cast. For instance, to send a ping from chain 42 to chain 44: + +```bash +cast send 0x521F44132489CDD54c9ceC8167CfC377CbAEa351 --rpc-url http://localhost:9001/rpc/43 --private-key 0x526db1890baf94e82162f17f25ad769eb7f981272d8d99c527ea1af443c2d0cc "doPing(uint32,address,uint16)" 42 0x521F44132489CDD54c9ceC8167CfC377CbAEa351 44 +``` + +Now, to make sure it work, you can pull up [scribe](../../services/scribe/) by going to http://localhost:9002/graphiql and [querying the logs](http://localhost:9002/graphiql?query=%7B%0A%20%20%0A%20%20logs(chain_id%3A%2044%2C%20page%3A%201)%7B%0A%20%20%20%20topics%0A%20%20%20%20receipt%0A%20%20%20%20block_number%0A%20%20%7D%0A%7D) for chain 44: + + +```graphql +{ + + logs(chain_id: 44, page: 1){ + topics + block_number + contract_address + } +} +``` + +If everything went well, you will see topic `0x0a72872b9cfe43d6c13b13553f28d4879e427f3b456545649fd0761fdcbe0311` in the logs, which is the topic for the `PingPongClient`'s `Pong` event. + +![graphql screenshot](./assets/screenshot.png) diff --git a/packages/contracts-core/assets/screenshot.png b/packages/contracts-core/assets/screenshot.png new file mode 100644 index 0000000000..c8c776396c Binary files /dev/null and b/packages/contracts-core/assets/screenshot.png differ diff --git a/packages/contracts-core/contracts/create3/CREATE3Factory.sol b/packages/contracts-core/contracts/create3/CREATE3Factory.sol new file mode 100644 index 0000000000..38601b7c5f --- /dev/null +++ b/packages/contracts-core/contracts/create3/CREATE3Factory.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import {CREATE3} from "solmate/utils/CREATE3.sol"; + +import {ICREATE3Factory} from "./ICREATE3Factory.sol"; + +/// @title Factory for deploying contracts to deterministic addresses via CREATE3 +/// @author zefram.eth +/// @notice Enables deploying contracts using CREATE3. Each deployer (msg.sender) has +/// its own namespace for deployed addresses. +contract CREATE3Factory is ICREATE3Factory { + /// @inheritdoc ICREATE3Factory + function deploy(bytes32 salt, bytes memory creationCode) + external + payable + override + returns (address deployed) + { + // hash salt with the deployer address to give each deployer its own namespace + salt = keccak256(abi.encodePacked(msg.sender, salt)); + return CREATE3.deploy(salt, creationCode, msg.value); + } + + /// @inheritdoc ICREATE3Factory + function getDeployed(address deployer, bytes32 salt) + external + view + override + returns (address deployed) + { + // hash salt with the deployer address to give each deployer its own namespace + salt = keccak256(abi.encodePacked(deployer, salt)); + return CREATE3.getDeployed(salt); + } +} diff --git a/packages/contracts-core/contracts/create3/ICREATE3Factory.sol b/packages/contracts-core/contracts/create3/ICREATE3Factory.sol new file mode 100644 index 0000000000..8c2c66471f --- /dev/null +++ b/packages/contracts-core/contracts/create3/ICREATE3Factory.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity >=0.6.0; + +/// @title Factory for deploying contracts to deterministic addresses via CREATE3 +/// @author zefram.eth +/// @notice Enables deploying contracts using CREATE3. Each deployer (msg.sender) has +/// its own namespace for deployed addresses. +interface ICREATE3Factory { + /// @notice Deploys a contract using CREATE3 + /// @dev The provided salt is hashed together with msg.sender to generate the final salt + /// @param salt The deployer-specific salt for determining the deployed contract's address + /// @param creationCode The creation code of the contract to deploy + /// @return deployed The address of the deployed contract + function deploy(bytes32 salt, bytes memory creationCode) + external + payable + returns (address deployed); + + /// @notice Predicts the address of a deployed contract + /// @dev The provided salt is hashed together with the deployer address to generate the final salt + /// @param deployer The deployer account that will call deploy() + /// @param salt The deployer-specific salt for determining the deployed contract's address + /// @return deployed The address of the contract that will be deployed + function getDeployed(address deployer, bytes32 salt) + external + view + returns (address deployed); +} diff --git a/packages/contracts-core/deployments/chain_a/.gitignore b/packages/contracts-core/deployments/chain_a/.gitignore new file mode 100644 index 0000000000..7dc54a519e --- /dev/null +++ b/packages/contracts-core/deployments/chain_a/.gitignore @@ -0,0 +1,5 @@ +# Ignore everything +* + +# But not this file +!.gitignore diff --git a/packages/contracts-core/deployments/chain_b/.gitignore b/packages/contracts-core/deployments/chain_b/.gitignore new file mode 100644 index 0000000000..7dc54a519e --- /dev/null +++ b/packages/contracts-core/deployments/chain_b/.gitignore @@ -0,0 +1,5 @@ +# Ignore everything +* + +# But not this file +!.gitignore diff --git a/packages/contracts-core/deployments/chain_c/.gitignore b/packages/contracts-core/deployments/chain_c/.gitignore new file mode 100644 index 0000000000..7dc54a519e --- /dev/null +++ b/packages/contracts-core/deployments/chain_c/.gitignore @@ -0,0 +1,5 @@ +# Ignore everything +* + +# But not this file +!.gitignore diff --git a/packages/contracts-core/devnet.env b/packages/contracts-core/devnet.env new file mode 100644 index 0000000000..2700cd1789 --- /dev/null +++ b/packages/contracts-core/devnet.env @@ -0,0 +1,11 @@ +MESSAGING_DEPLOYER_PRIVATE_KEY=63e21d10fd50155dbba0e7d3f7431a400b84b4c2ac1ee38872f82448fe3ecfb9 + +RPC_AVALANCHE=https://api.avax.network/ext/bc/C/rpc +RPC_OPTIMISM=https://1rpc.io/op +RPC_POLYGON=https://polygon.llamarpc.com + +ETHERSCAN_AVALANCHE_KEY= +ETHERSCAN_OPTIMISM_KEY= +ETHERSCAN_POLYGON_KEY= + +DEVNET=true diff --git a/packages/contracts-core/foundry.toml b/packages/contracts-core/foundry.toml index f6f52240ea..f6be41d854 100644 --- a/packages/contracts-core/foundry.toml +++ b/packages/contracts-core/foundry.toml @@ -25,6 +25,11 @@ polygon = "${RPC_POLYGON}" arb_goerli = "${RPC_ARB_GOERLI}" op_goerli = "${RPC_OP_GOERLI}" sepolia = "${RPC_SEPOLIA}" +# devnet chains +chain_a = "http://localhost:9001/rpc/42" +chain_b = "http://localhost:9001/rpc/43" +chain_c = "http://localhost:9001/rpc/44" + [etherscan] avalanche = { key = "${ETHERSCAN_AVALANCHE_KEY}" } diff --git a/packages/contracts-core/lib/solmate b/packages/contracts-core/lib/solmate new file mode 160000 index 0000000000..fadb2e2778 --- /dev/null +++ b/packages/contracts-core/lib/solmate @@ -0,0 +1 @@ +Subproject commit fadb2e2778adbf01c80275bfb99e5c14969d964b diff --git a/packages/contracts-core/script/DeployCREATE3.sol b/packages/contracts-core/script/DeployCREATE3.sol new file mode 100644 index 0000000000..e2074b6933 --- /dev/null +++ b/packages/contracts-core/script/DeployCREATE3.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import {console, stdJson} from "forge-std/Script.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; + +// ═════════════════════════════ INTERNAL IMPORTS ══════════════════════════════ +import {DeployerUtils} from "./utils/DeployerUtils.sol"; + +import {CREATE3Factory} from "../contracts/create3/CREATE3Factory.sol"; + + +// TODO: remove this script, I don't think we need it since we handle the devnet create3 deploy in setupDevnetIfEnabled(); +contract DeployCREATE3 is DeployerUtils { + using stdJson for string; + using Strings for uint256; + constructor() { + setupPK("MESSAGING_DEPLOYER_PRIVATE_KEY"); + setupDevnetIfEnabled(); + } + + function run() external { + startBroadcast(true); + CREATE3Factory NewFactory = new CREATE3Factory(); + saveDeployment("CREATE3", "CREATE3", address(NewFactory), "0x"); + stopBroadcast(); + } +} diff --git a/packages/contracts-core/script/DeployClients003.s.sol b/packages/contracts-core/script/DeployClients003.s.sol index 491f8577d6..d8d30e90e5 100644 --- a/packages/contracts-core/script/DeployClients003.s.sol +++ b/packages/contracts-core/script/DeployClients003.s.sol @@ -26,6 +26,7 @@ contract DeployClients003Script is DeployerUtils { constructor() { setupPK("MESSAGING_DEPLOYER_PRIVATE_KEY"); + setupDevnetIfEnabled(); } /// @dev Function to exclude script from coverage report diff --git a/packages/contracts-core/script/DeployMessaging003Base.s.sol b/packages/contracts-core/script/DeployMessaging003Base.s.sol index 450c1f8d78..c62018ca59 100644 --- a/packages/contracts-core/script/DeployMessaging003Base.s.sol +++ b/packages/contracts-core/script/DeployMessaging003Base.s.sol @@ -47,6 +47,7 @@ abstract contract DeployMessaging003BaseScript is DeployerUtils { setupPK("MESSAGING_DEPLOYER_PRIVATE_KEY"); localDomain = uint32(block.chainid); deploymentSalt = keccak256("Messaging003"); + setupDevnetIfEnabled(); } /// @dev Function to exclude script from coverage report @@ -86,6 +87,8 @@ abstract contract DeployMessaging003BaseScript is DeployerUtils { globalConfig = loadGlobalDeployConfig("Messaging003"); synapseDomain = globalConfig.readUint(".chainidSummit"); startBroadcast(_isBroadcasted); + // assert this is the first thing deployed + getFactory(); // Predict deployments agentManager = predictFactoryDeployment(agentManagerName()); statementInbox = predictFactoryDeployment(statementInboxName()); diff --git a/packages/contracts-core/script/SetupGasOracle003.s.sol b/packages/contracts-core/script/SetupGasOracle003.s.sol index c0dfb44a30..404b50aa44 100644 --- a/packages/contracts-core/script/SetupGasOracle003.s.sol +++ b/packages/contracts-core/script/SetupGasOracle003.s.sol @@ -47,6 +47,7 @@ contract SetupGasOracle003Script is DeployerUtils { /// @dev To simulate setup on $chainName /// forge script script/SetupGasOracle003.s.sol -f chainName function run() external { + setupDevnetIfEnabled(); startBroadcast(true); gasDataConfig = loadGlobalDeployConfig("Messaging003GasData"); gasOracle = GasOracle(loadDeployment(GAS_ORACLE)); diff --git a/packages/contracts-core/script/configs/devnet/Messaging003.dc.json b/packages/contracts-core/script/configs/devnet/Messaging003.dc.json new file mode 100644 index 0000000000..550a797ebf --- /dev/null +++ b/packages/contracts-core/script/configs/devnet/Messaging003.dc.json @@ -0,0 +1,10 @@ +{ + "agents": { + "0": ["0x8230645aC28A4EdD1b0B53E7Cd8019744E9dD559"], + "43": ["0x65c150B7eF3B1adbB9cB2b8041C892b15eDde05A"], + "44": ["0x1AebbE69459B80d4975259378577Bc01d2924Cf4"] + }, + "chainidSummit": 42, + "domains": [0, 43, 44], + "owner": "0xC49926C4124cEe1cbA0Ea94Ea31a6c12318df947" +} diff --git a/packages/contracts-core/script/configs/devnet/Messaging003AgentRoot.dc.json b/packages/contracts-core/script/configs/devnet/Messaging003AgentRoot.dc.json new file mode 100644 index 0000000000..0fe837b3aa --- /dev/null +++ b/packages/contracts-core/script/configs/devnet/Messaging003AgentRoot.dc.json @@ -0,0 +1,17 @@ +{ + "initialAgentRoot": "0x9217e3148f2955ca87ef2b50a553745175a8346a0f46b0ec01f032f3c9aeb8e5", + "proofs": { + "0x1AebbE69459B80d4975259378577Bc01d2924Cf4": [ + "0x233c546cc7740219a8b86762a818d32bbd44b31dcf0feb8bb5dd8252b4bcccae", + "0x341965897855fc9b044cf7adfaacb1e911e1b556915a9d6466624a54272b547f" + ], + "0x65c150B7eF3B1adbB9cB2b8041C892b15eDde05A": [ + "0x58f00a9902fe04863e49cca3696496c64a1e00d71994571fa4e9fb78b6713872", + "0x341965897855fc9b044cf7adfaacb1e911e1b556915a9d6466624a54272b547f" + ], + "0x8230645aC28A4EdD1b0B53E7Cd8019744E9dD559": [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xd3187c6dce38b7b0553221b12b240ed86b9eafc8d9dc9d1be6658c7d3678bc51" + ] + } +} \ No newline at end of file diff --git a/packages/contracts-core/script/configs/devnet/README.md b/packages/contracts-core/script/configs/devnet/README.md new file mode 100644 index 0000000000..bbc65c1a5b --- /dev/null +++ b/packages/contracts-core/script/configs/devnet/README.md @@ -0,0 +1,13 @@ +# Configs + +[Messaging003AgentRoot.dc.json](Messaging003AgentRoot.dc.json) is automatically generated by the deploy script and contains proofs of the agent root representing the current state of each agent on each chain. +[Messaging003.dc.json](Messaging003.dc.json) contains the configuration for the Messaging003 contract. + agents: + - 0: address of the guard(s). + - other chains: address of the notary/ies for that chain + chainIdSummit: chainId of the Summit chain + owner: address of the owner of the contracts. Should match deployer address for devnet. + +_Note: Synchain needs no notary_, instead every Notary signature is valid on their remote chain and SynChain at the same time. +This way they could both submit snapshots to Summit, and attestations to the remote chain. + diff --git a/packages/contracts-core/script/utils/DeployerUtils.sol b/packages/contracts-core/script/utils/DeployerUtils.sol index 3c74d97053..18de6b89ac 100644 --- a/packages/contracts-core/script/utils/DeployerUtils.sol +++ b/packages/contracts-core/script/utils/DeployerUtils.sol @@ -5,6 +5,8 @@ import {console, Script, stdJson} from "forge-std/Script.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; +import {CREATE3Factory} from "../../contracts/create3/CREATE3Factory.sol"; + interface ICreate3Factory { function deploy(bytes32 salt, bytes memory creationCode) external payable returns (address deployed); @@ -21,10 +23,14 @@ contract DeployerUtils is Script { /// @dev Path to artifacts, deployments and configs directories string private constant ARTIFACTS = "artifacts/"; string private constant DEPLOYMENTS = "deployments/"; - string private constant DEPLOY_CONFIGS = "script/configs/"; + string private DEPLOY_CONFIGS = "script/configs/"; - // TODO: this is only deployed on 7 chains, deploy our own factory for prod deployments - ICreate3Factory internal constant FACTORY = ICreate3Factory(0x9fBB3DF7C40Da2e5A0dE984fFE2CCB7C47cd0ABf); + // @dev wether or not devnet is enabled + bool private DEVNET_ENABLED = false; + // @dev env var for wether or not devnet is in use + string private constant DEVNET_ENABLED_VAR = "DEVNET"; + // @dev artifacted path to use if devnet is enabled; + string private constant DEPLOY_CONFIGS_DEVNET = "script/configs/devnet/"; /// @dev Whether the script will be broadcasted or not bool internal isBroadcasted = false; @@ -35,6 +41,11 @@ contract DeployerUtils is Script { uint256 internal broadcasterPK; address internal broadcasterAddress; + // TODO: this should be set from one of the config files. + // default to original value in the git diff. Get @chitimeschi to review + // @dev: use getFactory() to get the factory + ICreate3Factory private factory = ICreate3Factory(0x6438CB36cb18520774EfC7A172410D8BBBe9a428); + bytes32 internal deploymentSalt; /// @notice Prevents this contract from being included in the coverage report @@ -54,6 +65,49 @@ contract DeployerUtils is Script { isBroadcasted = isBroadcasted_; } + // @dev this is called just in time so we can make sure that startBroadcast() is called before this. + // it's only overriden and deployed just-in-time in the case of devnet + // TODO: this pattern sucks. It introduces potential unexpected behavior if the dev calls factory. directly. + // It's also slow. + function getFactory() internal returns (ICreate3Factory) { + if (!DEVNET_ENABLED) { + return factory; + } + + address factoryDeployment = tryLoadDeployment("Create3Factory"); + if (factoryDeployment == address(0)) { + if (broadcasterPK == 0){ + console.log("please setup a private key before calling this function"); + } + + console.log("Create3Factory not deployed on devnet, deploying now"); + CREATE3Factory NewFactory = new CREATE3Factory(); + saveDeployment("Create3Factory", "Create3Factory", address(NewFactory), "0x"); + factoryDeployment = address(NewFactory); + + } + factory = ICreate3Factory(factoryDeployment); + return factory; + } + + // @dev must be called after setupPK() + function setupDevnetIfEnabled() public { + DEVNET_ENABLED = vm.envOr(DEVNET_ENABLED_VAR, false); + + if (DEVNET_ENABLED) { + DEVNET_ENABLED = true; + // setup the chains + setChain("chain_a", Chain("chain_a", 42, "chain_a", "http://localhost:9001/rpc/42")); + setChain("chain_b", Chain("chain_b", 43, "chain_b", "http://localhost:9001/rpc/43")); + setChain("chain_c", Chain("chain_c", 44, "chain_c", "http://localhost:9001/rpc/44")); + + // override the configs path + DEPLOY_CONFIGS = DEPLOY_CONFIGS_DEVNET; + + chainAlias = getChainAlias(); + } + } + function setupDeployerPK() public { setupPK("DEPLOYER_PRIVATE_KEY"); } @@ -83,8 +137,8 @@ contract DeployerUtils is Script { internal returns (address deployment) { - require(Address.isContract(address(FACTORY)), "Factory not deployed"); - deployment = FACTORY.deploy( + require(Address.isContract(address(getFactory())), "Factory not deployed"); + deployment = getFactory().deploy( getDeploymentSalt(contractName), // salt abi.encodePacked(creationCode, constructorArgs) // creation code with appended constructor args ); @@ -97,9 +151,10 @@ contract DeployerUtils is Script { } /// @notice Predicts the deployment address for a contract. - function predictFactoryDeployment(string memory contractName) internal view returns (address) { - require(Address.isContract(address(FACTORY)), "Factory not deployed"); - return FACTORY.getDeployed(broadcasterAddress, getDeploymentSalt(contractName)); + function predictFactoryDeployment(string memory contractName) internal returns (address) { + ICreate3Factory _factory = getFactory(); + require(Address.isContract(address(_factory)), "Factory not deployed"); + return _factory.getDeployed(broadcasterAddress, getDeploymentSalt(contractName)); } /// @notice Deploys the contract and saves the deployment artifact @@ -203,7 +258,7 @@ contract DeployerUtils is Script { /// @notice Loads deploy config for a given contract on the current chain. /// Will revert if config doesn't exist. - function loadDeployConfig(string memory contractName) public view returns (string memory json) { + function loadDeployConfig(string memory contractName) public returns (string memory json) { return vm.readFile(deployConfigPath(contractName)); } @@ -218,7 +273,7 @@ contract DeployerUtils is Script { /// @notice Loads deploy config for a given contract on the current chain. /// Will revert if config doesn't exist. - function loadGlobalDeployConfig(string memory contractName) public view returns (string memory json) { + function loadGlobalDeployConfig(string memory contractName) public returns (string memory json) { return vm.readFile(globalDeployConfigPath(contractName)); } @@ -246,13 +301,13 @@ contract DeployerUtils is Script { } /// @notice Returns path to the contract deploy config JSON on the current chain. - function deployConfigPath(string memory contractName) public view returns (string memory path) { + function deployConfigPath(string memory contractName) public returns (string memory path) { require(bytes(chainAlias).length != 0, "Chain not set"); return string.concat(DEPLOY_CONFIGS, chainAlias, "/", deployConfigFn(contractName)); } /// @notice Returns path to the global contract deploy config JSON. - function globalDeployConfigPath(string memory contractName) public pure returns (string memory path) { + function globalDeployConfigPath(string memory contractName) public returns (string memory path) { return string.concat(DEPLOY_CONFIGS, deployConfigFn(contractName)); } diff --git a/services/scribe/cmd/commands.go b/services/scribe/cmd/commands.go index a5a551f778..cf7853055b 100644 --- a/services/scribe/cmd/commands.go +++ b/services/scribe/cmd/commands.go @@ -88,6 +88,7 @@ func createScribeParameters(c *cli.Context) (eventDB db.EventDB, clients map[uin } var scribeCommand = &cli.Command{ + // TODO: rename this command to indexer Name: "scribe", Description: "scribe runs the scribe, livefilling across all specified chains", Flags: []cli.Flag{configFlag, dbFlag, pathFlag}, diff --git a/services/scribe/db/datastore/sql/base/receipt.go b/services/scribe/db/datastore/sql/base/receipt.go index 3bfdc21f1b..6f86839a1c 100644 --- a/services/scribe/db/datastore/sql/base/receipt.go +++ b/services/scribe/db/datastore/sql/base/receipt.go @@ -123,7 +123,7 @@ func (s Store) RetrieveReceiptsWithFilter(ctx context.Context, receiptFilter db. if err != nil { return []types.Receipt{}, fmt.Errorf("could not build receipts from db receipts: %w", err) } - logger.Infof("[RECEIPT QUERY] Retrieved %d receipts with filter %+v", len(parsedReceipts), receiptFilter) + return parsedReceipts, nil } @@ -180,7 +180,6 @@ func (s Store) buildReceiptsFromDBReceipts(ctx context.Context, dbReceipts []Rec page++ logs = append(logs, logGroup...) } - logger.Infof("[RECEIPT QUERY] logs collected: %d, %v, page: %d", len(logs), logFilter, page) parsedReceipt := types.Receipt{ Type: dbReceipt.Type, @@ -196,11 +195,9 @@ func (s Store) buildReceiptsFromDBReceipts(ctx context.Context, dbReceipts []Rec BlockNumber: big.NewInt(int64(dbReceipt.BlockNumber)), TransactionIndex: uint(dbReceipt.TransactionIndex), } - logger.Infof("[RECEIPT QUERY] parsedReceipt:, %v", parsedReceipt) receipts = append(receipts, parsedReceipt) } - logger.Infof("[RECEIPT QUERY] parsedReceipt: %d", len(receipts)) return receipts, nil }