diff --git a/Dockerfile.evm b/Dockerfile.evm deleted file mode 100644 index ad3e0e9..0000000 --- a/Dockerfile.evm +++ /dev/null @@ -1,38 +0,0 @@ -FROM ghcr.io/foundry-rs/foundry:v1.3.4@sha256:3afb57dcd8f06e098d643d04e0b541ef83a1edf94c0a80ea5e89329ec50ccd92 AS builder - -# Foundry image runs as foundry user by default -# We need root to both run apt and to write files to the filesystem -USER root -RUN apt update && apt install -y jq wget - -# Preflight - -WORKDIR /app -COPY foundry.toml foundry.toml -COPY --link lib/wormhole-solidity-sdk lib/wormhole-solidity-sdk -COPY --link src/evm src/evm - -# Prepare compiler input. NOTE: jq must be pre-applied to "clean" the output from forge. -# Otherwise solc aborts with duplicated key/newline problems. - -RUN forge verify-contract --show-standard-json-input 0x0000000000000000000000000000000000000000 src/evm/WormholeVerifier.sol:WormholeVerifier | jq '.' > WormholeVerifier.input.json - -# Get compiler according to forge configuration (foundry.toml specified) - -RUN SOLC_VERSION=$(forge config | grep "^solc =" | sed 's/solc = //' | sed 's/"//g'); \ - if [ -z $SOLC_VERSION ]; then echo "SOLC_VERSION not set"; exit 1; fi; \ - wget --output-document=solc https://github.com/ethereum/solidity/releases/download/v$SOLC_VERSION/solc-static-linux && chmod +x solc - -# Compile contract(s). - -RUN ./solc --standard-json WormholeVerifier.input.json > WormholeVerifier.output.json && \ - SOLC_ERR=$(jq '.errors[]? | select(.severity == "error")' WormholeVerifier.output.json) && \ - if [ ! -z "$SOLC_ERR" ]; then \ - echo "Error detected during solc execution."; \ - echo $SOLC_ERR; \ - exit 2; \ - fi - -# Consolidate all generated output -FROM scratch AS foundry-export -COPY --from=builder /app/*.input.json /app/*.output.json ./ diff --git a/Makefile b/Makefile index e57ed3f..b22f8b0 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ build-evm: dependencies-evm forge build verifiable-build-evm: dependencies-evm - mkdir -p verifiable-evm-build && docker build --file ./Dockerfile.evm . --tag verification-v2-evm-build --output type=local,dest=verifiable-evm-build + mkdir -p verifiable-evm-build && docker build --file ./evm-build.Dockerfile . --tag verification-v2-evm-build --output type=local,dest=verifiable-evm-build test-evm: dependencies-evm forge test diff --git a/evm-build.Dockerfile b/evm-build.Dockerfile new file mode 100644 index 0000000..9c1acee --- /dev/null +++ b/evm-build.Dockerfile @@ -0,0 +1,49 @@ +FROM ghcr.io/foundry-rs/foundry:v1.5.1@sha256:3a70bfa9bd2c732a767bb60d12c8770b40e8f9b6cca28efc4b12b1be81c7f28e AS builder + +# Foundry image runs as foundry user by default +# We need root to both run apt and to write files to the filesystem +USER root +RUN apt-get --quiet update && apt-get --quiet --no-install-recommends --yes install jq wget +USER foundry + +# Preflight + +WORKDIR /app +COPY foundry.toml foundry.toml +COPY --link lib/wormhole-solidity-sdk lib/wormhole-solidity-sdk +COPY --link src/evm src/evm + +# forge logs "errors" and warnings to the standard output +# Luckily, the only warning here is that forge's solidity compiler cache is missing. +# However, we need to drop it to have a valid JSON. +# See https://github.com/foundry-rs/foundry/issues/13034 + +# Prepare compiler input. NOTE: jq must be pre-applied to "clean" the output from forge. +# Otherwise solc aborts with duplicated key/newline problems. + +RUN forge verify-contract \ + --show-standard-json-input \ + 0x0000000000000000000000000000000000000000 \ + src/evm/WormholeVerifier.sol:WormholeVerifier \ + | sed '1d' \ + | jq '.' > WormholeVerifier.input.json + +# Get compiler according to forge configuration (foundry.toml specified) + +RUN SOLC_VERSION=$(forge config | grep "^solc =" | sed 's/solc = //' | sed 's/"//g'); \ + if [ -z "$SOLC_VERSION" ]; then echo "SOLC_VERSION not set"; exit 1; fi; \ + wget --progress=dot:giga --output-document=solc "https://github.com/ethereum/solidity/releases/download/v$SOLC_VERSION/solc-static-linux" && chmod +x solc + +# Compile contract(s). + +RUN ./solc --standard-json WormholeVerifier.input.json > WormholeVerifier.output.json && \ + SOLC_ERR=$(jq '.errors[]? | select(.severity == "error")' WormholeVerifier.output.json) && \ + if [ ! -z "$SOLC_ERR" ]; then \ + echo "Error detected during solc execution."; \ + echo "$SOLC_ERR"; \ + exit 2; \ + fi + +# Consolidate all generated output +FROM scratch AS foundry-export +COPY --from=builder /app/*.input.json /app/*.output.json / diff --git a/evm-build.Dockerfile.dockerignore b/evm-build.Dockerfile.dockerignore new file mode 100644 index 0000000..3598e85 --- /dev/null +++ b/evm-build.Dockerfile.dockerignore @@ -0,0 +1,32 @@ +data/ +.vscode/ +.vim/ +.github/ +cache/ +out/ +tmp/ +verifiable-evm-build/ + +ts-pkgs/ + +src/solana +test/ + +**/ts-build + +.gitignore +.gitmodules +*.sublime-* +*.awk +guardian_key.txt +README.md +eslint.config.mjs + +.yarn/ +package.json +tsconfig.json +yarn.lock +.pnp.* + +*Dockerfile +*dockerignore \ No newline at end of file diff --git a/src/evm/WormholeVerifier.sol b/src/evm/WormholeVerifier.sol index a2ad13f..12b1c61 100644 --- a/src/evm/WormholeVerifier.sol +++ b/src/evm/WormholeVerifier.sol @@ -1280,6 +1280,8 @@ contract WormholeVerifier is EIP712Encoding { // Check if we need to update the current guardian set if (oldMultisigKeysLength > 0) { // Pull and write the current guardian set expiration time + // There can't be more than 2^32 - 1 guardian sets + // forge-lint: disable-next-line(unsafe-typecast) uint32 updateIndex = uint32(oldMultisigKeysLength - 1); uint32 expirationTime = _coreBridge.getGuardianSet(updateIndex).expirationTime; _setMultisigExpirationTime(updateIndex, expirationTime); @@ -1292,6 +1294,8 @@ contract WormholeVerifier is EIP712Encoding { // Pull and append the guardian sets for (uint256 i = oldMultisigKeysLength; i < upper; i++) { // Pull the guardian set, write the expiration time, and append the guardian set data to the ExtStore + // There can't be more than 2^32 - 1 guardian sets + // forge-lint: disable-next-line(unsafe-typecast) GuardianSet memory guardians = _coreBridge.getGuardianSet(uint32(i)); _appendMultisigKeyData(guardians.keys, guardians.expirationTime); } @@ -1319,9 +1323,13 @@ contract WormholeVerifier is EIP712Encoding { uint256 multisigDataSlot = SLOT_MULTISIG_KEY_DATA + index; uint256 entry; assembly ("memory-safe") { entry := sload(multisigDataSlot) } + // We clear the upper bits + // forge-lint: disable-next-line(unsafe-typecast) expirationTime = uint32(entry & MASK_MULTISIG_ENTRY_EXPIRATION_TIME); // Load the key data contract, validate the size + // We select the bits that contain the address + // forge-lint: disable-next-line(unsafe-typecast) address keyDataAddress = address(uint160(entry >> SHIFT_MULTISIG_ENTRY_ADDRESS)); uint256 keyDataSize = keyDataAddress.code.length; require (keyDataSize > 0, UnknownGuardianSet(index)); @@ -1398,9 +1406,12 @@ contract WormholeVerifier is EIP712Encoding { uint256 storageWord; assembly ("memory-safe") { storageWord := sload(extraDataSlot) } + // We select the relevant bits for each field + // forge-lint: disable-start(unsafe-typecast) expirationTime = uint32( storageWord & MASK_SCHNORR_EXTRA_EXPIRATION_TIME); shardCount = uint8 ((storageWord >> SHIFT_SCHNORR_EXTRA_SHARD_COUNT) & MASK_SCHNORR_EXTRA_SHARD_COUNT ); multisigKeyIndex = uint32( storageWord >> SHIFT_SCHNORR_EXTRA_MULTISIG_KEY_INDEX ); + // forge-lint: disable-end(unsafe-typecast) } function _getSchnorrShardDataExport(uint32 index) internal view returns (uint8 shardCount, bytes memory shardData) { diff --git a/ts-pkgs/peer-client/Dockerfile b/ts-pkgs/peer-client/Dockerfile index fdf446c..6a4c6fa 100644 --- a/ts-pkgs/peer-client/Dockerfile +++ b/ts-pkgs/peer-client/Dockerfile @@ -1,11 +1,19 @@ FROM node:22.21-trixie-slim@sha256:1ddaeddded05b2edeaf35fac720a18019e1044a6791509c8670c53c2308301bb -RUN apt-get update && apt-get -y install git +RUN mkdir --parents core-bridge/ts-pkgs/peer-client core-bridge/ts-pkgs/peer-lib +COPY --link .yarn /core-bridge/.yarn +COPY --link package.json yarn.lock .yarnrc.yml /core-bridge/ +COPY --link ts-pkgs/peer-client/package.json /core-bridge/ts-pkgs/peer-client/ +COPY --link ts-pkgs/peer-lib/package.json /core-bridge/ts-pkgs/peer-lib/ +WORKDIR /core-bridge +RUN yarnpkg workspaces focus --all -# TODO: Pin the commit -RUN git clone -b feat/dkg-docker --depth 1 https://github.com/XLabs/core-bridge.git +COPY --link ts-pkgs/config/ ts-pkgs/config/ +COPY --link ts-pkgs/peer-lib/tsconfig.json ts-pkgs/peer-lib/ +COPY --link ts-pkgs/peer-lib/src ts-pkgs/peer-lib/src +COPY --link ts-pkgs/peer-client/tsconfig.json ts-pkgs/peer-client/ +COPY --link ts-pkgs/peer-client/src ts-pkgs/peer-client/src WORKDIR /core-bridge/ts-pkgs/peer-client -RUN yarnpkg install --immutable RUN yarnpkg build ARG TLS_HOSTNAME diff --git a/ts-pkgs/peer-client/Dockerfile.dockerignore b/ts-pkgs/peer-client/Dockerfile.dockerignore new file mode 100644 index 0000000..0252a5e --- /dev/null +++ b/ts-pkgs/peer-client/Dockerfile.dockerignore @@ -0,0 +1,37 @@ +src/ +data/ +.vscode/ +.vim/ +.github/ +cache/ +lib/ +out/ +tmp/ +verifiable-evm-build/ + +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions + +ts-pkgs/deploy +ts-pkgs/peer-e2e +ts-pkgs/tss-definitions +ts-pkgs/peer-server + +**/ts-build + +.gitignore +.gitmodules +*.sublime-* +*.awk +foundry.toml +guardian_key.txt +Makefile +README.md +eslint.config.mjs + +*Dockerfile +*dockerignore \ No newline at end of file diff --git a/ts-pkgs/peer-client/dkg/Dockerfile b/ts-pkgs/peer-client/dkg.Dockerfile similarity index 65% rename from ts-pkgs/peer-client/dkg/Dockerfile rename to ts-pkgs/peer-client/dkg.Dockerfile index a72b839..1802bca 100644 --- a/ts-pkgs/peer-client/dkg/Dockerfile +++ b/ts-pkgs/peer-client/dkg.Dockerfile @@ -1,16 +1,33 @@ FROM node:22.21-trixie-slim@sha256:1ddaeddded05b2edeaf35fac720a18019e1044a6791509c8670c53c2308301bb -RUN apt-get update && apt-get -y install git golang jq +RUN apt-get --quiet update && apt-get --quiet --no-install-recommends --yes install \ + git \ + golang \ + jq \ + ca-certificates \ + && rm -rf /var/lib/apt/lists # TODO: Pin the commit -RUN git clone -b feat/dkg-docker --depth 1 https://github.com/XLabs/core-bridge.git RUN git clone -b schnorr --depth 1 https://github.com/XLabs/wormhole.git WORKDIR /wormhole/node/pkg/tss/internal/cmd RUN go build -o=./server ./dkg +WORKDIR / +RUN mkdir --parents core-bridge/ts-pkgs/peer-client core-bridge/ts-pkgs/peer-lib +COPY --link .yarn core-bridge/.yarn +COPY --link package.json yarn.lock .yarnrc.yml core-bridge/ +COPY --link ts-pkgs/peer-client/package.json core-bridge/ts-pkgs/peer-client/ +COPY --link ts-pkgs/peer-lib/package.json core-bridge/ts-pkgs/peer-lib/ +WORKDIR /core-bridge +RUN yarnpkg workspaces focus --all + +COPY --link ts-pkgs/config/ ts-pkgs/config/ +COPY --link ts-pkgs/peer-lib/tsconfig.json ts-pkgs/peer-lib/ +COPY --link ts-pkgs/peer-lib/src ts-pkgs/peer-lib/src +COPY --link ts-pkgs/peer-client/tsconfig.json ts-pkgs/peer-client/ +COPY --link ts-pkgs/peer-client/src ts-pkgs/peer-client/src WORKDIR /core-bridge/ts-pkgs/peer-client -RUN yarnpkg install --immutable RUN yarnpkg build COPY --chmod=555 </dev/null & + docker stop "Guardian$i" 2>/dev/null & done; docker stop anvil-with-verifier peer-server 2>/dev/null & diff --git a/ts-pkgs/peer-e2e/tests/e2e/scripts/client.sh b/ts-pkgs/peer-e2e/tests/e2e/scripts/client.sh index db5c4f9..efc0cc4 100755 --- a/ts-pkgs/peer-e2e/tests/e2e/scripts/client.sh +++ b/ts-pkgs/peer-e2e/tests/e2e/scripts/client.sh @@ -33,7 +33,7 @@ GUARDIAN_PRIVATE_KEYS=( ) createGuardianPrivateKey() { - echo 0a20${GUARDIAN_PRIVATE_KEYS[$1]} | \ + echo "0a20${GUARDIAN_PRIVATE_KEYS[$1]}" | \ xxd -r -p | gpg --enarmor | \ awk 'BEGIN {print "-----BEGIN WORMHOLE GUARDIAN PRIVATE KEY-----"} NR>2 {print last} @@ -42,13 +42,13 @@ createGuardianPrivateKey() { } # Build the dockerfile that generates the TLS key and certificate -docker build --tag tls-gen --file ../../../peer-client/tls/Dockerfile --progress=plain . +yarn workspace @xlabs-xyz/peer-client run docker:build:tls-gen for i in "${!GUARDIAN_PRIVATE_KEYS[@]}" do - mkdir -p out/$i/keys - docker run --rm --mount type=bind,src=./out/$i/keys,dst=/keys \ - --env TLS_HOSTNAME=${TLS_HOSTNAME}$i \ + mkdir -p "out/$i/keys" + docker run --rm --mount "type=bind,src=./out/$i/keys,dst=/keys" \ + --env "TLS_HOSTNAME=${TLS_HOSTNAME}$i" \ --env TLS_PUBLIC_IP=${TLS_PUBLIC_IP} \ tls-gen & done @@ -56,7 +56,7 @@ done wait # Build the docker cache first. It will throw an error but it will save time -docker build --builder dkg-builder --network=host --file ../../../peer-client/Dockerfile --progress=plain . 2>/dev/null || true +docker build --builder dkg-builder --network=host --file ../../../peer-client/Dockerfile --progress=plain ../../../.. 2>/dev/null || true # Wait until the server starts listening until docker logs peer-server 2>/dev/null | grep "running" @@ -68,24 +68,24 @@ for i in "${!GUARDIAN_PRIVATE_KEYS[@]}" do # The host here refers to the builder host container, not the host machine. docker build --builder dkg-builder --network=host --file ../../../peer-client/Dockerfile \ - --secret id=guardian_pk,src=<(createGuardianPrivateKey $i) \ - --secret id=cert.pem,src=./out/$i/keys/cert.pem \ - --build-arg TLS_HOSTNAME=${TLS_HOSTNAME}$i \ - --build-arg TLS_PORT=$((TLS_BASE_PORT + $i)) \ + --secret id=guardian_pk,src=<(createGuardianPrivateKey "$i") \ + --secret "id=cert.pem,src=./out/$i/keys/cert.pem" \ + --build-arg "TLS_HOSTNAME=${TLS_HOSTNAME}$i" \ + --build-arg TLS_PORT=$((TLS_BASE_PORT + i)) \ --build-arg PEER_SERVER_URL=${PEER_SERVER_URL} \ - --progress=plain . & + --progress=plain ../../../.. & done wait -docker build --tag dkg-client --file ../../../peer-client/dkg/Dockerfile --progress=plain . +docker build --tag dkg-client --file ../../../peer-client/dkg.Dockerfile --progress=plain ../../../.. for i in "${!GUARDIAN_PRIVATE_KEYS[@]}" do - docker run --rm --name ${TLS_HOSTNAME}$i --network=dkg-test \ - --mount type=bind,src=./out/$i/keys,dst=/keys \ - --env TLS_HOSTNAME=${TLS_HOSTNAME}$i \ - --env TLS_PORT=$((TLS_BASE_PORT + $i)) \ + docker run --rm --name "${TLS_HOSTNAME}$i" --network=dkg-test \ + --mount "type=bind,src=./out/$i/keys,dst=/keys" \ + --env "TLS_HOSTNAME=${TLS_HOSTNAME}$i" \ + --env TLS_PORT=$((TLS_BASE_PORT + i)) \ --env PEER_SERVER_URL=${PEER_SERVER_URL} \ --env ETHEREUM_RPC_URL=${ETHEREUM_RPC_URL} \ --env WORMHOLE_CONTRACT_ADDRESS=${WORMHOLE_ADDRESS} \ diff --git a/ts-pkgs/peer-e2e/tests/e2e/scripts/server.sh b/ts-pkgs/peer-e2e/tests/e2e/scripts/server.sh index 008318b..1be5062 100755 --- a/ts-pkgs/peer-e2e/tests/e2e/scripts/server.sh +++ b/ts-pkgs/peer-e2e/tests/e2e/scripts/server.sh @@ -7,12 +7,24 @@ SERVER_PORT="3000" ETHEREUM_RPC_URL="http://anvil-with-verifier:8545" WORMHOLE_ADDRESS="0x5FbDB2315678afecb367f032d93F642f64180aa3" -docker build --tag peer-server --file ../../../peer-server/Dockerfile --build-arg SERVER_PORT=${SERVER_PORT} --build-arg ETHEREUM_RPC_URL=${ETHEREUM_RPC_URL} --build-arg WORMHOLE_ADDRESS=${WORMHOLE_ADDRESS} --progress=plain . +docker build --tag peer-server \ + --file ../../../peer-server/Dockerfile \ + --build-arg SERVER_PORT=${SERVER_PORT} \ + --build-arg ETHEREUM_RPC_URL=${ETHEREUM_RPC_URL} \ + --build-arg WORMHOLE_ADDRESS=${WORMHOLE_ADDRESS} \ + --progress=plain ../../../.. # Wait until anvil starts listening -until docker logs anvil-with-verifier 2>/dev/null | grep Listening -do - sleep 1 -done +docker run --rm --network=dkg-test --env ETHEREUM_RPC_URL ghcr.io/foundry-rs/foundry:v1.5.1@sha256:3a70bfa9bd2c732a767bb60d12c8770b40e8f9b6cca28efc4b12b1be81c7f28e sh -lc ' + deadline=$((SECONDS+60)) + until cast block-number --rpc-url "$ETHEREUM_RPC_URL" >/dev/null 2>&1; do + if [ "$SECONDS" -ge "$deadline" ]; then + echo "Timed out waiting for $ETHEREUM_RPC_URL" >&2 + exit 1 + fi + sleep 0.5 + done +' + docker run --rm --network=dkg-test --name peer-server peer-server diff --git a/ts-pkgs/peer-server/Dockerfile b/ts-pkgs/peer-server/Dockerfile index 389780e..db48e7c 100644 --- a/ts-pkgs/peer-server/Dockerfile +++ b/ts-pkgs/peer-server/Dockerfile @@ -1,19 +1,29 @@ FROM node:22.21-trixie-slim@sha256:1ddaeddded05b2edeaf35fac720a18019e1044a6791509c8670c53c2308301bb -RUN apt-get update && apt-get -y install git - ARG SERVER_PORT ARG ETHEREUM_RPC_URL ARG WORMHOLE_ADDRESS=0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B -RUN test -n "${SERVER_PORT}" && \ - test -n "${ETHEREUM_RPC_URL}" +RUN mkdir --parents core-bridge/ts-pkgs/peer-server core-bridge/ts-pkgs/peer-lib +COPY --link .yarn /core-bridge/.yarn +COPY --link package.json yarn.lock .yarnrc.yml /core-bridge/ +COPY --link ts-pkgs/peer-server/package.json /core-bridge/ts-pkgs/peer-server/ +COPY --link ts-pkgs/peer-lib/package.json /core-bridge/ts-pkgs/peer-lib/ +WORKDIR /core-bridge +RUN yarnpkg workspaces focus --all + +COPY --link ts-pkgs/config/ ts-pkgs/config/ +COPY --link ts-pkgs/peer-lib/tsconfig.json ts-pkgs/peer-lib/ +COPY --link ts-pkgs/peer-lib/src ts-pkgs/peer-lib/src +COPY --link ts-pkgs/peer-server/tsconfig.json ts-pkgs/peer-server/ +COPY --link ts-pkgs/peer-server/src ts-pkgs/peer-server/src -# TODO: Pin the commit -RUN git clone -b feat/dkg-docker --depth 1 https://github.com/XLabs/core-bridge.git WORKDIR /core-bridge/ts-pkgs/peer-server -RUN yarnpkg install --immutable RUN yarnpkg build + +RUN test -n "${SERVER_PORT}" && \ + test -n "${ETHEREUM_RPC_URL}" + # This is the config for the peer server COPY <