From f69a91d97c1b165c70e3d97e7f12006a5bcd9b8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Drozd?= Date: Tue, 13 Dec 2022 14:05:06 +0100 Subject: [PATCH] Add the Squads Mesh program to Tilt and initialize a Vault for it (#421) * solana-devnet: Deploy a copy of the Mesh multisig program * solana/keys: Add keys for multisig testing * *.py: Remove airdrop, use devnet_setup.sh keypair instead * Dockerfile.p2w-attest: Improve caching - Move cheap Python script additions after the expensive rust build - Cache Cargo's package cache to shorten the "updating crates.io registry" build steps * Add a multisig Tilt resource, k8s yaml and runtime script This contains most of the work on Tilt/testing harness side. * multisig-wh-message-builder: Add init-vault subcommand This new subcommand enables the runtime Python script to create a multisig vault on the fly on the mock Solana devnet. * multisig-wh-message-builder: findProgramAddress -> getMsPDA * mutlisig-wh-message-builder: apply review advice - Use default mesh program address - remove unused program.json - remove redundant null checks - hardcode vault address (it is deterministic against the constant mesh program pubkey) - Start depending on solana-devnet in Tilt - Add carol to vault and set threshold to 2 --- Dockerfile.multisig | 37 +++++ Dockerfile.solana | 14 +- Tiltfile | 15 ++ devnet/multisig.yaml | 39 ++++++ devnet/solana-devnet.yaml | 3 + solana/keys/squads/create_key.json | 6 + solana/keys/squads/external_authority.json | 6 + solana/keys/squads/member_alice.json | 6 + solana/keys/squads/member_bob.json | 6 + solana/keys/squads/member_carol.json | 6 + third_party/pyth/Dockerfile.p2w-attest | 13 +- .../package-lock.json | 128 +++++++++--------- .../multisig-wh-message-builder/package.json | 12 +- .../multisig-wh-message-builder/src/index.ts | 127 +++++++++++++++-- third_party/pyth/p2w_autoattest.py | 23 +--- third_party/pyth/prepare_multisig.py | 60 ++++++++ third_party/pyth/pyth_publisher.py | 5 + third_party/pyth/pyth_utils.py | 27 ++-- 18 files changed, 419 insertions(+), 114 deletions(-) create mode 100644 Dockerfile.multisig create mode 100644 devnet/multisig.yaml create mode 100644 solana/keys/squads/create_key.json create mode 100644 solana/keys/squads/external_authority.json create mode 100644 solana/keys/squads/member_alice.json create mode 100644 solana/keys/squads/member_bob.json create mode 100644 solana/keys/squads/member_carol.json create mode 100644 third_party/pyth/prepare_multisig.py diff --git a/Dockerfile.multisig b/Dockerfile.multisig new file mode 100644 index 00000000..ff4e04ab --- /dev/null +++ b/Dockerfile.multisig @@ -0,0 +1,37 @@ +#syntax=docker/dockerfile:1.2@sha256:e2a8561e419ab1ba6b2fe6cbdf49fd92b95912df1cf7d313c3e2230a333fdbcc +FROM ghcr.io/certusone/solana:1.10.31 + +# This image builds an environment to initialize and use a local +# devnet multisig. It uses Pyth's Mesh client. + +RUN apt-get update && apt-get install -yq python3 libudev-dev ncat + +RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash - && apt-get install -y nodejs + +WORKDIR /root + +# Also used by the multisig provisioning script +ENV MULTISIG_SCRIPT_DIR=/root/pyth/multisig-wh-message-builder + +WORKDIR $MULTISIG_SCRIPT_DIR + +ENV LOCAL_MULTISIG_SCRIPT_DIR=third_party/pyth/multisig-wh-message-builder + +# Add a barebones representation of our deps for Docker layer caching +ADD $LOCAL_MULTISIG_SCRIPT_DIR/package.json \ + $LOCAL_MULTISIG_SCRIPT_DIR/package-lock.json \ + $LOCAL_MULTISIG_SCRIPT_DIR/tsconfig.json \ + . + +RUN mkdir src # tsc is run as part of the install, add minimal placeholders to satisfy it +RUN touch src/index.ts + +RUN --mount=type=cache,target=/home/node/.cache \ + npm ci && cp -r node_modules node_modules_cached +RUN rm -rf node_modules && mv node_modules_cached node_modules + +# Add the rest of the code. This ensures that real code changes do not affect the layer caching of `npm ci` +ADD third_party/pyth /root/pyth +ADD solana/keys /usr/src/solana/keys + +RUN npm install diff --git a/Dockerfile.solana b/Dockerfile.solana index 3032af73..beb1e36b 100644 --- a/Dockerfile.solana +++ b/Dockerfile.solana @@ -34,6 +34,15 @@ RUN tar -xvf v${WORMHOLE_REV}.tar.gz RUN mv wormhole-${WORMHOLE_REV} wormhole # RUN mkdir -p /usr/src/bridge/wormhole/solana/target +WORKDIR /usr/src/squads + +ARG SQUADS_REV=1.2.0 +ADD https://github.com/Squads-Protocol/squads-mpl/archive/refs/tags/v${SQUADS_REV}.tar.gz . + +RUN tar -xvf v${SQUADS_REV}.tar.gz +RUN mv squads-mpl-${SQUADS_REV} squads-mpl + +WORKDIR /usr/src/bridge ADD solana solana ADD pythnet pythnet @@ -48,14 +57,17 @@ WORKDIR /usr/src/bridge/solana # Build Wormhole Solana programs RUN --mount=type=cache,target=/usr/src/bridge/wormhole/solana/target \ --mount=type=cache,target=/usr/src/bridge/solana/pyth2wormhole/target \ + --mount=type=cache,target=/usr/src/squads/squads-mpl/target \ --mount=type=cache,target=/usr/local/cargo/registry \ --mount=type=cache,target=/root/.cache \ cargo build-bpf --manifest-path "../wormhole/solana/bridge/program/Cargo.toml" -- --locked && \ cargo build-bpf --manifest-path "../wormhole/solana/bridge/cpi_poster/Cargo.toml" -- --locked && \ cargo build-bpf --manifest-path "pyth2wormhole/program/Cargo.toml" -- --locked && \ + cargo build-bpf --manifest-path "/usr/src/squads/squads-mpl/programs/mesh/Cargo.toml" -- --locked && \ cp ../wormhole/solana/target/deploy/bridge.so /opt/solana/deps/bridge.so && \ cp ../wormhole/solana/target/deploy/cpi_poster.so /opt/solana/deps/cpi_poster.so && \ - cp pyth2wormhole/target/deploy/pyth2wormhole.so /opt/solana/deps/pyth2wormhole.so + cp pyth2wormhole/target/deploy/pyth2wormhole.so /opt/solana/deps/pyth2wormhole.so && \ + cp /usr/src/squads/squads-mpl/target/deploy/mesh.so /opt/solana/deps/mesh.so COPY --from=pyth-oracle-copy /home/pyth/pyth-client/target/oracle.so /opt/solana/deps/pyth_oracle.so diff --git a/Tiltfile b/Tiltfile index 76ae3f39..97073728 100644 --- a/Tiltfile +++ b/Tiltfile @@ -351,3 +351,18 @@ k8s_resource( labels = ["prometheus"], trigger_mode = trigger_mode, ) + +docker_build( + ref = "multisig", + context = ".", + dockerfile = "Dockerfile.multisig", +) + +k8s_yaml_with_ns("devnet/multisig.yaml") + +k8s_resource( + "multisig", + resource_deps = ["solana-devnet"], + labels = ["solana"], + trigger_mode = trigger_mode, +) diff --git a/devnet/multisig.yaml b/devnet/multisig.yaml new file mode 100644 index 00000000..f488f712 --- /dev/null +++ b/devnet/multisig.yaml @@ -0,0 +1,39 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: multisig + labels: + app: multisig +spec: + clusterIP: None + selector: + app: multisig +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: multisig +spec: + selector: + matchLabels: + app: multisig + serviceName: multisig + template: + metadata: + labels: + app: multisig + spec: + restartPolicy: Always + terminationGracePeriodSeconds: 0 + containers: + - name: multisig + image: multisig + readinessProbe: + tcpSocket: + port: 2000 + periodSeconds: 1 + failureThreshold: 300 + command: + - python3 + - /root/pyth/prepare_multisig.py diff --git a/devnet/solana-devnet.yaml b/devnet/solana-devnet.yaml index 6476e0e9..9d20d4fb 100644 --- a/devnet/solana-devnet.yaml +++ b/devnet/solana-devnet.yaml @@ -50,6 +50,9 @@ spec: - --bpf-program - P2WH424242424242424242424242424242424242424 - /opt/solana/deps/pyth2wormhole.so + - --bpf-program + - SMPLVC8MxZ5Bf5EfF7PaMiTCxoBAcmkbM2vkrvMK8ho # copied from squads-mpl/programs/mesh/src/lib.rs + - /opt/solana/deps/mesh.so - --log ports: - containerPort: 8001 diff --git a/solana/keys/squads/create_key.json b/solana/keys/squads/create_key.json new file mode 100644 index 00000000..da5d9abf --- /dev/null +++ b/solana/keys/squads/create_key.json @@ -0,0 +1,6 @@ +[ + 174, 86, 158, 146, 5, 14, 115, 61, 113, 135, 247, 80, 154, 1, 168, 241, 237, + 184, 94, 53, 32, 115, 162, 198, 35, 226, 72, 198, 108, 242, 35, 175, 226, 156, + 60, 163, 77, 178, 58, 243, 50, 48, 28, 249, 226, 125, 150, 188, 35, 23, 131, + 149, 177, 124, 235, 145, 103, 119, 237, 30, 30, 25, 145, 128 +] diff --git a/solana/keys/squads/external_authority.json b/solana/keys/squads/external_authority.json new file mode 100644 index 00000000..d0392df5 --- /dev/null +++ b/solana/keys/squads/external_authority.json @@ -0,0 +1,6 @@ +[ + 172, 234, 168, 90, 159, 133, 183, 38, 206, 220, 115, 240, 201, 186, 191, 12, + 38, 133, 233, 164, 62, 92, 164, 155, 149, 133, 68, 83, 168, 233, 67, 12, 1, + 134, 165, 231, 211, 192, 216, 167, 186, 77, 109, 120, 172, 131, 36, 27, 95, + 207, 60, 228, 128, 201, 74, 109, 132, 176, 165, 156, 62, 146, 247, 75 +] diff --git a/solana/keys/squads/member_alice.json b/solana/keys/squads/member_alice.json new file mode 100644 index 00000000..7e2ab3ad --- /dev/null +++ b/solana/keys/squads/member_alice.json @@ -0,0 +1,6 @@ +[ + 196, 148, 217, 170, 205, 37, 40, 95, 214, 198, 118, 8, 52, 12, 250, 196, 95, + 138, 15, 163, 55, 212, 93, 215, 72, 15, 11, 125, 221, 67, 196, 176, 219, 38, + 22, 196, 10, 226, 177, 210, 88, 255, 245, 194, 140, 68, 61, 222, 16, 199, 151, + 74, 161, 165, 178, 130, 124, 60, 99, 168, 130, 199, 251, 149 +] diff --git a/solana/keys/squads/member_bob.json b/solana/keys/squads/member_bob.json new file mode 100644 index 00000000..3ab1c589 --- /dev/null +++ b/solana/keys/squads/member_bob.json @@ -0,0 +1,6 @@ +[ + 238, 68, 150, 179, 87, 216, 135, 224, 44, 190, 97, 182, 75, 109, 167, 101, + 146, 236, 95, 142, 190, 237, 251, 179, 186, 54, 100, 145, 166, 113, 222, 85, + 0, 42, 46, 190, 161, 239, 138, 33, 240, 218, 84, 112, 63, 54, 170, 185, 140, + 21, 211, 216, 57, 146, 161, 87, 170, 18, 29, 186, 231, 15, 241, 91 +] diff --git a/solana/keys/squads/member_carol.json b/solana/keys/squads/member_carol.json new file mode 100644 index 00000000..e66c2bf6 --- /dev/null +++ b/solana/keys/squads/member_carol.json @@ -0,0 +1,6 @@ +[ + 140, 155, 132, 14, 35, 183, 53, 188, 155, 194, 27, 24, 251, 0, 26, 136, 173, + 9, 140, 24, 155, 157, 172, 249, 41, 205, 221, 234, 114, 32, 128, 179, 120, + 208, 86, 87, 116, 157, 31, 72, 104, 90, 21, 155, 104, 85, 190, 144, 253, 151, + 189, 47, 172, 194, 170, 246, 97, 26, 211, 96, 203, 106, 98, 216 +] diff --git a/third_party/pyth/Dockerfile.p2w-attest b/third_party/pyth/Dockerfile.p2w-attest index 0aed6fdd..b08d3433 100644 --- a/third_party/pyth/Dockerfile.p2w-attest +++ b/third_party/pyth/Dockerfile.p2w-attest @@ -5,10 +5,7 @@ RUN apt-get update && apt-get install -yq python3 libudev-dev ncat RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash - && apt-get install -y nodejs ADD pythnet/remote-executor /usr/src/pythnet/remote-executor -ADD third_party/pyth/pyth_utils.py /usr/src/pyth/pyth_utils.py -ADD third_party/pyth/p2w_autoattest.py /usr/src/pyth/p2w_autoattest.py ADD third_party/pyth/p2w-sdk/rust /usr/src/third_party/pyth/p2w-sdk/rust - ADD solana /usr/src/solana WORKDIR /usr/src/solana/pyth2wormhole @@ -17,14 +14,18 @@ ENV EMITTER_ADDRESS="11111111111111111111111111111115" ENV BRIDGE_ADDRESS="Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o" RUN --mount=type=cache,target=/root/.cache \ + --mount=type=cache,target=/usr/local/cargo/registry \ --mount=type=cache,target=target \ cargo test --package pyth2wormhole-client && \ cargo build --package pyth2wormhole-client && \ - mv target/debug/pyth2wormhole-client /usr/local/bin/pyth2wormhole-client && \ - chmod a+rx /usr/src/pyth/*.py + mv target/debug/pyth2wormhole-client /usr/local/bin/pyth2wormhole-client + +ADD third_party/pyth/pyth_utils.py /usr/src/pyth/pyth_utils.py +ADD third_party/pyth/p2w_autoattest.py /usr/src/pyth/p2w_autoattest.py +RUN chmod a+rx /usr/src/pyth/*.py + ENV P2W_OWNER_KEYPAIR="/usr/src/solana/keys/p2w_owner.json" ENV P2W_ATTESTATIONS_PORT="4343" ENV PYTH_PUBLISHER_KEYPAIR="/usr/src/solana/keys/pyth_publisher.json" ENV PYTH_PROGRAM_KEYPAIR="/usr/src/solana/keys/pyth_program.json" -ENV SOL_AIRDROP_AMT="100" diff --git a/third_party/pyth/multisig-wh-message-builder/package-lock.json b/third_party/pyth/multisig-wh-message-builder/package-lock.json index 1ad8a6e5..4be94aa5 100644 --- a/third_party/pyth/multisig-wh-message-builder/package-lock.json +++ b/third_party/pyth/multisig-wh-message-builder/package-lock.json @@ -16,10 +16,14 @@ "@solana/web3.js": "^1.53.0", "@sqds/mesh": "^1.0.6", "@types/lodash": "^4.14.186", + "@types/node-fetch": "^2.6.2", + "@types/node-hid": "^1.3.1", "bs58": "^5.0.0", "commander": "^9.4.0", "ethers": "^5.7.0", - "lodash": "^4.17.21" + "lodash": "^4.17.21", + "ts-node": "^10.9.1", + "typescript": "^4.8.3" }, "devDependencies": { "@types/bn.js": "^5.1.0", @@ -29,9 +33,7 @@ "eslint": "^8.23.0", "jest": "^28.0.8", "prettier": "^2.7.1", - "ts-jest": "^28.0.8", - "ts-node": "^10.9.1", - "typescript": "^4.8.3" + "ts-jest": "^28.0.8" } }, "node_modules/@ampproject/remapping": { @@ -674,7 +676,6 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -686,7 +687,6 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -1869,7 +1869,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -1886,8 +1885,7 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.15", @@ -2414,26 +2412,22 @@ "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" }, "node_modules/@tsconfig/node16": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==" }, "node_modules/@types/babel__core": { "version": "7.1.19", @@ -2557,6 +2551,23 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.15.tgz", "integrity": "sha512-XnjpaI8Bgc3eBag2Aw4t2Uj/49lLBSStHWfqKvIuXD7FIrZyMLWp8KuAFHAqxMZYTF9l08N1ctUn9YNybZJVmQ==" }, + "node_modules/@types/node-fetch": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz", + "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", + "dependencies": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "node_modules/@types/node-hid": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/node-hid/-/node-hid-1.3.1.tgz", + "integrity": "sha512-VPxuGDCoDxOUKrTZPSok7IEmiK4cVLfj8Csu09FtG5uF+eqf1HETERHXQkO02Rk6j6YiiHxp0/DA9R4llvhEzQ==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/prettier": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.0.tgz", @@ -2781,7 +2792,6 @@ "version": "8.8.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", - "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -2802,7 +2812,6 @@ "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -2963,8 +2972,7 @@ "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" }, "node_modules/argparse": { "version": "2.0.1", @@ -3648,8 +3656,7 @@ "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" }, "node_modules/cross-fetch": { "version": "3.1.5", @@ -3793,7 +3800,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, "engines": { "node": ">=0.3.1" } @@ -5893,8 +5899,7 @@ "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" }, "node_modules/makeerror": { "version": "1.0.12", @@ -7406,7 +7411,6 @@ "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -7529,7 +7533,6 @@ "version": "4.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", - "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7619,8 +7622,7 @@ "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" }, "node_modules/v8-to-istanbul": { "version": "9.0.1", @@ -7804,7 +7806,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, "engines": { "node": ">=6" } @@ -8319,7 +8320,6 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, "requires": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -8328,7 +8328,6 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -9091,8 +9090,7 @@ "@jridgewell/resolve-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" }, "@jridgewell/set-array": { "version": "1.1.2", @@ -9103,8 +9101,7 @@ "@jridgewell/sourcemap-codec": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "@jridgewell/trace-mapping": { "version": "0.3.15", @@ -9561,26 +9558,22 @@ "@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" }, "@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" }, "@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" }, "@tsconfig/node16": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==" }, "@types/babel__core": { "version": "7.1.19", @@ -9704,6 +9697,23 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.15.tgz", "integrity": "sha512-XnjpaI8Bgc3eBag2Aw4t2Uj/49lLBSStHWfqKvIuXD7FIrZyMLWp8KuAFHAqxMZYTF9l08N1ctUn9YNybZJVmQ==" }, + "@types/node-fetch": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz", + "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "@types/node-hid": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/node-hid/-/node-hid-1.3.1.tgz", + "integrity": "sha512-VPxuGDCoDxOUKrTZPSok7IEmiK4cVLfj8Csu09FtG5uF+eqf1HETERHXQkO02Rk6j6YiiHxp0/DA9R4llvhEzQ==", + "requires": { + "@types/node": "*" + } + }, "@types/prettier": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.0.tgz", @@ -9838,8 +9848,7 @@ "acorn": { "version": "8.8.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", - "dev": true + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==" }, "acorn-jsx": { "version": "5.3.2", @@ -9851,8 +9860,7 @@ "acorn-walk": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" }, "aes-js": { "version": "3.0.0", @@ -9981,8 +9989,7 @@ "arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" }, "argparse": { "version": "2.0.1", @@ -10529,8 +10536,7 @@ "create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" }, "cross-fetch": { "version": "3.1.5", @@ -10629,8 +10635,7 @@ "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" }, "diff-sequences": { "version": "28.1.1", @@ -12233,8 +12238,7 @@ "make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" }, "makeerror": { "version": "1.0.12", @@ -13329,7 +13333,6 @@ "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, "requires": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -13410,8 +13413,7 @@ "typescript": { "version": "4.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", - "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", - "dev": true + "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==" }, "update-browserslist-db": { "version": "1.0.7", @@ -13469,8 +13471,7 @@ "v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" }, "v8-to-istanbul": { "version": "9.0.1", @@ -13609,8 +13610,7 @@ "yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" }, "yocto-queue": { "version": "0.1.0", diff --git a/third_party/pyth/multisig-wh-message-builder/package.json b/third_party/pyth/multisig-wh-message-builder/package.json index f5f1bbc4..58aade0e 100644 --- a/third_party/pyth/multisig-wh-message-builder/package.json +++ b/third_party/pyth/multisig-wh-message-builder/package.json @@ -18,7 +18,7 @@ "prepublishOnly": "npm test && npm run lint", "preversion": "npm run lint", "version": "npm run format && git add -A src", - "start": "ts-node src/index.ts" + "start": "node lib/index.js" }, "keywords": [ "pyth", @@ -33,9 +33,7 @@ "eslint": "^8.23.0", "jest": "^28.0.8", "prettier": "^2.7.1", - "ts-jest": "^28.0.8", - "ts-node": "^10.9.1", - "typescript": "^4.8.3" + "ts-jest": "^28.0.8" }, "dependencies": { "@certusone/wormhole-sdk": "^0.6.2", @@ -45,9 +43,13 @@ "@solana/web3.js": "^1.53.0", "@sqds/mesh": "^1.0.6", "@types/lodash": "^4.14.186", + "@types/node-fetch": "^2.6.2", + "@types/node-hid": "^1.3.1", "bs58": "^5.0.0", "commander": "^9.4.0", "ethers": "^5.7.0", - "lodash": "^4.17.21" + "lodash": "^4.17.21", + "ts-node": "^10.9.1", + "typescript": "^4.8.3" } } diff --git a/third_party/pyth/multisig-wh-message-builder/src/index.ts b/third_party/pyth/multisig-wh-message-builder/src/index.ts index 83d0d886..28bd7eef 100644 --- a/third_party/pyth/multisig-wh-message-builder/src/index.ts +++ b/third_party/pyth/multisig-wh-message-builder/src/index.ts @@ -15,7 +15,7 @@ import { TransactionInstruction, } from "@solana/web3.js"; import Squads from "@sqds/mesh"; -import { getIxAuthorityPDA, getIxPDA } from "@sqds/mesh"; +import { getIxAuthorityPDA, getIxPDA, getMsPDA } from "@sqds/mesh"; import { InstructionAccount } from "@sqds/mesh/lib/types"; import bs58 from "bs58"; import { program } from "commander"; @@ -26,7 +26,21 @@ import { getActiveProposals, getProposalInstructions } from "./multisig"; setDefaultWasm("node"); -type Cluster = "devnet" | "mainnet" | "localnet"; +// NOTE(2022-11-30): Naming disambiguation: +// - "mainnet" - always means a public production environment +// +// - "testnet" in Wormhole context - a collection of public testnets +// of the supported blockchain +// - "testnet" in Solana context - Never used here; The public solana +// cluster called "testnet" at https://api.testnet.solana.com +// +// - "devnet" in Wormhole context - local Tilt devnet +// - "devnet" in Solana context - The "devnet" public Solana cluster +// at https://api.devnet.solana.com +// +// - "localdevnet" - always means the Tilt devnet + +type Cluster = "devnet" | "mainnet" | "localdevnet"; type WormholeNetwork = "TESTNET" | "MAINNET" | "DEVNET"; type Config = { @@ -46,10 +60,10 @@ const CONFIG: Record = { vault: new PublicKey("FVQyHcooAtThJ83XFrNnv74BcinbRH3bRmfFamAHBfuj"), wormholeRpcEndpoint: "https://wormhole-v2-mainnet-api.certus.one", }, - localnet: { + localdevnet: { wormholeClusterName: "DEVNET", - vault: new PublicKey("2VVHgWVHi32P1aoMjHmL3e1Hf6yi7uERahXF1T5n6EHx"), // Placeholder - wormholeRpcEndpoint: "https://wormhole-v2-mainnet-api.certus.one", // Placeholder + vault: new PublicKey("DFkA5ubJSETKiFnniAsm8qRXUa7RrnnE7U9awTzbcrJF"), + wormholeRpcEndpoint: "http://guardian:7071", }, }; @@ -58,6 +72,77 @@ program .description("CLI to creating and executing multisig transactions for pyth") .version("0.1.0"); +program + .command("init-vault") + .description( + "Initialize a new multisig vault. NOTE: It's unlikely that you need run this manually. Primarily used in the Tilt local devnet" + ) + .requiredOption( + "-k --create-key
", + "Vault create key. It's a pubkey used to seed the vault's address" + ) + .requiredOption( + "-x --external-authority
", + "External authority address" + ) + .option("-c --cluster ", "solana cluster to use", "devnet") + .option("-p --payer ", "payer keypair file") + .option( + "-t --threshold ", + "Approval quorum threshold for the vault", + "2" + ) + .requiredOption( + "-i --initial-members ", + "comma-separated list of initial multisig members, without spaces" + ) + .option( + "-r --solana-rpc ", + "Solana RPC address to use", + "http://localhost:8899" + ) + .action(async (options: any) => { + let cluster: Cluster = options.cluster; + let createKeyAddr: PublicKey = new PublicKey(options.createKey); + let extAuthorityAddr: PublicKey = new PublicKey(options.externalAuthority); + + let threshold: number = parseInt(options.threshold, 10); + + let initialMembers = options.initialMembers + .split(",") + .map((m: string) => new PublicKey(m)); + + let mesh = await getSquadsClient( + cluster, + options.ledger, + options.ledgerDerivationAccount, + options.ledgerDerivationChange, + options.payer, + cluster == "localdevnet" ? options.solanaRpc : undefined + ); + + let vaultAddr = CONFIG[cluster].vault; + console.log("Creating new vault at", vaultAddr.toString()); + + try { + let _multisig = await mesh.getMultisig(vaultAddr); + + // NOTE(2022-12-08): If this check prevents you from iterating dev + // work in tilt, restart solana-devnet. + console.log( + "Reached an existing vault under the address, refusing to create." + ); + process.exit(17); // EEXIST + } catch (e: any) {} + console.log("No existing vault found, creating..."); + await mesh.createMultisig( + extAuthorityAddr, + threshold, + createKeyAddr, + initialMembers + ); + }); + program .command("create") .description("Create a new multisig transaction") @@ -308,6 +393,7 @@ program options.ledgerDerivationChange, options.wallet ); + await addMember( options.cluster, squad, @@ -361,7 +447,8 @@ async function getSquadsClient( ledger: boolean, ledgerDerivationAccount: number | undefined, ledgerDerivationChange: number | undefined, - walletPath: string + walletPath: string, + solRpcUrl?: string ) { let wallet: LedgerNodeWallet | NodeWallet; if (ledger) { @@ -379,13 +466,27 @@ async function getSquadsClient( ); console.log(`Loaded wallet with address: ${wallet.publicKey.toBase58()}`); } - const squad = - cluster === "devnet" - ? Squads.devnet(wallet) - : cluster == "mainnet" - ? Squads.mainnet(wallet) - : Squads.endpoint("http://127.0.0.1:8899", wallet); - return squad; + switch (cluster) { + case "devnet": { + return Squads.devnet(wallet); + break; + } + case "mainnet": { + return Squads.mainnet(wallet); + break; + } + case "localdevnet": { + if (solRpcUrl) { + return Squads.endpoint(solRpcUrl, wallet); + } else { + console.log("rpc:", solRpcUrl); + throw `ERROR: solRpcUrl was not specified for localdevnet!`; + } + } + default: { + throw `ERROR: unrecognized cluster ${cluster}`; + } + } } async function createTx(squad: Squads, vault: PublicKey): Promise { diff --git a/third_party/pyth/p2w_autoattest.py b/third_party/pyth/p2w_autoattest.py index 1d23d233..9bda2c63 100755 --- a/third_party/pyth/p2w_autoattest.py +++ b/third_party/pyth/p2w_autoattest.py @@ -37,18 +37,6 @@ WORMHOLE_ADDRESS = os.environ.get( # attester needs string, but we validate as int first P2W_RPC_TIMEOUT_SECS = str(int(os.environ.get("P2W_RPC_TIMEOUT_SECS", "20"))) -if SOL_AIRDROP_AMT > 0: - # Fund the p2w owner - sol_run_or_die( - "airdrop", - [ - str(SOL_AIRDROP_AMT), - "--keypair", - P2W_OWNER_KEYPAIR, - "--commitment", - "finalized", - ], - ) if P2W_INITIALIZE_SOL_CONTRACT is not None: # Get actor pubkeys @@ -56,7 +44,7 @@ if P2W_INITIALIZE_SOL_CONTRACT is not None: "address", ["--keypair", P2W_OWNER_KEYPAIR], capture_output=True ).stdout.strip() PYTH_OWNER_ADDRESS = sol_run_or_die( - "address", ["--keypair", PYTH_PROGRAM_KEYPAIR], capture_output=True + "address", ["--keypair", PYTH_PROGRAM_KEYPAIR], capture_output=True, ).stdout.strip() init_result = run_or_die( @@ -67,7 +55,7 @@ if P2W_INITIALIZE_SOL_CONTRACT is not None: "--rpc-url", SOL_RPC_URL, "--payer", - P2W_OWNER_KEYPAIR, + SOL_PAYER_KEYPAIR, "init", "--wh-prog", WORMHOLE_ADDRESS, @@ -77,6 +65,7 @@ if P2W_INITIALIZE_SOL_CONTRACT is not None: PYTH_OWNER_ADDRESS, ], capture_output=True, + debug=True, die=False, ) @@ -92,7 +81,7 @@ if P2W_INITIALIZE_SOL_CONTRACT is not None: "--rpc-url", SOL_RPC_URL, "--payer", - P2W_OWNER_KEYPAIR, + SOL_PAYER_KEYPAIR, "set-config", "--owner", P2W_OWNER_KEYPAIR, @@ -190,7 +179,7 @@ first_attest_result = run_or_die( "--rpc-url", SOL_RPC_URL, "--payer", - P2W_OWNER_KEYPAIR, + SOL_PAYER_KEYPAIR, "attest", "-f", P2W_ATTESTATION_CFG, @@ -220,7 +209,7 @@ while True: "--rpc-url", SOL_RPC_URL, "--payer", - P2W_OWNER_KEYPAIR, + SOL_PAYER_KEYPAIR, "attest", "-f", P2W_ATTESTATION_CFG, diff --git a/third_party/pyth/prepare_multisig.py b/third_party/pyth/prepare_multisig.py new file mode 100644 index 00000000..f415c324 --- /dev/null +++ b/third_party/pyth/prepare_multisig.py @@ -0,0 +1,60 @@ +# This script prepares a local Squads multisig deployment for use with +# the multisig-wh-message-builder +import errno +import os +import sys + +from pyth_utils import * + +MULTISIG_SCRIPT_CMD_PREFIX = "npm run start --".split(" ") +MULTISIG_SCRIPT_DIR = os.environ.get("MULTISIG_SCRIPT_DIR", "/root/pyth/multisig-wh-message-builder") + +MESH_KEY_DIR = "/usr/src/solana/keys/squads/" +MESH_PROGRAM_ADDR = "SMPLVC8MxZ5Bf5EfF7PaMiTCxoBAcmkbM2vkrvMK8ho" +MESH_CREATE_KEY_PATH = MESH_KEY_DIR + "create_key.json" +MESH_VAULT_EXT_AUTHORITY_KEY_PATH = MESH_KEY_DIR + "external_authority.json" + +ALICE_KEY_PATH = MESH_KEY_DIR + "member_alice.json" +BOB_KEY_PATH = MESH_KEY_DIR + "member_bob.json" +CAROL_KEY_PATH = MESH_KEY_DIR + "member_carol.json" + +create_key_addr = sol_run_or_die("address", ["--keypair", MESH_CREATE_KEY_PATH], capture_output=True).stdout.strip() +ext_authority_addr = sol_run_or_die("address", ["--keypair", MESH_VAULT_EXT_AUTHORITY_KEY_PATH], capture_output=True).stdout.strip() + +alice_addr = sol_run_or_die("address", ["--keypair", ALICE_KEY_PATH], capture_output=True).stdout.strip() +bob_addr = sol_run_or_die("address", ["--keypair", BOB_KEY_PATH], capture_output=True).stdout.strip() +carol_addr = sol_run_or_die("address", ["--keypair", CAROL_KEY_PATH], capture_output=True).stdout.strip() + +# wrap run_or_die in msg builder common cli args +def msg_builder_run_or_die(args = [], debug=False, **kwargs): + """ + Message builder boilerplate in front of run_or_die() + """ + return run_or_die( + MULTISIG_SCRIPT_CMD_PREFIX + args, cwd=MULTISIG_SCRIPT_DIR, debug=debug, **kwargs) + +# create a Multisig Vault +res = msg_builder_run_or_die([ + "init-vault", + "-k", create_key_addr, + "-x", ext_authority_addr, + "-p", SOL_PAYER_KEYPAIR, + "-c", "localdevnet", + "-r", SOL_RPC_URL, + "-i", f"{alice_addr},{bob_addr},{carol_addr}", + "-t", "2", # 2/3 threshold + ], + capture_output=True, debug=True, die=False) + +if res.returncode == errno.EEXIST: + print("WARNING: Skipping vault creation and testing, received EEXIST from script", file=sys.stderr) +elif res.returncode != 0: + print(f"ERROR: unexpected failure with code {res.returncode}", file=sys.stderr) + sys.exit(res.returncode) +else: + print("Vault created, starting test routine", file=sys.stderr) + # TODO(2022-12-08): Add test scenarios + +sys.stderr.flush() + +readiness() diff --git a/third_party/pyth/pyth_publisher.py b/third_party/pyth/pyth_publisher.py index 5ba12619..c0c2e3c7 100644 --- a/third_party/pyth/pyth_publisher.py +++ b/third_party/pyth/pyth_publisher.py @@ -12,6 +12,11 @@ import sys import threading import time +# The mock publisher needs to fund the publisher identity account, +# unable to use a separate payer +SOL_AIRDROP_AMT = int(os.environ.get("SOL_AIRDROP_AMT", 0)) + + class PythAccEndpoint(BaseHTTPRequestHandler): """ A dumb endpoint to respond with a JSON containing Pyth symbol and mapping addresses diff --git a/third_party/pyth/pyth_utils.py b/third_party/pyth/pyth_utils.py index 69b557fe..e545c011 100644 --- a/third_party/pyth/pyth_utils.py +++ b/third_party/pyth/pyth_utils.py @@ -6,6 +6,12 @@ import subprocess import sys from http.client import HTTPConnection + +# A generic unprivileged payer account with funds +SOL_PAYER_KEYPAIR = os.environ.get( + "SOL_PAYER_KEYPAIR", "/usr/src/solana/keys/solana-devnet.json" +) + # Settings specific to local devnet Pyth instance PYTH = os.environ.get("PYTH", "./pyth") PYTH_ADMIN = os.environ.get("PYTH_ADMIN", "./pyth_admin") @@ -17,6 +23,7 @@ PYTH_PROGRAM_SO_PATH = os.environ.get("PYTH_PROGRAM_SO", "../target/oracle.so") PYTH_PUBLISHER_KEYPAIR = os.environ.get( "PYTH_PUBLISHER_KEYPAIR", f"{PYTH_KEY_STORE}/publish_key_pair.json" ) + # How long to sleep between mock Pyth price updates PYTH_PUBLISHER_INTERVAL_SECS = float(os.environ.get("PYTH_PUBLISHER_INTERVAL_SECS", "5")) PYTH_TEST_SYMBOL_COUNT = int(os.environ.get("PYTH_TEST_SYMBOL_COUNT", "11")) @@ -34,9 +41,6 @@ PYTH_MAPPING_KEYPAIR = os.environ.get( "PYTH_MAPPING_KEYPAIR", f"{PYTH_KEY_STORE}/mapping_key_pair.json" ) -# 0 setting disables airdropping -SOL_AIRDROP_AMT = int(os.environ.get("SOL_AIRDROP_AMT", 0)) - # SOL RPC settings SOL_RPC_HOST = os.environ.get("SOL_RPC_HOST", "solana-devnet") SOL_RPC_PORT = int(os.environ.get("SOL_RPC_PORT", 8899)) @@ -48,7 +52,7 @@ SOL_RPC_URL = os.environ.get( READINESS_PORT = int(os.environ.get("READINESS_PORT", "2000")) -def run_or_die(args, die=True, **kwargs): +def run_or_die(args, die=True, debug=False, **kwargs): """ Opinionated subprocess.run() call with fancy logging """ @@ -57,23 +61,29 @@ def run_or_die(args, die=True, **kwargs): sys.stderr.flush() ret = subprocess.run(args, text=True, **kwargs) - if ret.returncode != 0: + if ret.returncode == 0: + print(f"CMD OK\t{args_readable}", file=sys.stderr) + else: print(f"CMD FAIL {ret.returncode}\t{args_readable}", file=sys.stderr) + if debug: out = ret.stdout if ret.stdout is not None else "" err = ret.stderr if ret.stderr is not None else "" print(f"CMD STDOUT\n{out}", file=sys.stderr) print(f"CMD STDERR\n{err}", file=sys.stderr) + sys.stderr.flush() + + if ret.returncode != 0: if die: sys.exit(ret.returncode) else: print(f'{"CMD DIE FALSE"}', file=sys.stderr) + sys.stderr.flush() + + - else: - print(f"CMD OK\t{args_readable}", file=sys.stderr) - sys.stderr.flush() return ret @@ -143,4 +153,5 @@ def readiness(): with socketserver.TCPServer( ("0.0.0.0", READINESS_PORT), ReadinessTCPHandler ) as srv: + print(f"Opening port {READINESS_PORT} for readiness TCP probe") srv.serve_forever()