Refactor our CW contract + cleanups (#262)
* Use the cw sdk * Fix clippy warnings * Use Addr instead of String * Format the code (cargo +nightly fmt) * Depend on wormhole git instead of local dependency * Add error enum * refactor * Refactor errors * certusone->wormhole-foundation rename It will fix some compile problems * Build wormhole contract from their repo * remove wormhole package * Remove memmap2 patch It was not needed anymore with the newer verison of solana (wormhole removed it too) * Rename pyth-bridge to pyth-cosmwasm
This commit is contained in:
parent
8cea878c34
commit
b4c8f2e470
|
@ -14,7 +14,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./cosmwasm/contracts/pyth-bridge
|
||||
working-directory: ./cosmwasm/contracts/pyth
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build
|
||||
|
|
|
@ -28,7 +28,7 @@ and code reviews are our most important tools to accomplish that.
|
|||
well-defined changes.
|
||||
|
||||
Documentation for the in-the-wild deployments lives in the
|
||||
[wormhole-networks](https://github.com/certusone/wormhole-networks) repository.
|
||||
[wormhole-networks](https://github.com/wormhole-foundation/wormhole-networks) repository.
|
||||
|
||||
See [DEVELOP.md](./DEVELOP.md) for more information on how to run the development environment.
|
||||
|
||||
|
|
|
@ -25,5 +25,5 @@ RUN --mount=type=cache,target=/root/.cache \
|
|||
--mount=type=cache,target=/usr/local/cargo/registry,id=cargo_registry \
|
||||
--mount=type=cache,target=target,id=cargo_registry \
|
||||
set -xe && \
|
||||
cargo install bridge_client --git https://github.com/certusone/wormhole --tag $WORMHOLE_TAG --locked --root /usr/local && \
|
||||
cargo install token_bridge_client --git https://github.com/certusone/wormhole --tag $WORMHOLE_TAG --locked --root /usr/local
|
||||
cargo install bridge_client --git https://github.com/wormhole-foundation/wormhole --tag $WORMHOLE_TAG --locked --root /usr/local && \
|
||||
cargo install token_bridge_client --git https://github.com/wormhole-foundation/wormhole --tag $WORMHOLE_TAG --locked --root /usr/local
|
||||
|
|
|
@ -1,12 +1,27 @@
|
|||
# This is a multi-stage docker file, first stage builds contracts
|
||||
# And the second one creates node.js environment to deploy them
|
||||
FROM cosmwasm/workspace-optimizer:0.12.6@sha256:e6565a5e87c830ef3e8775a9035006b38ad0aaf0a96319158c802457b1dd1d08 AS builder
|
||||
# This is a multi-stage docker file, first and second stage builds contracts
|
||||
# And the third one creates node.js environment to deploy them
|
||||
|
||||
FROM cosmwasm/workspace-optimizer:0.12.6@sha256:e6565a5e87c830ef3e8775a9035006b38ad0aaf0a96319158c802457b1dd1d08 AS wormhole_builder
|
||||
|
||||
WORKDIR /tmp/wormhole-repo
|
||||
|
||||
ARG WORMHOLE_REV=2.8.9
|
||||
ADD https://github.com/certusone/wormhole/archive/refs/tags/v${WORMHOLE_REV}.tar.gz .
|
||||
RUN tar -xvf v${WORMHOLE_REV}.tar.gz
|
||||
RUN mv wormhole-${WORMHOLE_REV}/cosmwasm/Cargo.lock /code/
|
||||
RUN mv wormhole-${WORMHOLE_REV}/cosmwasm/Cargo.toml /code/
|
||||
RUN mv wormhole-${WORMHOLE_REV}/cosmwasm/contracts /code/contracts
|
||||
|
||||
WORKDIR /code
|
||||
RUN --mount=type=cache,target=/code/target,id=cosmwasm_wormhole_target --mount=type=cache,target=/usr/local/cargo/registry optimize_workspace.sh
|
||||
|
||||
FROM cosmwasm/workspace-optimizer:0.12.6@sha256:e6565a5e87c830ef3e8775a9035006b38ad0aaf0a96319158c802457b1dd1d08 AS pyth_builder
|
||||
|
||||
COPY cosmwasm/Cargo.lock /code/
|
||||
COPY cosmwasm/Cargo.toml /code/
|
||||
COPY cosmwasm/contracts /code/contracts
|
||||
COPY third_party/pyth/p2w-sdk/rust /third_party/pyth/p2w-sdk/rust
|
||||
RUN --mount=type=cache,target=/code/target,id=cosmwasm_target --mount=type=cache,target=/usr/local/cargo/registry optimize_workspace.sh
|
||||
RUN --mount=type=cache,target=/code/target,id=cosmwasm_pyth_target --mount=type=cache,target=/usr/local/cargo/registry optimize_workspace.sh
|
||||
|
||||
# Contract deployment stage
|
||||
FROM node:16-buster-slim@sha256:93c9fc3550f5f7d159f282027228e90e3a7f8bf38544758024f005e82607f546
|
||||
|
@ -15,7 +30,8 @@ RUN apt update && apt install netcat curl jq -y
|
|||
|
||||
WORKDIR /app/tools
|
||||
|
||||
COPY --from=builder /code/artifacts /app/artifacts
|
||||
COPY --from=wormhole_builder /code/artifacts/wormhole.wasm /app/artifacts/wormhole.wasm
|
||||
COPY --from=pyth_builder /code/artifacts/pyth_cosmwasm.wasm /app/artifacts/pyth_cosmwasm.wasm
|
||||
|
||||
COPY ./cosmwasm/tools/package.json ./cosmwasm/tools/package-lock.json /app/tools/
|
||||
RUN --mount=type=cache,uid=1000,gid=1000,target=/home/node/.npm \
|
||||
|
|
|
@ -29,7 +29,7 @@ RUN cargo init --lib /tmp/decoy-crate && \
|
|||
WORKDIR /usr/src/bridge
|
||||
|
||||
ARG WORMHOLE_REV=2.8.9
|
||||
ADD https://github.com/certusone/wormhole/archive/refs/tags/v${WORMHOLE_REV}.tar.gz .
|
||||
ADD https://github.com/wormhole-foundation/wormhole/archive/refs/tags/v${WORMHOLE_REV}.tar.gz .
|
||||
RUN tar -xvf v${WORMHOLE_REV}.tar.gz
|
||||
RUN mv wormhole-${WORMHOLE_REV} wormhole
|
||||
# RUN mkdir -p /usr/src/bridge/wormhole/solana/target
|
||||
|
|
|
@ -6,7 +6,7 @@ Pyth2Wormhole. The base repository is a fork from Certus One's reference
|
|||
for building projects based on Wormhole's various SDKs. Much of the existing
|
||||
documentation from there will also apply to this repository.
|
||||
|
||||
[Wormhole]: https://github.com/certusone/wormhole
|
||||
[Wormhole]: https://github.com/wormhole-foundation/wormhole
|
||||
|
||||
Within this monorepo you will find the following subprojects:
|
||||
|
||||
|
|
|
@ -63,9 +63,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
|||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.5.1"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bdca834647821e0b13d9539a8634eb62d3501b6b6c2cec1722786ee6671b851"
|
||||
checksum = "ea2b2456fd614d856680dcd9fcc660a51a820fa09daef2e49772b56a193c8474"
|
||||
|
||||
[[package]]
|
||||
name = "bigint"
|
||||
|
@ -146,15 +146,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.10.0"
|
||||
version = "3.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
|
||||
checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
|
||||
|
||||
[[package]]
|
||||
name = "bytecheck"
|
||||
version = "0.6.8"
|
||||
version = "0.6.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a31f923c2db9513e4298b72df143e6e655a759b3d6a0966df18f81223fff54f"
|
||||
checksum = "d11cac2c12b5adc6570dad2ee1b87eff4955dac476fe12d81e5fdd352e52406f"
|
||||
dependencies = [
|
||||
"bytecheck_derive",
|
||||
"ptr_meta",
|
||||
|
@ -162,9 +162,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bytecheck_derive"
|
||||
version = "0.6.8"
|
||||
version = "0.6.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edb17c862a905d912174daa27ae002326fff56dc8b8ada50a0a5f0976cb174f0"
|
||||
checksum = "13e576ebe98e605500b3c8041bb888e966653577172df6dd97398714eb30b9bf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -179,9 +179,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
|||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.2.0"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0b3de4a0c5e67e16066a0715723abd91edc2f9001d09c46e1dca929351e130e"
|
||||
checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
|
@ -279,9 +279,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.2"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
|
||||
checksum = "dc948ebb96241bb40ab73effeb80d9f93afaad49359d159a5e61be51619fe813"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -357,9 +357,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.5"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c"
|
||||
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
|
@ -367,9 +367,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
|
||||
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
|
@ -378,9 +378,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.9"
|
||||
version = "0.9.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d"
|
||||
checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
|
@ -392,9 +392,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.10"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83"
|
||||
checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
|
@ -448,9 +448,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "curve25519-dalek"
|
||||
version = "3.2.1"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0"
|
||||
checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"digest",
|
||||
|
@ -459,6 +459,17 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw-storage-plus"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "648b1507290bbc03a8d88463d7cd9b04b1fa0155e5eef366c4fa052b9caaac7a"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw-utils"
|
||||
version = "0.13.4"
|
||||
|
@ -471,6 +482,18 @@ dependencies = [
|
|||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw2"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04cf4639517490dd36b333bbd6c4fbd92e325fd0acf4683b41753bc5eb63bfc1"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw20"
|
||||
version = "0.13.4"
|
||||
|
@ -483,6 +506,38 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw20-base"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0306e606581f4fb45e82bcbb7f0333179ed53dd949c6523f01a99b4bfc1475a0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"cw2",
|
||||
"cw20",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw20-wrapped-2"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/wormhole-foundation/wormhole?tag=v2.8.9#e47f9e481ef84d4dea7a94c9eafbf3b180892466"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cosmwasm-storage",
|
||||
"cw-storage-plus",
|
||||
"cw2",
|
||||
"cw20",
|
||||
"cw20-base",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.13.4"
|
||||
|
@ -543,9 +598,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "dyn-clone"
|
||||
version = "1.0.8"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d07a982d1fb29db01e5a59b1918e03da4df7297eaeee7686ac45542fd4e59c8"
|
||||
checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2"
|
||||
|
||||
[[package]]
|
||||
name = "dynasm"
|
||||
|
@ -614,9 +669,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.7.0"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be"
|
||||
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||
|
||||
[[package]]
|
||||
name = "elliptic-curve"
|
||||
|
@ -700,9 +755,9 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
|||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.7.0"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
|
||||
checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
|
||||
dependencies = [
|
||||
"instant",
|
||||
]
|
||||
|
@ -741,9 +796,9 @@ checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e"
|
|||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.5"
|
||||
version = "0.14.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
|
||||
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
|
@ -884,15 +939,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.2"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
||||
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.58"
|
||||
version = "0.3.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27"
|
||||
checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
@ -941,9 +996,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.126"
|
||||
version = "0.2.132"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
|
@ -1002,9 +1057,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
|||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.5.5"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a79b39c93a7a5a27eeaf9a23b5ff43f1b9e0ad6b1cdd441140ae53c35613fc7"
|
||||
checksum = "95af15f345b17af2efc8ead6080fb8bc376f8cec1b35277b935637595fe77498"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -1066,9 +1121,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.13.0"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
|
||||
checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
|
@ -1143,9 +1198,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.40"
|
||||
version = "1.0.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
|
||||
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
@ -1180,7 +1235,7 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyth-bridge"
|
||||
name = "pyth-cosmwasm"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bigint",
|
||||
|
@ -1192,7 +1247,7 @@ dependencies = [
|
|||
"k256 0.9.6",
|
||||
"lazy_static",
|
||||
"p2w-sdk",
|
||||
"pyth-sdk",
|
||||
"pyth-sdk-cw",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
|
@ -1200,14 +1255,14 @@ dependencies = [
|
|||
"sha3",
|
||||
"terraswap",
|
||||
"thiserror",
|
||||
"wormhole-bridge-terra",
|
||||
"wormhole-bridge-terra-2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyth-sdk"
|
||||
version = "0.4.2"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f262b88557d8f152a247e1be786a8359d63112fac0a6e49fa41082a8ef789e8d"
|
||||
checksum = "f5c805ba3dfb5b7ed6a8ffa62ec38391f485a79c7cf6b3b11d3bd44fb0325824"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"borsh-derive",
|
||||
|
@ -1217,10 +1272,23 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.20"
|
||||
name = "pyth-sdk-cw"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
|
||||
checksum = "4f1575db8f501031a810aa5fe5da8ac3868bfcee7ce3deb27d6354c81a5833cb"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cosmwasm-storage",
|
||||
"pyth-sdk",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -1269,9 +1337,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.13"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
|
||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
@ -1367,15 +1435,15 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
|||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.8"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24c8ad4f0c00e1eb5bc7614d236a7f1300e3dbd76b68cac8e06fb00b015ad8d8"
|
||||
checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.10"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
|
||||
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
|
@ -1428,9 +1496,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.139"
|
||||
version = "1.0.144"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6"
|
||||
checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
@ -1446,18 +1514,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_bytes"
|
||||
version = "0.11.6"
|
||||
version = "0.11.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "212e73464ebcde48d723aa02eb270ba62eff38a9b732df31f33f1b4e145f3a54"
|
||||
checksum = "cfc50e8183eeeb6178dcb167ae34a8051d63535023ae38b5d8d12beae193d37b"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.139"
|
||||
version = "1.0.144"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc1d3230c1de7932af58ad8ffbe1d784bd55efd5a9d84ac24f69c72d83543dfb"
|
||||
checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1477,9 +1545,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.82"
|
||||
version = "1.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
|
||||
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
|
@ -1557,9 +1625,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.98"
|
||||
version = "1.0.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
|
||||
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1588,9 +1656,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "terraswap"
|
||||
version = "2.7.0"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13d4151fb37794dbae0a0be342fc8474566c7b5d1f3086cd96aea9b0d480b068"
|
||||
checksum = "b9540f8489ec6e098de380c9fa8fa81fa95e502f87d63705aa6fba56817ad1a7"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cosmwasm-storage",
|
||||
|
@ -1602,18 +1670,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.31"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
||||
checksum = "3d0a539a918745651435ac7db7a18761589a94cd7e94cd56999f828bf73c8a57"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.31"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
||||
checksum = "c251e90f708e16c49a16f4917dc2131e75222b72edfa9cb7f7c58ae56aae0c09"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1631,9 +1699,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.35"
|
||||
version = "0.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160"
|
||||
checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"log",
|
||||
|
@ -1655,9 +1723,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.28"
|
||||
version = "0.1.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7"
|
||||
checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
@ -1682,9 +1750,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.2"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7"
|
||||
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
|
@ -1706,9 +1774,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.81"
|
||||
version = "0.2.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994"
|
||||
checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
|
@ -1716,13 +1784,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.81"
|
||||
version = "0.2.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a"
|
||||
checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
|
@ -1731,9 +1799,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.81"
|
||||
version = "0.2.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa"
|
||||
checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
|
@ -1741,9 +1809,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.81"
|
||||
version = "0.2.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048"
|
||||
checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1754,9 +1822,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.81"
|
||||
version = "0.2.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be"
|
||||
checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a"
|
||||
|
||||
[[package]]
|
||||
name = "wasmer"
|
||||
|
@ -1990,13 +2058,13 @@ checksum = "52144d4c78e5cf8b055ceab8e5fa22814ce4315d6002ad32cfd914f37c12fd65"
|
|||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.2.5"
|
||||
version = "4.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae"
|
||||
checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b"
|
||||
dependencies = [
|
||||
"either",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2022,12 +2090,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "wormhole-bridge-terra"
|
||||
name = "wormhole-bridge-terra-2"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/wormhole-foundation/wormhole?tag=v2.8.9#e47f9e481ef84d4dea7a94c9eafbf3b180892466"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cosmwasm-storage",
|
||||
"cosmwasm-vm",
|
||||
"cw20",
|
||||
"cw20-base",
|
||||
"cw20-wrapped-2",
|
||||
"generic-array",
|
||||
"getrandom 0.2.7",
|
||||
"hex",
|
||||
|
@ -2035,18 +2106,12 @@ dependencies = [
|
|||
"lazy_static",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha3",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.3.0"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"
|
||||
|
||||
[[patch.unused]]
|
||||
name = "memmap2"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/certusone/wormhole#7a1b1344a19db22935d8532135ac249da128b4e7"
|
||||
checksum = "d68d9dcec5f9b43a30d38c49f91dfedfaac384cb8f085faca366c26207dd1619"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[workspace]
|
||||
members = ["contracts/wormhole","contracts/pyth-bridge"]
|
||||
members = ["contracts/pyth"]
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
|
@ -12,5 +12,3 @@ panic = 'abort'
|
|||
incremental = false
|
||||
overflow-checks = true
|
||||
|
||||
[patch.crates-io]
|
||||
memmap2 = { git = "https://github.com/certusone/wormhole", package = "memmap2" }
|
||||
|
|
|
@ -22,17 +22,17 @@ bash build.sh
|
|||
|
||||
This command will build and save the Pyth contract in the `artifact` directory.
|
||||
|
||||
Then, to deploy the Pyth contract (`pyth_bridge.wasm`), run the following command in the `tools` directory:
|
||||
Then, to deploy the Pyth contract (`pyth_cosmwasm.wasm`), run the following command in the `tools` directory:
|
||||
|
||||
``` sh
|
||||
npm ci # Do it only once to install the required packages
|
||||
npm run deploy-pyth -- --network testnet --artifact ../artifacts/pyth_bridge.wasm --mnemonic "..."
|
||||
npm run deploy-pyth -- --network testnet --artifact ../artifacts/pyth_cosmwasm.wasm --mnemonic "..."
|
||||
```
|
||||
|
||||
If successful, this command will print something along the lines of:
|
||||
|
||||
``` sh
|
||||
Storing WASM: ../artifacts/pyth_bridge.wasm (367689 bytes)
|
||||
Storing WASM: ../artifacts/pyth_cosmwasm.wasm (367689 bytes)
|
||||
Deploy fee: 88446uluna
|
||||
Code ID: 2435
|
||||
```
|
||||
|
@ -45,17 +45,17 @@ If you want to instantiate a new contract after your deployment, pass `--instant
|
|||
This command will upload the code and instantiates a new Pyth contract with the resulting code id:
|
||||
|
||||
``` sh
|
||||
npm run deploy-pyth -- --network testnet --artifact ../artifacts/pyth_bridge.wasm --mnemonic "..." --instantiate
|
||||
npm run deploy-pyth -- --network testnet --artifact ../artifacts/pyth_cosmwasm.wasm --mnemonic "..." --instantiate
|
||||
```
|
||||
|
||||
If successful, the output should look like so:
|
||||
```
|
||||
Storing WASM: ../artifacts/pyth_bridge.wasm (183749 bytes)
|
||||
Storing WASM: ../artifacts/pyth_cosmwasm.wasm (183749 bytes)
|
||||
Deploy fee: 44682uluna
|
||||
Code ID: 53199
|
||||
Instantiating a contract
|
||||
Sleeping for 10 seconds for store transaction to finalize.
|
||||
Instantiated Pyth Bridge at terra123456789yelw23uh22nadqlyjvtl7s5527er97 (0x0000000000000000000000001234567896267ee5479752a7d683e49317ff4294)
|
||||
Instantiated Pyth at terra123456789yelw23uh22nadqlyjvtl7s5527er97 (0x0000000000000000000000001234567896267ee5479752a7d683e49317ff4294)
|
||||
Deployed Pyth contract at terra123456789yelw23uh22nadqlyjvtl7s5527er97
|
||||
```
|
||||
|
||||
|
@ -64,12 +64,12 @@ If you want to upgrade an existing contract pass `--migrate --contract terra1234
|
|||
This command will upload the code, and with the resulting code id, will migrate the existing contract to the new one:
|
||||
|
||||
``` sh
|
||||
npm run deploy-pyth -- --network testnet --artifact ../artifacts/pyth_bridge.wasm --mnemonic "..." --migrate --contract "terra123..."
|
||||
npm run deploy-pyth -- --network testnet --artifact ../artifacts/pyth_cosmwasm.wasm --mnemonic "..." --migrate --contract "terra123..."
|
||||
```
|
||||
|
||||
If successful, the output should look like so:
|
||||
```
|
||||
Storing WASM: ../artifacts/pyth_bridge.wasm (183749 bytes)
|
||||
Storing WASM: ../artifacts/pyth_cosmwasm.wasm (183749 bytes)
|
||||
Deploy fee: 44682uluna
|
||||
Code ID: 53227
|
||||
Sleeping for 10 seconds for store transaction to finalize.
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
use cosmwasm_std::Binary;
|
||||
use pyth_sdk::{
|
||||
PriceFeed,
|
||||
PriceIdentifier,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{
|
||||
Deserialize,
|
||||
Serialize,
|
||||
};
|
||||
|
||||
use crate::state::PythDataSource;
|
||||
|
||||
type HumanAddr = String;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct InstantiateMsg {
|
||||
pub wormhole_contract: HumanAddr,
|
||||
pub pyth_emitter: Binary,
|
||||
pub pyth_emitter_chain: u16,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ExecuteMsg {
|
||||
SubmitVaa { data: Binary },
|
||||
AddDataSource { data_source: PythDataSource },
|
||||
RemoveDataSource { data_source: PythDataSource },
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct MigrateMsg {}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum QueryMsg {
|
||||
PriceFeed { id: PriceIdentifier },
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct PriceFeedResponse {
|
||||
/// Pyth Price Feed
|
||||
pub price_feed: PriceFeed,
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "pyth-bridge"
|
||||
name = "pyth-cosmwasm"
|
||||
version = "0.1.0"
|
||||
authors = ["Wormhole Contributors <contact@certus.one>"]
|
||||
edition = "2018"
|
||||
|
@ -20,7 +20,7 @@ schemars = "0.8.1"
|
|||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
serde_derive = { version = "1.0.103"}
|
||||
terraswap = "2.4.0"
|
||||
wormhole-bridge-terra = { path = "../wormhole", features = ["library"] }
|
||||
wormhole-bridge-terra-2 = { git = "https://github.com/wormhole-foundation/wormhole", tag = "v2.8.9", features = ["library"] }
|
||||
thiserror = { version = "1.0.20" }
|
||||
k256 = { version = "0.9.4", default-features = false, features = ["ecdsa"] }
|
||||
sha3 = { version = "0.9.1", default-features = false }
|
||||
|
@ -29,7 +29,7 @@ hex = "0.4.2"
|
|||
lazy_static = "1.4.0"
|
||||
bigint = "4"
|
||||
p2w-sdk = { path = "../../../third_party/pyth/p2w-sdk/rust" }
|
||||
pyth-sdk = "0.4.2"
|
||||
pyth-sdk-cw = "0.2.0"
|
||||
|
||||
[dev-dependencies]
|
||||
cosmwasm-vm = { version = "1.0.0", default-features = false }
|
|
@ -13,10 +13,9 @@ use cosmwasm_std::{
|
|||
StdResult,
|
||||
Timestamp,
|
||||
WasmQuery,
|
||||
StdError,
|
||||
};
|
||||
|
||||
use pyth_sdk::{
|
||||
use pyth_sdk_cw::{
|
||||
PriceFeed,
|
||||
PriceIdentifier,
|
||||
PriceStatus,
|
||||
|
@ -37,13 +36,14 @@ use crate::state::{
|
|||
price_info_read,
|
||||
ConfigInfo,
|
||||
PriceInfo,
|
||||
VALID_TIME_PERIOD,
|
||||
PythDataSource,
|
||||
VALID_TIME_PERIOD,
|
||||
};
|
||||
|
||||
use crate::error::PythContractError;
|
||||
|
||||
use p2w_sdk::BatchPriceAttestation;
|
||||
|
||||
use wormhole::error::ContractError;
|
||||
use wormhole::msg::QueryMsg as WormholeQueryMsg;
|
||||
use wormhole::state::ParsedVAA;
|
||||
|
||||
|
@ -61,10 +61,10 @@ pub fn instantiate(
|
|||
) -> StdResult<Response> {
|
||||
// Save general wormhole and pyth info
|
||||
let state = ConfigInfo {
|
||||
owner: info.sender.to_string(),
|
||||
wormhole_contract: msg.wormhole_contract,
|
||||
data_sources: HashSet::from([PythDataSource {
|
||||
emitter: msg.pyth_emitter,
|
||||
owner: info.sender,
|
||||
wormhole_contract: deps.api.addr_validate(msg.wormhole_contract.as_ref())?,
|
||||
data_sources: HashSet::from([PythDataSource {
|
||||
emitter: msg.pyth_emitter,
|
||||
pyth_emitter_chain: msg.pyth_emitter_chain,
|
||||
}]),
|
||||
};
|
||||
|
@ -76,7 +76,7 @@ pub fn instantiate(
|
|||
pub fn parse_vaa(deps: DepsMut, block_time: u64, data: &Binary) -> StdResult<ParsedVAA> {
|
||||
let cfg = config_read(deps.storage).load()?;
|
||||
let vaa: ParsedVAA = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart {
|
||||
contract_addr: cfg.wormhole_contract,
|
||||
contract_addr: cfg.wormhole_contract.to_string(),
|
||||
msg: to_binary(&WormholeQueryMsg::VerifyVAA {
|
||||
vaa: data.clone(),
|
||||
block_time,
|
||||
|
@ -89,8 +89,10 @@ pub fn parse_vaa(deps: DepsMut, block_time: u64, data: &Binary) -> StdResult<Par
|
|||
pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> StdResult<Response> {
|
||||
match msg {
|
||||
ExecuteMsg::SubmitVaa { data } => submit_vaa(deps, env, info, &data),
|
||||
ExecuteMsg::AddDataSource { data_source } => add_data_source(deps, env, info, data_source ),
|
||||
ExecuteMsg::RemoveDataSource { data_source } => remove_data_source(deps, env, info, data_source ),
|
||||
ExecuteMsg::AddDataSource { data_source } => add_data_source(deps, env, info, data_source),
|
||||
ExecuteMsg::RemoveDataSource { data_source } => {
|
||||
remove_data_source(deps, env, info, data_source)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,7 +110,7 @@ fn submit_vaa(
|
|||
|
||||
let data = &vaa.payload;
|
||||
let batch_attestation = BatchPriceAttestation::deserialize(&data[..])
|
||||
.map_err(|_| ContractError::InvalidVAA.std())?;
|
||||
.map_err(|_| PythContractError::InvalidUpdatePayload)?;
|
||||
|
||||
process_batch_attestation(deps, env, &batch_attestation)
|
||||
}
|
||||
|
@ -122,25 +124,22 @@ fn add_data_source(
|
|||
let mut state = config_read(deps.storage).load()?;
|
||||
|
||||
if state.owner != info.sender {
|
||||
return ContractError::PermissionDenied.std_err();
|
||||
return Err(PythContractError::PermissionDenied)?;
|
||||
}
|
||||
|
||||
if state.data_sources.insert(data_source.clone()) == false {
|
||||
return Err(StdError::GenericErr { msg: format!("Data source already exists") });
|
||||
if !state.data_sources.insert(data_source.clone()) {
|
||||
return Err(PythContractError::DataSourceAlreadyExists)?;
|
||||
}
|
||||
|
||||
config(deps.storage).save(&state)?;
|
||||
|
||||
Ok(Response::new()
|
||||
.add_attribute("action", "add_data_source")
|
||||
.add_attribute(
|
||||
"data_source_emitter",
|
||||
format!("{}", data_source.emitter),
|
||||
)
|
||||
.add_attribute(
|
||||
"data_source_emitter_chain",
|
||||
format!("{}", data_source.pyth_emitter_chain)
|
||||
))
|
||||
.add_attribute("action", "add_data_source")
|
||||
.add_attribute("data_source_emitter", format!("{}", data_source.emitter))
|
||||
.add_attribute(
|
||||
"data_source_emitter_chain",
|
||||
format!("{}", data_source.pyth_emitter_chain),
|
||||
))
|
||||
}
|
||||
|
||||
fn remove_data_source(
|
||||
|
@ -150,27 +149,24 @@ fn remove_data_source(
|
|||
data_source: PythDataSource,
|
||||
) -> StdResult<Response> {
|
||||
let mut state = config_read(deps.storage).load()?;
|
||||
|
||||
|
||||
if state.owner != info.sender {
|
||||
return ContractError::PermissionDenied.std_err();
|
||||
return Err(PythContractError::PermissionDenied)?;
|
||||
}
|
||||
|
||||
if state.data_sources.remove(&data_source) == false {
|
||||
return Err(StdError::GenericErr { msg: format!("Data source does not exist") });
|
||||
if !state.data_sources.remove(&data_source) {
|
||||
return Err(PythContractError::DataSourceDoesNotExists)?;
|
||||
}
|
||||
|
||||
config(deps.storage).save(&state)?;
|
||||
|
||||
Ok(Response::new()
|
||||
.add_attribute("action", "remove_data_source")
|
||||
.add_attribute(
|
||||
"data_source_emitter",
|
||||
format!("{}", data_source.emitter),
|
||||
)
|
||||
.add_attribute(
|
||||
"data_source_emitter_chain",
|
||||
format!("{}", data_source.pyth_emitter_chain)
|
||||
))
|
||||
.add_attribute("action", "remove_data_source")
|
||||
.add_attribute("data_source_emitter", format!("{}", data_source.emitter))
|
||||
.add_attribute(
|
||||
"data_source_emitter_chain",
|
||||
format!("{}", data_source.pyth_emitter_chain),
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
|
@ -178,11 +174,11 @@ fn remove_data_source(
|
|||
// (Solana)
|
||||
fn verify_vaa_sender(state: &ConfigInfo, vaa: &ParsedVAA) -> StdResult<()> {
|
||||
let vaa_data_source = PythDataSource {
|
||||
emitter: vaa.emitter_address.clone().into(),
|
||||
pyth_emitter_chain: vaa.emitter_chain
|
||||
emitter: vaa.emitter_address.clone().into(),
|
||||
pyth_emitter_chain: vaa.emitter_chain,
|
||||
};
|
||||
if !state.data_sources.contains(&vaa_data_source) {
|
||||
return ContractError::InvalidVAA.std_err();
|
||||
return Err(PythContractError::InvalidUpdateEmitter)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -282,18 +278,17 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult<Binary> {
|
|||
|
||||
pub fn query_price_feed(deps: Deps, env: Env, address: &[u8]) -> StdResult<PriceFeedResponse> {
|
||||
match price_info_read(deps.storage).load(address) {
|
||||
Ok(mut terra_price_info) => {
|
||||
Ok(mut price_info) => {
|
||||
let env_time_sec = env.block.time.seconds();
|
||||
let price_pub_time_sec = terra_price_info.price_feed.publish_time as u64;
|
||||
let price_pub_time_sec = price_info.price_feed.publish_time as u64;
|
||||
|
||||
// Cases that it will cover:
|
||||
// - This will ensure to set status unknown if the price has become very old and hasn't
|
||||
// updated yet.
|
||||
// - If a price has arrived very late to terra it will set the status to unknown.
|
||||
// - If a price has arrived very late to this chain it will set the status to unknown.
|
||||
// - If a price is coming from future it's tolerated up to VALID_TIME_PERIOD seconds
|
||||
// (using abs diff) but more than that is set to unknown, the reason is huge clock
|
||||
// difference means there exists a problem in a either Terra or Solana blockchain and
|
||||
// if it is Solana we don't want to propagate Solana internal problems to Terra
|
||||
// (using abs diff) but more than that is set to unknown, the reason could be the
|
||||
// clock time drift between the source and target chains.
|
||||
let time_abs_diff = if env_time_sec > price_pub_time_sec {
|
||||
env_time_sec - price_pub_time_sec
|
||||
} else {
|
||||
|
@ -301,14 +296,14 @@ pub fn query_price_feed(deps: Deps, env: Env, address: &[u8]) -> StdResult<Price
|
|||
};
|
||||
|
||||
if time_abs_diff > VALID_TIME_PERIOD.as_secs() {
|
||||
terra_price_info.price_feed.status = PriceStatus::Unknown;
|
||||
price_info.price_feed.status = PriceStatus::Unknown;
|
||||
}
|
||||
|
||||
Ok(PriceFeedResponse {
|
||||
price_feed: terra_price_info.price_feed,
|
||||
price_feed: price_info.price_feed,
|
||||
})
|
||||
}
|
||||
Err(_) => ContractError::AssetNotFound.std_err(),
|
||||
Err(_) => Err(PythContractError::PriceFeedNotFound)?
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -317,12 +312,15 @@ mod test {
|
|||
use cosmwasm_std::testing::{
|
||||
mock_dependencies,
|
||||
mock_env,
|
||||
mock_info,
|
||||
MockApi,
|
||||
MockQuerier,
|
||||
MockStorage,
|
||||
mock_info,
|
||||
};
|
||||
use cosmwasm_std::OwnedDeps;
|
||||
use cosmwasm_std::{
|
||||
Addr,
|
||||
OwnedDeps,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -346,19 +344,28 @@ mod test {
|
|||
}
|
||||
}
|
||||
|
||||
fn create_zero_config_info() -> ConfigInfo {
|
||||
ConfigInfo {
|
||||
owner: Addr::unchecked(String::default()),
|
||||
wormhole_contract: Addr::unchecked(String::default()),
|
||||
data_sources: HashSet::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_price_feed(expo: i32) -> PriceFeed {
|
||||
let mut price_feed = PriceFeed::default();
|
||||
price_feed.expo = expo;
|
||||
price_feed
|
||||
}
|
||||
|
||||
fn create_data_sources(pyth_emitter: Vec<u8>, pyth_emitter_chain: u16) -> HashSet<PythDataSource> {
|
||||
HashSet::from([
|
||||
PythDataSource {
|
||||
emitter: pyth_emitter.into(),
|
||||
pyth_emitter_chain
|
||||
}
|
||||
])
|
||||
fn create_data_sources(
|
||||
pyth_emitter: Vec<u8>,
|
||||
pyth_emitter_chain: u16,
|
||||
) -> HashSet<PythDataSource> {
|
||||
HashSet::from([PythDataSource {
|
||||
emitter: pyth_emitter.into(),
|
||||
pyth_emitter_chain,
|
||||
}])
|
||||
}
|
||||
|
||||
/// Updates the price feed with the given attestation time stamp and
|
||||
|
@ -382,7 +389,7 @@ mod test {
|
|||
fn test_verify_vaa_sender_ok() {
|
||||
let config_info = ConfigInfo {
|
||||
data_sources: create_data_sources(vec![1u8], 3),
|
||||
..Default::default()
|
||||
..create_zero_config_info()
|
||||
};
|
||||
|
||||
let mut vaa = create_zero_vaa();
|
||||
|
@ -396,7 +403,7 @@ mod test {
|
|||
fn test_verify_vaa_sender_fail_wrong_emitter_address() {
|
||||
let config_info = ConfigInfo {
|
||||
data_sources: create_data_sources(vec![1u8], 3),
|
||||
..Default::default()
|
||||
..create_zero_config_info()
|
||||
};
|
||||
|
||||
let mut vaa = create_zero_vaa();
|
||||
|
@ -404,7 +411,7 @@ mod test {
|
|||
vaa.emitter_chain = 3;
|
||||
assert_eq!(
|
||||
verify_vaa_sender(&config_info, &vaa),
|
||||
ContractError::InvalidVAA.std_err()
|
||||
Err(PythContractError::InvalidUpdateEmitter.into())
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -412,7 +419,7 @@ mod test {
|
|||
fn test_verify_vaa_sender_fail_wrong_emitter_chain() {
|
||||
let config_info = ConfigInfo {
|
||||
data_sources: create_data_sources(vec![1u8], 3),
|
||||
..Default::default()
|
||||
..create_zero_config_info()
|
||||
};
|
||||
|
||||
let mut vaa = create_zero_vaa();
|
||||
|
@ -420,7 +427,7 @@ mod test {
|
|||
vaa.emitter_chain = 2;
|
||||
assert_eq!(
|
||||
verify_vaa_sender(&config_info, &vaa),
|
||||
ContractError::InvalidVAA.std_err()
|
||||
Err(PythContractError::InvalidUpdateEmitter.into())
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -595,35 +602,51 @@ mod test {
|
|||
|
||||
assert_eq!(
|
||||
query_price_feed(deps.as_ref(), env, b"123".as_ref()),
|
||||
ContractError::AssetNotFound.std_err()
|
||||
Err(PythContractError::PriceFeedNotFound.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_data_source_ok_with_owner() {
|
||||
let (mut deps, env) = setup_test();
|
||||
config(&mut deps.storage).save(&ConfigInfo {
|
||||
owner: String::from("123"),
|
||||
..Default::default()
|
||||
}).unwrap();
|
||||
config(&mut deps.storage)
|
||||
.save(&ConfigInfo {
|
||||
owner: Addr::unchecked("123"),
|
||||
..create_zero_config_info()
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let data_source = PythDataSource { emitter: vec![1u8].into(), pyth_emitter_chain: 1 };
|
||||
let data_source = PythDataSource {
|
||||
emitter: vec![1u8].into(),
|
||||
pyth_emitter_chain: 1,
|
||||
};
|
||||
|
||||
assert!(add_data_source(deps.as_mut(), env.clone(), mock_info("123", &[]), data_source.clone()).is_ok());
|
||||
assert!(add_data_source(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
mock_info("123", &[]),
|
||||
data_source.clone()
|
||||
)
|
||||
.is_ok());
|
||||
|
||||
// Adding an existing data source should result an error
|
||||
assert!(add_data_source(deps.as_mut(), env.clone(), mock_info("123", &[]), data_source.clone()).is_err());
|
||||
assert!(add_data_source(deps.as_mut(), env, mock_info("123", &[]), data_source).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_data_source_err_without_owner() {
|
||||
let (mut deps, env) = setup_test();
|
||||
config(&mut deps.storage).save(&ConfigInfo {
|
||||
owner: String::from("123"),
|
||||
..Default::default()
|
||||
}).unwrap();
|
||||
config(&mut deps.storage)
|
||||
.save(&ConfigInfo {
|
||||
owner: Addr::unchecked("123"),
|
||||
..create_zero_config_info()
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let data_source = PythDataSource { emitter: vec![1u8].into(), pyth_emitter_chain: 1 };
|
||||
let data_source = PythDataSource {
|
||||
emitter: vec![1u8].into(),
|
||||
pyth_emitter_chain: 1,
|
||||
};
|
||||
|
||||
assert!(add_data_source(deps.as_mut(), env, mock_info("321", &[]), data_source).is_err());
|
||||
}
|
||||
|
@ -631,74 +654,116 @@ mod test {
|
|||
#[test]
|
||||
fn test_remove_data_source_ok_with_owner() {
|
||||
let (mut deps, env) = setup_test();
|
||||
config(&mut deps.storage).save(&ConfigInfo {
|
||||
owner: String::from("123"),
|
||||
data_sources: create_data_sources(vec![1u8], 1),
|
||||
..Default::default()
|
||||
}).unwrap();
|
||||
config(&mut deps.storage)
|
||||
.save(&ConfigInfo {
|
||||
owner: Addr::unchecked("123"),
|
||||
data_sources: create_data_sources(vec![1u8], 1),
|
||||
..create_zero_config_info()
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let data_source = PythDataSource { emitter: vec![1u8].into(), pyth_emitter_chain: 1 };
|
||||
let data_source = PythDataSource {
|
||||
emitter: vec![1u8].into(),
|
||||
pyth_emitter_chain: 1,
|
||||
};
|
||||
|
||||
assert!(remove_data_source(deps.as_mut(), env.clone(), mock_info("123", &[]), data_source.clone()).is_ok());
|
||||
assert!(remove_data_source(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
mock_info("123", &[]),
|
||||
data_source.clone()
|
||||
)
|
||||
.is_ok());
|
||||
|
||||
// Removing a non existent data source should result an error
|
||||
assert!(remove_data_source(deps.as_mut(), env.clone(), mock_info("123", &[]), data_source.clone()).is_err());
|
||||
assert!(
|
||||
remove_data_source(deps.as_mut(), env, mock_info("123", &[]), data_source).is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_data_source_err_without_owner() {
|
||||
let (mut deps, env) = setup_test();
|
||||
config(&mut deps.storage).save(&ConfigInfo {
|
||||
owner: String::from("123"),
|
||||
data_sources: create_data_sources(vec![1u8], 1),
|
||||
..Default::default()
|
||||
}).unwrap();
|
||||
config(&mut deps.storage)
|
||||
.save(&ConfigInfo {
|
||||
owner: Addr::unchecked("123"),
|
||||
data_sources: create_data_sources(vec![1u8], 1),
|
||||
..create_zero_config_info()
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let data_source = PythDataSource { emitter: vec![1u8].into(), pyth_emitter_chain: 1 };
|
||||
let data_source = PythDataSource {
|
||||
emitter: vec![1u8].into(),
|
||||
pyth_emitter_chain: 1,
|
||||
};
|
||||
|
||||
assert!(remove_data_source(deps.as_mut(), env, mock_info("321", &[]), data_source).is_err());
|
||||
assert!(
|
||||
remove_data_source(deps.as_mut(), env, mock_info("321", &[]), data_source).is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_vaa_works_after_adding_data_source() {
|
||||
let (mut deps, env) = setup_test();
|
||||
config(&mut deps.storage).save(&ConfigInfo {
|
||||
owner: String::from("123"),
|
||||
..Default::default()
|
||||
}).unwrap();
|
||||
config(&mut deps.storage)
|
||||
.save(&ConfigInfo {
|
||||
owner: Addr::unchecked("123"),
|
||||
..create_zero_config_info()
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let mut vaa = create_zero_vaa();
|
||||
vaa.emitter_address = vec![1u8];
|
||||
vaa.emitter_chain = 3;
|
||||
|
||||
// Should result an error because there is no data source
|
||||
assert_eq!(verify_vaa_sender(&config_read(&deps.storage).load().unwrap(), &vaa), ContractError::InvalidVAA.std_err());
|
||||
assert_eq!(
|
||||
verify_vaa_sender(&config_read(&deps.storage).load().unwrap(), &vaa),
|
||||
Err(PythContractError::InvalidUpdateEmitter.into())
|
||||
);
|
||||
|
||||
let data_source = PythDataSource { emitter: vec![1u8].into(), pyth_emitter_chain: 3 };
|
||||
assert!(add_data_source(deps.as_mut(), env.clone(), mock_info("123", &[]), data_source.clone()).is_ok());
|
||||
let data_source = PythDataSource {
|
||||
emitter: vec![1u8].into(),
|
||||
pyth_emitter_chain: 3,
|
||||
};
|
||||
assert!(add_data_source(deps.as_mut(), env, mock_info("123", &[]), data_source).is_ok());
|
||||
|
||||
assert_eq!(verify_vaa_sender(&config_read(&deps.storage).load().unwrap(), &vaa), Ok(()));
|
||||
assert_eq!(
|
||||
verify_vaa_sender(&config_read(&deps.storage).load().unwrap(), &vaa),
|
||||
Ok(())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_vaa_err_after_removing_data_source() {
|
||||
let (mut deps, env) = setup_test();
|
||||
config(&mut deps.storage).save(&ConfigInfo {
|
||||
owner: String::from("123"),
|
||||
data_sources: create_data_sources(vec![1u8], 3),
|
||||
..Default::default()
|
||||
}).unwrap();
|
||||
config(&mut deps.storage)
|
||||
.save(&ConfigInfo {
|
||||
owner: Addr::unchecked("123"),
|
||||
data_sources: create_data_sources(vec![1u8], 3),
|
||||
..create_zero_config_info()
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let mut vaa = create_zero_vaa();
|
||||
vaa.emitter_address = vec![1u8];
|
||||
vaa.emitter_chain = 3;
|
||||
|
||||
assert_eq!(verify_vaa_sender(&config_read(&deps.storage).load().unwrap(), &vaa), Ok(()));
|
||||
assert_eq!(
|
||||
verify_vaa_sender(&config_read(&deps.storage).load().unwrap(), &vaa),
|
||||
Ok(())
|
||||
);
|
||||
|
||||
let data_source = PythDataSource { emitter: vec![1u8].into(), pyth_emitter_chain: 3 };
|
||||
assert!(remove_data_source(deps.as_mut(), env.clone(), mock_info("123", &[]), data_source.clone()).is_ok());
|
||||
let data_source = PythDataSource {
|
||||
emitter: vec![1u8].into(),
|
||||
pyth_emitter_chain: 3,
|
||||
};
|
||||
assert!(remove_data_source(deps.as_mut(), env, mock_info("123", &[]), data_source).is_ok());
|
||||
|
||||
// Should result an error because data source should not exist anymore
|
||||
assert_eq!(verify_vaa_sender(&config_read(&deps.storage).load().unwrap(), &vaa), ContractError::InvalidVAA.std_err());
|
||||
assert_eq!(
|
||||
verify_vaa_sender(&config_read(&deps.storage).load().unwrap(), &vaa),
|
||||
Err(PythContractError::InvalidUpdateEmitter.into())
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
use cosmwasm_std::StdError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum PythContractError {
|
||||
/// Message sender not permitted to execute this operation
|
||||
#[error("PermissionDenied")]
|
||||
PermissionDenied,
|
||||
|
||||
/// Wrapped asset not found in the registry
|
||||
#[error("PriceFeedNotFound")]
|
||||
PriceFeedNotFound,
|
||||
|
||||
/// Message emitter is not an accepted data source.
|
||||
#[error("InvalidUpdateMessageEmitter")]
|
||||
InvalidUpdateEmitter,
|
||||
|
||||
/// Message payload cannot be deserialized to a batch attestation
|
||||
#[error("InvalidUpdatePayload")]
|
||||
InvalidUpdatePayload,
|
||||
|
||||
/// Data source does not exists error (on removing data source)
|
||||
#[error("DataSourceDoesNotExists")]
|
||||
DataSourceDoesNotExists,
|
||||
|
||||
/// Data source already exists error (on adding data source)
|
||||
#[error("DataSourceAlreadyExists")]
|
||||
DataSourceAlreadyExists,
|
||||
}
|
||||
|
||||
impl From<PythContractError> for StdError {
|
||||
fn from(other: PythContractError) -> StdError {
|
||||
StdError::GenericErr {
|
||||
msg: format!("{}", other),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,5 +2,6 @@
|
|||
extern crate lazy_static;
|
||||
|
||||
pub mod contract;
|
||||
pub mod error;
|
||||
pub mod msg;
|
||||
pub mod state;
|
|
@ -0,0 +1,34 @@
|
|||
use cosmwasm_std::Binary;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{
|
||||
Deserialize,
|
||||
Serialize,
|
||||
};
|
||||
|
||||
use crate::state::PythDataSource;
|
||||
|
||||
type HumanAddr = String;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct InstantiateMsg {
|
||||
pub wormhole_contract: HumanAddr,
|
||||
pub pyth_emitter: Binary,
|
||||
pub pyth_emitter_chain: u16,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ExecuteMsg {
|
||||
SubmitVaa { data: Binary },
|
||||
AddDataSource { data_source: PythDataSource },
|
||||
RemoveDataSource { data_source: PythDataSource },
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct MigrateMsg {}
|
||||
|
||||
pub use pyth_sdk_cw::{
|
||||
PriceFeedResponse,
|
||||
QueryMsg,
|
||||
};
|
|
@ -1,9 +1,7 @@
|
|||
use std::{
|
||||
time::Duration,
|
||||
collections::HashSet
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
use std::time::Duration;
|
||||
|
||||
use pyth_sdk::PriceFeed;
|
||||
use pyth_sdk_cw::PriceFeed;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{
|
||||
Deserialize,
|
||||
|
@ -11,9 +9,10 @@ use serde::{
|
|||
};
|
||||
|
||||
use cosmwasm_std::{
|
||||
Addr,
|
||||
Binary,
|
||||
Storage,
|
||||
Timestamp,
|
||||
Binary,
|
||||
};
|
||||
|
||||
use cosmwasm_storage::{
|
||||
|
@ -27,8 +26,6 @@ use cosmwasm_storage::{
|
|||
Singleton,
|
||||
};
|
||||
|
||||
type HumanAddr = String;
|
||||
|
||||
pub static CONFIG_KEY: &[u8] = b"config";
|
||||
pub static PRICE_INFO_KEY: &[u8] = b"price_info_v3";
|
||||
|
||||
|
@ -37,22 +34,21 @@ pub static PRICE_INFO_KEY: &[u8] = b"price_info_v3";
|
|||
/// This value considers attestation delay which currently might up to a minute.
|
||||
pub const VALID_TIME_PERIOD: Duration = Duration::from_secs(3 * 60);
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, Hash, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash, JsonSchema)]
|
||||
pub struct PythDataSource {
|
||||
pub emitter: Binary,
|
||||
pub emitter: Binary,
|
||||
pub pyth_emitter_chain: u16,
|
||||
}
|
||||
|
||||
// Guardian set information
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct ConfigInfo {
|
||||
pub owner: HumanAddr,
|
||||
pub wormhole_contract: HumanAddr,
|
||||
pub data_sources: HashSet<PythDataSource>,
|
||||
pub owner: Addr,
|
||||
pub wormhole_contract: Addr,
|
||||
pub data_sources: HashSet<PythDataSource>,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct PriceInfo {
|
||||
pub arrival_time: Timestamp,
|
|
@ -1,5 +0,0 @@
|
|||
[alias]
|
||||
wasm = "build --release --target wasm32-unknown-unknown"
|
||||
wasm-debug = "build --target wasm32-unknown-unknown"
|
||||
unit-test = "test --lib --features backtraces"
|
||||
integration-test = "test --test integration"
|
|
@ -1,32 +0,0 @@
|
|||
[package]
|
||||
name = "wormhole-bridge-terra"
|
||||
version = "0.1.0"
|
||||
authors = ["Yuriy Savchenko <yuriy.savchenko@gmail.com>"]
|
||||
edition = "2018"
|
||||
description = "Wormhole contract"
|
||||
|
||||
[lib]
|
||||
name = "wormhole"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
backtraces = ["cosmwasm-std/backtraces"]
|
||||
# use library feature to disable all init/handle/query exports
|
||||
library = []
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-std = { version = "1.0.0" }
|
||||
cosmwasm-storage = { version = "1.0.0" }
|
||||
schemars = "0.8.1"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = { version = "1.0.20" }
|
||||
k256 = { version = "0.9.4", default-features = false, features = ["ecdsa"] }
|
||||
getrandom = { version = "0.2", features = ["custom"] }
|
||||
sha3 = { version = "0.9.1", default-features = false }
|
||||
generic-array = { version = "0.14.4" }
|
||||
hex = "0.4.2"
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
[dev-dependencies]
|
||||
cosmwasm-vm = { version = "1.0.0", default-features = false }
|
||||
serde_json = "1.0"
|
|
@ -1,101 +0,0 @@
|
|||
use cosmwasm_std::CanonicalAddr;
|
||||
|
||||
pub trait ByteUtils {
|
||||
fn get_u8(&self, index: usize) -> u8;
|
||||
fn get_u16(&self, index: usize) -> u16;
|
||||
fn get_u32(&self, index: usize) -> u32;
|
||||
fn get_u64(&self, index: usize) -> u64;
|
||||
|
||||
fn get_u128_be(&self, index: usize) -> u128;
|
||||
/// High 128 then low 128
|
||||
fn get_u256(&self, index: usize) -> (u128, u128);
|
||||
fn get_address(&self, index: usize) -> CanonicalAddr;
|
||||
fn get_bytes32(&self, index: usize) -> &[u8];
|
||||
fn get_bytes(&self, index: usize, bytes: usize) -> &[u8];
|
||||
fn get_const_bytes<const N: usize>(&self, index: usize) -> [u8; N];
|
||||
}
|
||||
|
||||
impl ByteUtils for &[u8] {
|
||||
fn get_u8(&self, index: usize) -> u8 {
|
||||
self[index]
|
||||
}
|
||||
fn get_u16(&self, index: usize) -> u16 {
|
||||
let mut bytes: [u8; 16 / 8] = [0; 16 / 8];
|
||||
bytes.copy_from_slice(&self[index..index + 2]);
|
||||
u16::from_be_bytes(bytes)
|
||||
}
|
||||
fn get_u32(&self, index: usize) -> u32 {
|
||||
let mut bytes: [u8; 32 / 8] = [0; 32 / 8];
|
||||
bytes.copy_from_slice(&self[index..index + 4]);
|
||||
u32::from_be_bytes(bytes)
|
||||
}
|
||||
fn get_u64(&self, index: usize) -> u64 {
|
||||
let mut bytes: [u8; 64 / 8] = [0; 64 / 8];
|
||||
bytes.copy_from_slice(&self[index..index + 8]);
|
||||
u64::from_be_bytes(bytes)
|
||||
}
|
||||
fn get_u128_be(&self, index: usize) -> u128 {
|
||||
let mut bytes: [u8; 128 / 8] = [0; 128 / 8];
|
||||
bytes.copy_from_slice(&self[index..index + 128 / 8]);
|
||||
u128::from_be_bytes(bytes)
|
||||
}
|
||||
fn get_u256(&self, index: usize) -> (u128, u128) {
|
||||
(self.get_u128_be(index), self.get_u128_be(index + 128 / 8))
|
||||
}
|
||||
fn get_address(&self, index: usize) -> CanonicalAddr {
|
||||
// 32 bytes are reserved for addresses, but wasmd uses both 32 and 20 bytes
|
||||
// https://github.com/CosmWasm/wasmd/blob/ac92fdcf37388cc8dc24535f301f64395f8fb3da/x/wasm/types/types.go#L325
|
||||
if self.get_u128_be(index) >> 32 == 0 {
|
||||
return CanonicalAddr::from(&self[index + 12..index + 32])
|
||||
}
|
||||
return CanonicalAddr::from(&self[index..index + 32])
|
||||
}
|
||||
fn get_bytes32(&self, index: usize) -> &[u8] {
|
||||
&self[index..index + 32]
|
||||
}
|
||||
|
||||
fn get_bytes(&self, index: usize, bytes: usize) -> &[u8] {
|
||||
&self[index..index + bytes]
|
||||
}
|
||||
|
||||
fn get_const_bytes<const N: usize>(&self, index: usize) -> [u8; N] {
|
||||
let mut bytes: [u8; N] = [0; N];
|
||||
bytes.copy_from_slice(&self[index..index + N]);
|
||||
bytes
|
||||
}
|
||||
}
|
||||
|
||||
/// Left-pad a 20 byte address with 0s
|
||||
pub fn extend_address_to_32(addr: &CanonicalAddr) -> Vec<u8> {
|
||||
extend_address_to_32_array(addr).to_vec()
|
||||
}
|
||||
|
||||
pub fn extend_address_to_32_array(addr: &CanonicalAddr) -> [u8; 32] {
|
||||
let mut v: Vec<u8> = vec![0; 32 - addr.len()];
|
||||
v.extend(addr.as_slice());
|
||||
let mut result: [u8; 32] = [0; 32];
|
||||
result.copy_from_slice(&v);
|
||||
result
|
||||
}
|
||||
|
||||
/// Turn a string into a fixed length array. If the string is shorter than the
|
||||
/// resulting array, it gets padded with \0s on the right. If longer, it gets
|
||||
/// truncated.
|
||||
pub fn string_to_array<const N: usize>(s: &str) -> [u8; N] {
|
||||
let bytes = s.as_bytes();
|
||||
let len = usize::min(N, bytes.len());
|
||||
let zeros = vec![0; N - len];
|
||||
let padded = [bytes[..len].to_vec(), zeros].concat();
|
||||
let mut result: [u8; N] = [0; N];
|
||||
result.copy_from_slice(&padded);
|
||||
result
|
||||
}
|
||||
|
||||
pub fn extend_string_to_32(s: &str) -> Vec<u8> {
|
||||
string_to_array::<32>(s).to_vec()
|
||||
}
|
||||
|
||||
pub fn get_string_from_32(v: &Vec<u8>) -> String {
|
||||
let s = String::from_utf8_lossy(v);
|
||||
s.chars().filter(|c| c != &'\0').collect()
|
||||
}
|
|
@ -1,417 +0,0 @@
|
|||
use cosmwasm_std::{
|
||||
entry_point,
|
||||
has_coins,
|
||||
to_binary,
|
||||
BankMsg,
|
||||
Binary,
|
||||
Coin,
|
||||
CosmosMsg,
|
||||
Deps,
|
||||
DepsMut,
|
||||
Env,
|
||||
MessageInfo,
|
||||
Response,
|
||||
StdError,
|
||||
StdResult,
|
||||
Storage,
|
||||
WasmMsg,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
byte_utils::{
|
||||
extend_address_to_32,
|
||||
ByteUtils,
|
||||
},
|
||||
error::ContractError,
|
||||
msg::{
|
||||
ExecuteMsg,
|
||||
GetAddressHexResponse,
|
||||
GetStateResponse,
|
||||
GuardianSetInfoResponse,
|
||||
InstantiateMsg,
|
||||
MigrateMsg,
|
||||
QueryMsg,
|
||||
},
|
||||
state::{
|
||||
config,
|
||||
config_read,
|
||||
guardian_set_get,
|
||||
guardian_set_set,
|
||||
sequence_read,
|
||||
sequence_set,
|
||||
vaa_archive_add,
|
||||
vaa_archive_check,
|
||||
ConfigInfo,
|
||||
ContractUpgrade,
|
||||
GovernancePacket,
|
||||
GuardianAddress,
|
||||
GuardianSetInfo,
|
||||
GuardianSetUpgrade,
|
||||
ParsedVAA,
|
||||
SetFee,
|
||||
TransferFee,
|
||||
},
|
||||
};
|
||||
|
||||
use k256::{
|
||||
ecdsa::{
|
||||
recoverable::{
|
||||
Id as RecoverableId,
|
||||
Signature as RecoverableSignature,
|
||||
},
|
||||
Signature,
|
||||
VerifyingKey,
|
||||
},
|
||||
EncodedPoint,
|
||||
};
|
||||
use sha3::{
|
||||
Digest,
|
||||
Keccak256,
|
||||
};
|
||||
|
||||
use generic_array::GenericArray;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
type HumanAddr = String;
|
||||
|
||||
// Chain ID of Terra
|
||||
const CHAIN_ID: u16 = 3;
|
||||
|
||||
// Lock assets fee amount and denomination
|
||||
const FEE_AMOUNT: u128 = 0;
|
||||
pub const FEE_DENOMINATION: &str = "uluna";
|
||||
|
||||
#[cfg_attr(not(feature = "library"), entry_point)]
|
||||
pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult<Response> {
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "library"), entry_point)]
|
||||
pub fn instantiate(
|
||||
deps: DepsMut,
|
||||
_env: Env,
|
||||
_info: MessageInfo,
|
||||
msg: InstantiateMsg,
|
||||
) -> StdResult<Response> {
|
||||
// Save general wormhole info
|
||||
let state = ConfigInfo {
|
||||
gov_chain: msg.gov_chain,
|
||||
gov_address: msg.gov_address.as_slice().to_vec(),
|
||||
guardian_set_index: 0,
|
||||
guardian_set_expirity: msg.guardian_set_expirity,
|
||||
fee: Coin::new(FEE_AMOUNT, FEE_DENOMINATION), // 0.01 Luna (or 10000 uluna) fee by default
|
||||
};
|
||||
config(deps.storage).save(&state)?;
|
||||
|
||||
// Add initial guardian set to storage
|
||||
guardian_set_set(
|
||||
deps.storage,
|
||||
state.guardian_set_index,
|
||||
&msg.initial_guardian_set,
|
||||
)?;
|
||||
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "library"), entry_point)]
|
||||
pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> StdResult<Response> {
|
||||
match msg {
|
||||
ExecuteMsg::PostMessage { message, nonce } => {
|
||||
handle_post_message(deps, env, info, &message.as_slice(), nonce)
|
||||
}
|
||||
ExecuteMsg::SubmitVAA { vaa } => handle_submit_vaa(deps, env, info, vaa.as_slice()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Process VAA message signed by quardians
|
||||
fn handle_submit_vaa(
|
||||
deps: DepsMut,
|
||||
env: Env,
|
||||
_info: MessageInfo,
|
||||
data: &[u8],
|
||||
) -> StdResult<Response> {
|
||||
let state = config_read(deps.storage).load()?;
|
||||
|
||||
let vaa = parse_and_verify_vaa(deps.storage, data, env.block.time.seconds())?;
|
||||
vaa_archive_add(deps.storage, vaa.hash.as_slice())?;
|
||||
|
||||
if state.gov_chain == vaa.emitter_chain && state.gov_address == vaa.emitter_address {
|
||||
if state.guardian_set_index != vaa.guardian_set_index {
|
||||
return Err(StdError::generic_err(
|
||||
"governance VAAs must be signed by the current guardian set",
|
||||
));
|
||||
}
|
||||
return handle_governance_payload(deps, env, &vaa.payload);
|
||||
}
|
||||
|
||||
ContractError::InvalidVAAAction.std_err()
|
||||
}
|
||||
|
||||
fn handle_governance_payload(deps: DepsMut, env: Env, data: &Vec<u8>) -> StdResult<Response> {
|
||||
let gov_packet = GovernancePacket::deserialize(&data)?;
|
||||
|
||||
let module = String::from_utf8(gov_packet.module).unwrap();
|
||||
let module: String = module.chars().filter(|c| c != &'\0').collect();
|
||||
|
||||
if module != "Core" {
|
||||
return Err(StdError::generic_err("this is not a valid module"));
|
||||
}
|
||||
|
||||
if gov_packet.chain != 0 && gov_packet.chain != CHAIN_ID {
|
||||
return Err(StdError::generic_err(
|
||||
"the governance VAA is for another chain",
|
||||
));
|
||||
}
|
||||
|
||||
match gov_packet.action {
|
||||
1u8 => vaa_update_contract(deps, env, &gov_packet.payload),
|
||||
2u8 => vaa_update_guardian_set(deps, env, &gov_packet.payload),
|
||||
3u8 => handle_set_fee(deps, env, &gov_packet.payload),
|
||||
4u8 => handle_transfer_fee(deps, env, &gov_packet.payload),
|
||||
_ => ContractError::InvalidVAAAction.std_err(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses raw VAA data into a struct and verifies whether it contains sufficient signatures of an
|
||||
/// active guardian set i.e. is valid according to Wormhole consensus rules
|
||||
fn parse_and_verify_vaa(
|
||||
storage: &dyn Storage,
|
||||
data: &[u8],
|
||||
block_time: u64,
|
||||
) -> StdResult<ParsedVAA> {
|
||||
let vaa = ParsedVAA::deserialize(data)?;
|
||||
|
||||
if vaa.version != 1 {
|
||||
return ContractError::InvalidVersion.std_err();
|
||||
}
|
||||
|
||||
// Check if VAA with this hash was already accepted
|
||||
if vaa_archive_check(storage, vaa.hash.as_slice()) {
|
||||
return ContractError::VaaAlreadyExecuted.std_err();
|
||||
}
|
||||
|
||||
// Load and check guardian set
|
||||
let guardian_set = guardian_set_get(storage, vaa.guardian_set_index);
|
||||
let guardian_set: GuardianSetInfo =
|
||||
guardian_set.or_else(|_| ContractError::InvalidGuardianSetIndex.std_err())?;
|
||||
|
||||
if guardian_set.expiration_time != 0 && guardian_set.expiration_time < block_time {
|
||||
return ContractError::GuardianSetExpired.std_err();
|
||||
}
|
||||
if (vaa.len_signers as usize) < guardian_set.quorum() {
|
||||
return ContractError::NoQuorum.std_err();
|
||||
}
|
||||
|
||||
// Verify guardian signatures
|
||||
let mut last_index: i32 = -1;
|
||||
let mut pos = ParsedVAA::HEADER_LEN;
|
||||
|
||||
for _ in 0..vaa.len_signers {
|
||||
if pos + ParsedVAA::SIGNATURE_LEN > data.len() {
|
||||
return ContractError::InvalidVAA.std_err();
|
||||
}
|
||||
let index = data.get_u8(pos) as i32;
|
||||
if index <= last_index {
|
||||
return ContractError::WrongGuardianIndexOrder.std_err();
|
||||
}
|
||||
last_index = index;
|
||||
|
||||
let signature = Signature::try_from(
|
||||
&data[pos + ParsedVAA::SIG_DATA_POS
|
||||
..pos + ParsedVAA::SIG_DATA_POS + ParsedVAA::SIG_DATA_LEN],
|
||||
)
|
||||
.or_else(|_| ContractError::CannotDecodeSignature.std_err())?;
|
||||
let id = RecoverableId::new(data.get_u8(pos + ParsedVAA::SIG_RECOVERY_POS))
|
||||
.or_else(|_| ContractError::CannotDecodeSignature.std_err())?;
|
||||
let recoverable_signature = RecoverableSignature::new(&signature, id)
|
||||
.or_else(|_| ContractError::CannotDecodeSignature.std_err())?;
|
||||
|
||||
let verify_key = recoverable_signature
|
||||
.recover_verify_key_from_digest_bytes(GenericArray::from_slice(vaa.hash.as_slice()))
|
||||
.or_else(|_| ContractError::CannotRecoverKey.std_err())?;
|
||||
|
||||
let index = index as usize;
|
||||
if index >= guardian_set.addresses.len() {
|
||||
return ContractError::TooManySignatures.std_err();
|
||||
}
|
||||
if !keys_equal(&verify_key, &guardian_set.addresses[index]) {
|
||||
return ContractError::GuardianSignatureError.std_err();
|
||||
}
|
||||
pos += ParsedVAA::SIGNATURE_LEN;
|
||||
}
|
||||
|
||||
Ok(vaa)
|
||||
}
|
||||
|
||||
fn vaa_update_guardian_set(deps: DepsMut, env: Env, data: &Vec<u8>) -> StdResult<Response> {
|
||||
/* Payload format
|
||||
0 uint32 new_index
|
||||
4 uint8 len(keys)
|
||||
5 [][20]uint8 guardian addresses
|
||||
*/
|
||||
|
||||
let mut state = config_read(deps.storage).load()?;
|
||||
|
||||
let GuardianSetUpgrade {
|
||||
new_guardian_set_index,
|
||||
new_guardian_set,
|
||||
} = GuardianSetUpgrade::deserialize(&data)?;
|
||||
|
||||
if new_guardian_set_index != state.guardian_set_index + 1 {
|
||||
return ContractError::GuardianSetIndexIncreaseError.std_err();
|
||||
}
|
||||
|
||||
let old_guardian_set_index = state.guardian_set_index;
|
||||
|
||||
state.guardian_set_index = new_guardian_set_index;
|
||||
|
||||
guardian_set_set(deps.storage, state.guardian_set_index, &new_guardian_set)?;
|
||||
|
||||
config(deps.storage).save(&state)?;
|
||||
|
||||
let mut old_guardian_set = guardian_set_get(deps.storage, old_guardian_set_index)?;
|
||||
old_guardian_set.expiration_time = env.block.time.seconds() + state.guardian_set_expirity;
|
||||
guardian_set_set(deps.storage, old_guardian_set_index, &old_guardian_set)?;
|
||||
|
||||
Ok(Response::new()
|
||||
.add_attribute("action", "guardian_set_change")
|
||||
.add_attribute("old", old_guardian_set_index.to_string())
|
||||
.add_attribute("new", state.guardian_set_index.to_string()))
|
||||
}
|
||||
|
||||
fn vaa_update_contract(_deps: DepsMut, env: Env, data: &Vec<u8>) -> StdResult<Response> {
|
||||
/* Payload format
|
||||
0 [][32]uint8 new_contract
|
||||
*/
|
||||
|
||||
let ContractUpgrade { new_contract } = ContractUpgrade::deserialize(&data)?;
|
||||
|
||||
Ok(Response::new()
|
||||
.add_message(CosmosMsg::Wasm(WasmMsg::Migrate {
|
||||
contract_addr: env.contract.address.to_string(),
|
||||
new_code_id: new_contract,
|
||||
msg: to_binary(&MigrateMsg {})?,
|
||||
}))
|
||||
.add_attribute("action", "contract_upgrade"))
|
||||
}
|
||||
|
||||
pub fn handle_set_fee(deps: DepsMut, _env: Env, data: &Vec<u8>) -> StdResult<Response> {
|
||||
let set_fee_msg = SetFee::deserialize(&data)?;
|
||||
|
||||
// Save new fees
|
||||
let mut state = config_read(deps.storage).load()?;
|
||||
state.fee = set_fee_msg.fee;
|
||||
config(deps.storage).save(&state)?;
|
||||
|
||||
Ok(Response::new()
|
||||
.add_attribute("action", "fee_change")
|
||||
.add_attribute("new_fee.amount", state.fee.amount.to_string())
|
||||
.add_attribute("new_fee.denom", state.fee.denom.to_string()))
|
||||
}
|
||||
|
||||
pub fn handle_transfer_fee(deps: DepsMut, _env: Env, data: &Vec<u8>) -> StdResult<Response> {
|
||||
let transfer_msg = TransferFee::deserialize(&data)?;
|
||||
|
||||
Ok(Response::new().add_message(CosmosMsg::Bank(BankMsg::Send {
|
||||
to_address: deps.api.addr_humanize(&transfer_msg.recipient)?.to_string(),
|
||||
amount: vec![transfer_msg.amount],
|
||||
})))
|
||||
}
|
||||
|
||||
fn handle_post_message(
|
||||
deps: DepsMut,
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
message: &[u8],
|
||||
nonce: u32,
|
||||
) -> StdResult<Response> {
|
||||
let state = config_read(deps.storage).load()?;
|
||||
let fee = state.fee;
|
||||
|
||||
// Check fee
|
||||
if fee.amount.u128() > 0 && !has_coins(info.funds.as_ref(), &fee) {
|
||||
return ContractError::FeeTooLow.std_err();
|
||||
}
|
||||
|
||||
let emitter = extend_address_to_32(&deps.api.addr_canonicalize(&info.sender.as_str())?);
|
||||
let sequence = sequence_read(deps.storage, emitter.as_slice());
|
||||
sequence_set(deps.storage, emitter.as_slice(), sequence + 1)?;
|
||||
|
||||
Ok(Response::new()
|
||||
.add_attribute("message.message", hex::encode(message))
|
||||
.add_attribute("message.sender", hex::encode(emitter))
|
||||
.add_attribute("message.chain_id", CHAIN_ID.to_string())
|
||||
.add_attribute("message.nonce", nonce.to_string())
|
||||
.add_attribute("message.sequence", sequence.to_string())
|
||||
.add_attribute("message.block_time", env.block.time.seconds().to_string()))
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "library"), entry_point)]
|
||||
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
|
||||
match msg {
|
||||
QueryMsg::GuardianSetInfo {} => to_binary(&query_guardian_set_info(deps)?),
|
||||
QueryMsg::VerifyVAA { vaa, block_time } => to_binary(&query_parse_and_verify_vaa(
|
||||
deps,
|
||||
&vaa.as_slice(),
|
||||
block_time,
|
||||
)?),
|
||||
QueryMsg::GetState {} => to_binary(&query_state(deps)?),
|
||||
QueryMsg::QueryAddressHex { address } => to_binary(&query_address_hex(deps, &address)?),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn query_guardian_set_info(deps: Deps) -> StdResult<GuardianSetInfoResponse> {
|
||||
let state = config_read(deps.storage).load()?;
|
||||
let guardian_set = guardian_set_get(deps.storage, state.guardian_set_index)?;
|
||||
let res = GuardianSetInfoResponse {
|
||||
guardian_set_index: state.guardian_set_index,
|
||||
addresses: guardian_set.addresses,
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn query_parse_and_verify_vaa(
|
||||
deps: Deps,
|
||||
data: &[u8],
|
||||
block_time: u64,
|
||||
) -> StdResult<ParsedVAA> {
|
||||
parse_and_verify_vaa(deps.storage, data, block_time)
|
||||
}
|
||||
|
||||
// returns the hex of the 32 byte address we use for some address on this chain
|
||||
pub fn query_address_hex(deps: Deps, address: &HumanAddr) -> StdResult<GetAddressHexResponse> {
|
||||
Ok(GetAddressHexResponse {
|
||||
hex: hex::encode(extend_address_to_32(&deps.api.addr_canonicalize(&address)?)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn query_state(deps: Deps) -> StdResult<GetStateResponse> {
|
||||
let state = config_read(deps.storage).load()?;
|
||||
let res = GetStateResponse { fee: state.fee };
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn keys_equal(a: &VerifyingKey, b: &GuardianAddress) -> bool {
|
||||
let mut hasher = Keccak256::new();
|
||||
|
||||
let point: EncodedPoint = EncodedPoint::from(a);
|
||||
let point = point.decompress();
|
||||
if bool::from(point.is_none()) {
|
||||
return false;
|
||||
}
|
||||
let point = point.unwrap();
|
||||
|
||||
hasher.update(&point.as_bytes()[1..]);
|
||||
let a = &hasher.finalize()[12..];
|
||||
|
||||
let b = &b.bytes;
|
||||
if a.len() != b.len() {
|
||||
return false;
|
||||
}
|
||||
for (ai, bi) in a.iter().zip(b.as_slice().iter()) {
|
||||
if ai != bi {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
use cosmwasm_std::StdError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ContractError {
|
||||
/// Invalid VAA version
|
||||
#[error("InvalidVersion")]
|
||||
InvalidVersion,
|
||||
|
||||
/// Guardian set with this index does not exist
|
||||
#[error("InvalidGuardianSetIndex")]
|
||||
InvalidGuardianSetIndex,
|
||||
|
||||
/// Guardian set expiration date is zero or in the past
|
||||
#[error("GuardianSetExpired")]
|
||||
GuardianSetExpired,
|
||||
|
||||
/// Not enough signers on the VAA
|
||||
#[error("NoQuorum")]
|
||||
NoQuorum,
|
||||
|
||||
/// Wrong guardian index order, order must be ascending
|
||||
#[error("WrongGuardianIndexOrder")]
|
||||
WrongGuardianIndexOrder,
|
||||
|
||||
/// Some problem with signature decoding from bytes
|
||||
#[error("CannotDecodeSignature")]
|
||||
CannotDecodeSignature,
|
||||
|
||||
/// Some problem with public key recovery from the signature
|
||||
#[error("CannotRecoverKey")]
|
||||
CannotRecoverKey,
|
||||
|
||||
/// Recovered pubkey from signature does not match guardian address
|
||||
#[error("GuardianSignatureError")]
|
||||
GuardianSignatureError,
|
||||
|
||||
/// VAA action code not recognized
|
||||
#[error("InvalidVAAAction")]
|
||||
InvalidVAAAction,
|
||||
|
||||
/// VAA guardian set is not current
|
||||
#[error("NotCurrentGuardianSet")]
|
||||
NotCurrentGuardianSet,
|
||||
|
||||
/// Only 128-bit amounts are supported
|
||||
#[error("AmountTooHigh")]
|
||||
AmountTooHigh,
|
||||
|
||||
/// Amount should be higher than zero
|
||||
#[error("AmountTooLow")]
|
||||
AmountTooLow,
|
||||
|
||||
/// Source and target chain ids must be different
|
||||
#[error("SameSourceAndTarget")]
|
||||
SameSourceAndTarget,
|
||||
|
||||
/// Target chain id must be the same as the current CHAIN_ID
|
||||
#[error("WrongTargetChain")]
|
||||
WrongTargetChain,
|
||||
|
||||
/// Wrapped asset init hook sent twice for the same asset id
|
||||
#[error("AssetAlreadyRegistered")]
|
||||
AssetAlreadyRegistered,
|
||||
|
||||
/// Guardian set must increase in steps of 1
|
||||
#[error("GuardianSetIndexIncreaseError")]
|
||||
GuardianSetIndexIncreaseError,
|
||||
|
||||
/// VAA was already executed
|
||||
#[error("VaaAlreadyExecuted")]
|
||||
VaaAlreadyExecuted,
|
||||
|
||||
/// Message sender not permitted to execute this operation
|
||||
#[error("PermissionDenied")]
|
||||
PermissionDenied,
|
||||
|
||||
/// Could not decode target address from canonical to human-readable form
|
||||
#[error("WrongTargetAddressFormat")]
|
||||
WrongTargetAddressFormat,
|
||||
|
||||
/// More signatures than active guardians found
|
||||
#[error("TooManySignatures")]
|
||||
TooManySignatures,
|
||||
|
||||
/// Wrapped asset not found in the registry
|
||||
#[error("AssetNotFound")]
|
||||
AssetNotFound,
|
||||
|
||||
/// Generic error when there is a problem with VAA structure
|
||||
#[error("InvalidVAA")]
|
||||
InvalidVAA,
|
||||
|
||||
/// Thrown when fee is enabled for the action, but was not sent with the transaction
|
||||
#[error("FeeTooLow")]
|
||||
FeeTooLow,
|
||||
|
||||
/// Registering asset outside of the wormhole
|
||||
#[error("RegistrationForbidden")]
|
||||
RegistrationForbidden,
|
||||
}
|
||||
|
||||
impl ContractError {
|
||||
pub fn std(&self) -> StdError {
|
||||
StdError::GenericErr {
|
||||
msg: format!("{}", self),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn std_err<T>(&self) -> Result<T, StdError> {
|
||||
Err(self.std())
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
pub mod byte_utils;
|
||||
pub mod contract;
|
||||
pub mod error;
|
||||
pub mod msg;
|
||||
pub mod state;
|
||||
|
||||
pub use crate::error::ContractError;
|
|
@ -1,71 +0,0 @@
|
|||
use cosmwasm_std::{
|
||||
Binary,
|
||||
Coin,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{
|
||||
Deserialize,
|
||||
Serialize,
|
||||
};
|
||||
|
||||
use crate::state::{
|
||||
GuardianAddress,
|
||||
GuardianSetInfo,
|
||||
};
|
||||
|
||||
type HumanAddr = String;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct InstantiateMsg {
|
||||
pub gov_chain: u16,
|
||||
pub gov_address: Binary,
|
||||
|
||||
pub initial_guardian_set: GuardianSetInfo,
|
||||
pub guardian_set_expirity: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ExecuteMsg {
|
||||
SubmitVAA { vaa: Binary },
|
||||
PostMessage { message: Binary, nonce: u32 },
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct MigrateMsg {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum QueryMsg {
|
||||
GuardianSetInfo {},
|
||||
VerifyVAA { vaa: Binary, block_time: u64 },
|
||||
GetState {},
|
||||
QueryAddressHex { address: HumanAddr },
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct GuardianSetInfoResponse {
|
||||
pub guardian_set_index: u32, // Current guardian set index
|
||||
pub addresses: Vec<GuardianAddress>, // List of querdian addresses
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct WrappedRegistryResponse {
|
||||
pub address: HumanAddr,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct GetStateResponse {
|
||||
pub fee: Coin,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct GetAddressHexResponse {
|
||||
pub hex: String,
|
||||
}
|
|
@ -1,393 +0,0 @@
|
|||
use schemars::{
|
||||
JsonSchema,
|
||||
};
|
||||
use serde::{
|
||||
Deserialize,
|
||||
Serialize,
|
||||
};
|
||||
|
||||
use cosmwasm_std::{
|
||||
Binary,
|
||||
CanonicalAddr,
|
||||
Coin,
|
||||
StdResult,
|
||||
Storage,
|
||||
Uint128,
|
||||
};
|
||||
use cosmwasm_storage::{
|
||||
bucket,
|
||||
bucket_read,
|
||||
singleton,
|
||||
singleton_read,
|
||||
Bucket,
|
||||
ReadonlyBucket,
|
||||
ReadonlySingleton,
|
||||
Singleton,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
byte_utils::ByteUtils,
|
||||
error::ContractError,
|
||||
};
|
||||
|
||||
use sha3::{
|
||||
Digest,
|
||||
Keccak256,
|
||||
};
|
||||
|
||||
type HumanAddr = String;
|
||||
|
||||
pub static CONFIG_KEY: &[u8] = b"config";
|
||||
pub static GUARDIAN_SET_KEY: &[u8] = b"guardian_set";
|
||||
pub static SEQUENCE_KEY: &[u8] = b"sequence";
|
||||
pub static WRAPPED_ASSET_KEY: &[u8] = b"wrapped_asset";
|
||||
pub static WRAPPED_ASSET_ADDRESS_KEY: &[u8] = b"wrapped_asset_address";
|
||||
|
||||
// Guardian set information
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct ConfigInfo {
|
||||
// Current active guardian set
|
||||
pub guardian_set_index: u32,
|
||||
|
||||
// Period for which a guardian set stays active after it has been replaced
|
||||
pub guardian_set_expirity: u64,
|
||||
|
||||
// governance contract details
|
||||
pub gov_chain: u16,
|
||||
pub gov_address: Vec<u8>,
|
||||
|
||||
// Message sending fee
|
||||
pub fee: Coin,
|
||||
}
|
||||
|
||||
// Validator Action Approval(VAA) data
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct ParsedVAA {
|
||||
pub version: u8,
|
||||
pub guardian_set_index: u32,
|
||||
pub timestamp: u32,
|
||||
pub nonce: u32,
|
||||
pub len_signers: u8,
|
||||
|
||||
pub emitter_chain: u16,
|
||||
pub emitter_address: Vec<u8>,
|
||||
pub sequence: u64,
|
||||
pub consistency_level: u8,
|
||||
pub payload: Vec<u8>,
|
||||
|
||||
pub hash: Vec<u8>,
|
||||
}
|
||||
|
||||
impl ParsedVAA {
|
||||
/* VAA format:
|
||||
|
||||
header (length 6):
|
||||
0 uint8 version (0x01)
|
||||
1 uint32 guardian set index
|
||||
5 uint8 len signatures
|
||||
|
||||
per signature (length 66):
|
||||
0 uint8 index of the signer (in guardian keys)
|
||||
1 [65]uint8 signature
|
||||
|
||||
body:
|
||||
0 uint32 timestamp (unix in seconds)
|
||||
4 uint32 nonce
|
||||
8 uint16 emitter_chain
|
||||
10 [32]uint8 emitter_address
|
||||
42 uint64 sequence
|
||||
50 uint8 consistency_level
|
||||
51 []uint8 payload
|
||||
*/
|
||||
|
||||
pub const HEADER_LEN: usize = 6;
|
||||
pub const SIGNATURE_LEN: usize = 66;
|
||||
|
||||
pub const GUARDIAN_SET_INDEX_POS: usize = 1;
|
||||
pub const LEN_SIGNER_POS: usize = 5;
|
||||
|
||||
pub const VAA_NONCE_POS: usize = 4;
|
||||
pub const VAA_EMITTER_CHAIN_POS: usize = 8;
|
||||
pub const VAA_EMITTER_ADDRESS_POS: usize = 10;
|
||||
pub const VAA_SEQUENCE_POS: usize = 42;
|
||||
pub const VAA_CONSISTENCY_LEVEL_POS: usize = 50;
|
||||
pub const VAA_PAYLOAD_POS: usize = 51;
|
||||
|
||||
// Signature data offsets in the signature block
|
||||
pub const SIG_DATA_POS: usize = 1;
|
||||
// Signature length minus recovery id at the end
|
||||
pub const SIG_DATA_LEN: usize = 64;
|
||||
// Recovery byte is last after the main signature
|
||||
pub const SIG_RECOVERY_POS: usize = Self::SIG_DATA_POS + Self::SIG_DATA_LEN;
|
||||
|
||||
pub fn deserialize(data: &[u8]) -> StdResult<Self> {
|
||||
let version = data.get_u8(0);
|
||||
|
||||
// Load 4 bytes starting from index 1
|
||||
let guardian_set_index: u32 = data.get_u32(Self::GUARDIAN_SET_INDEX_POS);
|
||||
let len_signers = data.get_u8(Self::LEN_SIGNER_POS) as usize;
|
||||
let body_offset: usize = Self::HEADER_LEN + Self::SIGNATURE_LEN * len_signers as usize;
|
||||
|
||||
// Hash the body
|
||||
if body_offset >= data.len() {
|
||||
return ContractError::InvalidVAA.std_err();
|
||||
}
|
||||
let body = &data[body_offset..];
|
||||
let mut hasher = Keccak256::new();
|
||||
hasher.update(body);
|
||||
let hash = hasher.finalize().to_vec();
|
||||
|
||||
// Rehash the hash
|
||||
let mut hasher = Keccak256::new();
|
||||
hasher.update(hash);
|
||||
let hash = hasher.finalize().to_vec();
|
||||
|
||||
// Signatures valid, apply VAA
|
||||
if body_offset + Self::VAA_PAYLOAD_POS > data.len() {
|
||||
return ContractError::InvalidVAA.std_err();
|
||||
}
|
||||
|
||||
let timestamp = data.get_u32(body_offset);
|
||||
let nonce = data.get_u32(body_offset + Self::VAA_NONCE_POS);
|
||||
let emitter_chain = data.get_u16(body_offset + Self::VAA_EMITTER_CHAIN_POS);
|
||||
let emitter_address = data
|
||||
.get_bytes32(body_offset + Self::VAA_EMITTER_ADDRESS_POS)
|
||||
.to_vec();
|
||||
let sequence = data.get_u64(body_offset + Self::VAA_SEQUENCE_POS);
|
||||
let consistency_level = data.get_u8(body_offset + Self::VAA_CONSISTENCY_LEVEL_POS);
|
||||
let payload = data[body_offset + Self::VAA_PAYLOAD_POS..].to_vec();
|
||||
|
||||
Ok(ParsedVAA {
|
||||
version,
|
||||
guardian_set_index,
|
||||
timestamp,
|
||||
nonce,
|
||||
len_signers: len_signers as u8,
|
||||
emitter_chain,
|
||||
emitter_address,
|
||||
sequence,
|
||||
consistency_level,
|
||||
payload,
|
||||
hash,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Guardian address
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct GuardianAddress {
|
||||
pub bytes: Binary, // 20-byte addresses
|
||||
}
|
||||
|
||||
use crate::contract::FEE_DENOMINATION;
|
||||
#[cfg(test)]
|
||||
use hex;
|
||||
|
||||
#[cfg(test)]
|
||||
impl GuardianAddress {
|
||||
pub fn from(string: &str) -> GuardianAddress {
|
||||
GuardianAddress {
|
||||
bytes: hex::decode(string).expect("Decoding failed").into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Guardian set information
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct GuardianSetInfo {
|
||||
pub addresses: Vec<GuardianAddress>,
|
||||
// List of guardian addresses
|
||||
pub expiration_time: u64, // Guardian set expiration time
|
||||
}
|
||||
|
||||
impl GuardianSetInfo {
|
||||
pub fn quorum(&self) -> usize {
|
||||
// allow quorum of 0 for testing purposes...
|
||||
if self.addresses.len() == 0 {
|
||||
return 0;
|
||||
}
|
||||
((self.addresses.len() * 10 / 3) * 2) / 10 + 1
|
||||
}
|
||||
}
|
||||
|
||||
// Wormhole contract generic information
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct WormholeInfo {
|
||||
// Period for which a guardian set stays active after it has been replaced
|
||||
pub guardian_set_expirity: u64,
|
||||
}
|
||||
|
||||
pub fn config(storage: &mut dyn Storage) -> Singleton<ConfigInfo> {
|
||||
singleton(storage, CONFIG_KEY)
|
||||
}
|
||||
|
||||
pub fn config_read(storage: &dyn Storage) -> ReadonlySingleton<ConfigInfo> {
|
||||
singleton_read(storage, CONFIG_KEY)
|
||||
}
|
||||
|
||||
pub fn guardian_set_set(
|
||||
storage: &mut dyn Storage,
|
||||
index: u32,
|
||||
data: &GuardianSetInfo,
|
||||
) -> StdResult<()> {
|
||||
bucket(storage, GUARDIAN_SET_KEY).save(&index.to_be_bytes(), data)
|
||||
}
|
||||
|
||||
pub fn guardian_set_get(storage: &dyn Storage, index: u32) -> StdResult<GuardianSetInfo> {
|
||||
bucket_read(storage, GUARDIAN_SET_KEY).load(&index.to_be_bytes())
|
||||
}
|
||||
|
||||
pub fn sequence_set(storage: &mut dyn Storage, emitter: &[u8], sequence: u64) -> StdResult<()> {
|
||||
bucket(storage, SEQUENCE_KEY).save(emitter, &sequence)
|
||||
}
|
||||
|
||||
pub fn sequence_read(storage: &dyn Storage, emitter: &[u8]) -> u64 {
|
||||
bucket_read(storage, SEQUENCE_KEY)
|
||||
.load(&emitter)
|
||||
.or::<u64>(Ok(0))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn vaa_archive_add(storage: &mut dyn Storage, hash: &[u8]) -> StdResult<()> {
|
||||
bucket(storage, GUARDIAN_SET_KEY).save(hash, &true)
|
||||
}
|
||||
|
||||
pub fn vaa_archive_check(storage: &dyn Storage, hash: &[u8]) -> bool {
|
||||
bucket_read(storage, GUARDIAN_SET_KEY)
|
||||
.load(&hash)
|
||||
.or::<bool>(Ok(false))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn wrapped_asset(storage: &mut dyn Storage) -> Bucket<HumanAddr> {
|
||||
bucket(storage, WRAPPED_ASSET_KEY)
|
||||
}
|
||||
|
||||
pub fn wrapped_asset_read(storage: &dyn Storage) -> ReadonlyBucket<HumanAddr> {
|
||||
bucket_read(storage, WRAPPED_ASSET_KEY)
|
||||
}
|
||||
|
||||
pub fn wrapped_asset_address(storage: &mut dyn Storage) -> Bucket<Vec<u8>> {
|
||||
bucket(storage, WRAPPED_ASSET_ADDRESS_KEY)
|
||||
}
|
||||
|
||||
pub fn wrapped_asset_address_read(storage: &dyn Storage) -> ReadonlyBucket<Vec<u8>> {
|
||||
bucket_read(storage, WRAPPED_ASSET_ADDRESS_KEY)
|
||||
}
|
||||
|
||||
pub struct GovernancePacket {
|
||||
pub module: Vec<u8>,
|
||||
pub action: u8,
|
||||
pub chain: u16,
|
||||
pub payload: Vec<u8>,
|
||||
}
|
||||
|
||||
impl GovernancePacket {
|
||||
pub fn deserialize(data: &Vec<u8>) -> StdResult<Self> {
|
||||
let data = data.as_slice();
|
||||
let module = data.get_bytes32(0).to_vec();
|
||||
let action = data.get_u8(32);
|
||||
let chain = data.get_u16(33);
|
||||
let payload = data[35..].to_vec();
|
||||
|
||||
Ok(GovernancePacket {
|
||||
module,
|
||||
action,
|
||||
chain,
|
||||
payload,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// action 1
|
||||
pub struct ContractUpgrade {
|
||||
pub new_contract: u64,
|
||||
}
|
||||
|
||||
// action 2
|
||||
pub struct GuardianSetUpgrade {
|
||||
pub new_guardian_set_index: u32,
|
||||
pub new_guardian_set: GuardianSetInfo,
|
||||
}
|
||||
|
||||
impl ContractUpgrade {
|
||||
pub fn deserialize(data: &Vec<u8>) -> StdResult<Self> {
|
||||
let data = data.as_slice();
|
||||
let new_contract = data.get_u64(24);
|
||||
Ok(ContractUpgrade {
|
||||
new_contract,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl GuardianSetUpgrade {
|
||||
pub fn deserialize(data: &Vec<u8>) -> StdResult<Self> {
|
||||
const ADDRESS_LEN: usize = 20;
|
||||
|
||||
let data = data.as_slice();
|
||||
let new_guardian_set_index = data.get_u32(0);
|
||||
|
||||
let n_guardians = data.get_u8(4);
|
||||
|
||||
let mut addresses = vec![];
|
||||
|
||||
for i in 0..n_guardians {
|
||||
let pos = 5 + (i as usize) * ADDRESS_LEN;
|
||||
if pos + ADDRESS_LEN > data.len() {
|
||||
return ContractError::InvalidVAA.std_err();
|
||||
}
|
||||
|
||||
addresses.push(GuardianAddress {
|
||||
bytes: data[pos..pos + ADDRESS_LEN].to_vec().into(),
|
||||
});
|
||||
}
|
||||
|
||||
let new_guardian_set = GuardianSetInfo {
|
||||
addresses,
|
||||
expiration_time: 0,
|
||||
};
|
||||
|
||||
return Ok(GuardianSetUpgrade {
|
||||
new_guardian_set_index,
|
||||
new_guardian_set,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// action 3
|
||||
pub struct SetFee {
|
||||
pub fee: Coin,
|
||||
}
|
||||
|
||||
impl SetFee {
|
||||
pub fn deserialize(data: &Vec<u8>) -> StdResult<Self> {
|
||||
let data = data.as_slice();
|
||||
|
||||
let (_, amount) = data.get_u256(0);
|
||||
let fee = Coin {
|
||||
denom: String::from(FEE_DENOMINATION),
|
||||
amount: Uint128::new(amount),
|
||||
};
|
||||
Ok(SetFee { fee })
|
||||
}
|
||||
}
|
||||
|
||||
// action 4
|
||||
pub struct TransferFee {
|
||||
pub amount: Coin,
|
||||
pub recipient: CanonicalAddr,
|
||||
}
|
||||
|
||||
impl TransferFee {
|
||||
pub fn deserialize(data: &Vec<u8>) -> StdResult<Self> {
|
||||
let data = data.as_slice();
|
||||
let recipient = data.get_address(0);
|
||||
|
||||
let (_, amount) = data.get_u256(32);
|
||||
let amount = Coin {
|
||||
denom: String::from(FEE_DENOMINATION),
|
||||
amount: Uint128::new(amount),
|
||||
};
|
||||
Ok(TransferFee { amount, recipient })
|
||||
}
|
||||
}
|
|
@ -182,7 +182,7 @@ if (argv.instantiate) {
|
|||
throw(e);
|
||||
}
|
||||
});
|
||||
console.log(`Instantiated Pyth Bridge at ${address} (${convert_terra_address_to_hex(address)})`);
|
||||
console.log(`Instantiated Pyth at ${address} (${convert_terra_address_to_hex(address)})`);
|
||||
return address;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
// Deploy Wormhole and Pyth contract to Tilt. If you want to
|
||||
// test the contracts locally you need to build the wormhole contract
|
||||
// as well. You can use Dockerfile.cosmwasm in the root of this repo
|
||||
// to do that.
|
||||
|
||||
import { LCDClient, MnemonicKey } from "@terra-money/terra.js";
|
||||
import {
|
||||
MsgInstantiateContract,
|
||||
|
@ -14,7 +19,7 @@ import { zeroPad } from "ethers/lib/utils.js";
|
|||
*/
|
||||
const artifacts = [
|
||||
"wormhole.wasm",
|
||||
"pyth_bridge.wasm",
|
||||
"pyth_cosmwasm.wasm",
|
||||
];
|
||||
|
||||
/* Check that the artifact folder contains all the wasm files we expect and nothing else */
|
||||
|
@ -152,13 +157,15 @@ addresses["wormhole.wasm"] = await instantiate("wormhole.wasm", {
|
|||
],
|
||||
expiration_time: 0,
|
||||
},
|
||||
chain_id: 18,
|
||||
fee_denom: "uluna",
|
||||
}, "wormhole");
|
||||
|
||||
const pythEmitterAddress =
|
||||
"71f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b";
|
||||
const pythChain = 1;
|
||||
|
||||
addresses["pyth_bridge.wasm"] = await instantiate("pyth_bridge.wasm", {
|
||||
addresses["pyth_cosmwasm.wasm"] = await instantiate("pyth_cosmwasm.wasm", {
|
||||
wormhole_contract: addresses["wormhole.wasm"],
|
||||
pyth_emitter: Buffer.from(pythEmitterAddress, "hex").toString(
|
||||
"base64"
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
[package]
|
||||
name = "memmap2"
|
||||
version = "0.1.0"
|
||||
authors = ["Dan Burkert <dan@danburkert.com>", "Evgeniy Reizner <razrfalcon@gmail.com>"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/RazrFalcon/memmap2-rs"
|
||||
documentation = "https://docs.rs/memmap2"
|
||||
description = "Cross-platform Rust API for memory-mapped file IO"
|
||||
keywords = ["mmap", "memory-map", "io", "file"]
|
||||
edition = "2018"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2"
|
||||
|
||||
[dev-dependencies]
|
||||
tempdir = "0.3"
|
|
@ -1,201 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [2015] [Dan Burkert]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -1,26 +0,0 @@
|
|||
Copyright (c) 2020 Evgeniy Reizner
|
||||
Copyright (c) 2015 Dan Burkert
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
|
@ -1,3 +0,0 @@
|
|||
This is a fork of https://github.com/RazrFalcon/memmap2-rs which implements stubs for all functions which is required
|
||||
to make the client code compile to WASM. It uses the v0.3.0 code (which has stubs) but was adapted to the v0.1.0 API
|
||||
which is used by Solana.
|
|
@ -1,839 +0,0 @@
|
|||
//! A cross-platform Rust API for memory mapped buffers.
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/memmap2/0.3.0")]
|
||||
|
||||
|
||||
mod stub;
|
||||
use crate::stub::MmapInner;
|
||||
|
||||
use std::fmt;
|
||||
#[cfg(unix)]
|
||||
use std::fs::File;
|
||||
use std::io::{Error, ErrorKind, Result};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::slice;
|
||||
use std::usize;
|
||||
|
||||
|
||||
pub struct MmapRawDescriptor(i32);
|
||||
|
||||
pub trait MmapAsRawDesc {
|
||||
fn as_raw_desc(&self) -> MmapRawDescriptor;
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl MmapAsRawDesc for &File {
|
||||
fn as_raw_desc(&self) -> MmapRawDescriptor {
|
||||
MmapRawDescriptor(self.as_raw_fd())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// A memory map builder, providing advanced options and flags for specifying memory map behavior.
|
||||
///
|
||||
/// `MmapOptions` can be used to create an anonymous memory map using [`map_anon()`], or a
|
||||
/// file-backed memory map using one of [`map()`], [`map_mut()`], [`map_exec()`],
|
||||
/// [`map_copy()`], or [`map_copy_read_only()`].
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// All file-backed memory map constructors are marked `unsafe` because of the potential for
|
||||
/// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or
|
||||
/// out of process. Applications must consider the risk and take appropriate precautions when
|
||||
/// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g.
|
||||
/// unlinked) files exist but are platform specific and limited.
|
||||
///
|
||||
/// [`map_anon()`]: MmapOptions::map_anon()
|
||||
/// [`map()`]: MmapOptions::map()
|
||||
/// [`map_mut()`]: MmapOptions::map_mut()
|
||||
/// [`map_exec()`]: MmapOptions::map_exec()
|
||||
/// [`map_copy()`]: MmapOptions::map_copy()
|
||||
/// [`map_copy_read_only()`]: MmapOptions::map_copy_read_only()
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct MmapOptions {
|
||||
offset: u64,
|
||||
len: Option<usize>,
|
||||
stack: bool,
|
||||
populate: bool,
|
||||
}
|
||||
|
||||
impl MmapOptions {
|
||||
/// Creates a new set of options for configuring and creating a memory map.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use memmap2::{MmapMut, MmapOptions};
|
||||
/// # use std::io::Result;
|
||||
///
|
||||
/// # fn main() -> Result<()> {
|
||||
/// // Create a new memory map builder.
|
||||
/// let mut mmap_options = MmapOptions::new();
|
||||
///
|
||||
/// // Configure the memory map builder using option setters, then create
|
||||
/// // a memory map using one of `mmap_options.map_anon`, `mmap_options.map`,
|
||||
/// // `mmap_options.map_mut`, `mmap_options.map_exec`, or `mmap_options.map_copy`:
|
||||
/// let mut mmap: MmapMut = mmap_options.len(36).map_anon()?;
|
||||
///
|
||||
/// // Use the memory map:
|
||||
/// mmap.copy_from_slice(b"...data to copy to the memory map...");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn new() -> MmapOptions {
|
||||
MmapOptions::default()
|
||||
}
|
||||
|
||||
/// Configures the memory map to start at byte `offset` from the beginning of the file.
|
||||
///
|
||||
/// This option has no effect on anonymous memory maps.
|
||||
///
|
||||
/// By default, the offset is 0.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use memmap2::MmapOptions;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// let mmap = unsafe {
|
||||
/// MmapOptions::new()
|
||||
/// .offset(30)
|
||||
/// .map(&File::open("LICENSE-APACHE")?)?
|
||||
/// };
|
||||
/// assert_eq!(&b"Apache License"[..],
|
||||
/// &mmap[..14]);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn offset(&mut self, offset: u64) -> &mut Self {
|
||||
self.offset = offset;
|
||||
self
|
||||
}
|
||||
|
||||
/// Configures the created memory mapped buffer to be `len` bytes long.
|
||||
///
|
||||
/// This option is mandatory for anonymous memory maps.
|
||||
///
|
||||
/// For file-backed memory maps, the length will default to the file length.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use memmap2::MmapOptions;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// let mmap = unsafe {
|
||||
/// MmapOptions::new()
|
||||
/// .len(9)
|
||||
/// .map(&File::open("README.md")?)?
|
||||
/// };
|
||||
/// assert_eq!(&b"# memmap2"[..], &mmap[..]);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn len(&mut self, len: usize) -> &mut Self {
|
||||
self.len = Some(len);
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the configured length, or the length of the provided file.
|
||||
fn get_len<T: MmapAsRawDesc>(&self, _file: &T) -> Result<usize> {
|
||||
self.len.map(Ok).unwrap_or_else(|| {
|
||||
let file_len = 0;
|
||||
let len = file_len as u64 - self.offset;
|
||||
if len > (usize::MAX as u64) {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"memory map length overflows usize",
|
||||
));
|
||||
}
|
||||
Ok(len as usize)
|
||||
})
|
||||
}
|
||||
|
||||
/// Configures the anonymous memory map to be suitable for a process or thread stack.
|
||||
///
|
||||
/// This option corresponds to the `MAP_STACK` flag on Linux. It has no effect on Windows.
|
||||
///
|
||||
/// This option has no effect on file-backed memory maps.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use memmap2::MmapOptions;
|
||||
///
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// let stack = MmapOptions::new().stack().len(4096).map_anon();
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn stack(&mut self) -> &mut Self {
|
||||
self.stack = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Populate (prefault) page tables for a mapping.
|
||||
///
|
||||
/// For a file mapping, this causes read-ahead on the file. This will help to reduce blocking on page faults later.
|
||||
///
|
||||
/// This option corresponds to the `MAP_POPULATE` flag on Linux. It has no effect on Windows.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use memmap2::MmapOptions;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// let file = File::open("LICENSE-MIT")?;
|
||||
///
|
||||
/// let mmap = unsafe {
|
||||
/// MmapOptions::new().populate().map(&file)?
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(&b"Copyright"[..], &mmap[..9]);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn populate(&mut self) -> &mut Self {
|
||||
self.populate = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Creates a read-only memory map backed by a file.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns an error when the underlying system call fails, which can happen for a
|
||||
/// variety of reasons, such as when the file is not open with read permissions.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use memmap2::MmapOptions;
|
||||
/// use std::fs::File;
|
||||
/// use std::io::Read;
|
||||
///
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// let mut file = File::open("LICENSE-APACHE")?;
|
||||
///
|
||||
/// let mut contents = Vec::new();
|
||||
/// file.read_to_end(&mut contents)?;
|
||||
///
|
||||
/// let mmap = unsafe {
|
||||
/// MmapOptions::new().map(&file)?
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(&contents[..], &mmap[..]);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub unsafe fn map<T: MmapAsRawDesc>(&self, file: T) -> Result<Mmap> {
|
||||
let desc = file.as_raw_desc();
|
||||
|
||||
MmapInner::map(self.get_len(&file)?, desc.0, self.offset, self.populate)
|
||||
.map(|inner| Mmap { inner })
|
||||
}
|
||||
|
||||
/// Creates a readable and executable memory map backed by a file.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns an error when the underlying system call fails, which can happen for a
|
||||
/// variety of reasons, such as when the file is not open with read permissions.
|
||||
pub unsafe fn map_exec<T: MmapAsRawDesc>(&self, file: T) -> Result<Mmap> {
|
||||
let desc = file.as_raw_desc();
|
||||
|
||||
MmapInner::map_exec(self.get_len(&file)?, desc.0, self.offset, self.populate)
|
||||
.map(|inner| Mmap { inner: inner })
|
||||
}
|
||||
|
||||
/// Creates a writeable memory map backed by a file.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns an error when the underlying system call fails, which can happen for a
|
||||
/// variety of reasons, such as when the file is not open with read and write permissions.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate memmap2;
|
||||
/// # extern crate tempdir;
|
||||
/// #
|
||||
/// use std::fs::OpenOptions;
|
||||
/// use std::path::PathBuf;
|
||||
///
|
||||
/// use memmap2::MmapOptions;
|
||||
/// #
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// # let tempdir = tempdir::TempDir::new("mmap")?;
|
||||
/// let path: PathBuf = /* path to file */
|
||||
/// # tempdir.path().join("map_mut");
|
||||
/// let file = OpenOptions::new().read(true).write(true).create(true).open(&path)?;
|
||||
/// file.set_len(13)?;
|
||||
///
|
||||
/// let mut mmap = unsafe {
|
||||
/// MmapOptions::new().map_mut(&file)?
|
||||
/// };
|
||||
///
|
||||
/// mmap.copy_from_slice(b"Hello, world!");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub unsafe fn map_mut<T: MmapAsRawDesc>(&self, file: T) -> Result<MmapMut> {
|
||||
let desc = file.as_raw_desc();
|
||||
|
||||
MmapInner::map_mut(self.get_len(&file)?, desc.0, self.offset, self.populate)
|
||||
.map(|inner| MmapMut { inner: inner })
|
||||
}
|
||||
|
||||
/// Creates a copy-on-write memory map backed by a file.
|
||||
///
|
||||
/// Data written to the memory map will not be visible by other processes,
|
||||
/// and will not be carried through to the underlying file.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns an error when the underlying system call fails, which can happen for a
|
||||
/// variety of reasons, such as when the file is not open with writable permissions.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use memmap2::MmapOptions;
|
||||
/// use std::fs::File;
|
||||
/// use std::io::Write;
|
||||
///
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// let file = File::open("LICENSE-APACHE")?;
|
||||
/// let mut mmap = unsafe { MmapOptions::new().map_copy(&file)? };
|
||||
/// (&mut mmap[..]).write_all(b"Hello, world!")?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub unsafe fn map_copy<T: MmapAsRawDesc>(&self, file: T) -> Result<MmapMut> {
|
||||
let desc = file.as_raw_desc();
|
||||
|
||||
MmapInner::map_copy(self.get_len(&file)?, desc.0, self.offset, self.populate)
|
||||
.map(|inner| MmapMut { inner: inner })
|
||||
}
|
||||
|
||||
/// Creates a copy-on-write read-only memory map backed by a file.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns an error when the underlying system call fails, which can happen for a
|
||||
/// variety of reasons, such as when the file is not open with read permissions.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use memmap2::MmapOptions;
|
||||
/// use std::fs::File;
|
||||
/// use std::io::Read;
|
||||
///
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// let mut file = File::open("README.md")?;
|
||||
///
|
||||
/// let mut contents = Vec::new();
|
||||
/// file.read_to_end(&mut contents)?;
|
||||
///
|
||||
/// let mmap = unsafe {
|
||||
/// MmapOptions::new().map_copy_read_only(&file)?
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(&contents[..], &mmap[..]);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub unsafe fn map_copy_read_only<T: MmapAsRawDesc>(&self, file: T) -> Result<Mmap> {
|
||||
let desc = file.as_raw_desc();
|
||||
|
||||
MmapInner::map_copy_read_only(self.get_len(&file)?, desc.0, self.offset, self.populate)
|
||||
.map(|inner| Mmap { inner: inner })
|
||||
}
|
||||
|
||||
/// Creates an anonymous memory map.
|
||||
///
|
||||
/// Note: the memory map length must be configured to be greater than 0 before creating an
|
||||
/// anonymous memory map using `MmapOptions::len()`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns an error when the underlying system call fails.
|
||||
pub fn map_anon(&self) -> Result<MmapMut> {
|
||||
MmapInner::map_anon(self.len.unwrap_or(0), self.stack).map(|inner| MmapMut { inner })
|
||||
}
|
||||
|
||||
/// Creates a raw memory map.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns an error when the underlying system call fails, which can happen for a
|
||||
/// variety of reasons, such as when the file is not open with read and write permissions.
|
||||
pub fn map_raw<T: MmapAsRawDesc>(&self, file: T) -> Result<MmapRaw> {
|
||||
let desc = file.as_raw_desc();
|
||||
|
||||
MmapInner::map_mut(self.get_len(&file)?, desc.0, self.offset, self.populate)
|
||||
.map(|inner| MmapRaw { inner: inner })
|
||||
}
|
||||
}
|
||||
|
||||
/// A handle to an immutable memory mapped buffer.
|
||||
///
|
||||
/// A `Mmap` may be backed by a file, or it can be anonymous map, backed by volatile memory. Use
|
||||
/// [`MmapOptions`] or [`map()`] to create a file-backed memory map. To create an immutable
|
||||
/// anonymous memory map, first create a mutable anonymous memory map, and then make it immutable
|
||||
/// with [`MmapMut::make_read_only()`].
|
||||
///
|
||||
/// A file backed `Mmap` is created by `&File` reference, and will remain valid even after the
|
||||
/// `File` is dropped. In other words, the `Mmap` handle is completely independent of the `File`
|
||||
/// used to create it. For consistency, on some platforms this is achieved by duplicating the
|
||||
/// underlying file handle. The memory will be unmapped when the `Mmap` handle is dropped.
|
||||
///
|
||||
/// Dereferencing and accessing the bytes of the buffer may result in page faults (e.g. swapping
|
||||
/// the mapped pages into physical memory) though the details of this are platform specific.
|
||||
///
|
||||
/// `Mmap` is [`Sync`](std::marker::Sync) and [`Send`](std::marker::Send).
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// All file-backed memory map constructors are marked `unsafe` because of the potential for
|
||||
/// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or
|
||||
/// out of process. Applications must consider the risk and take appropriate precautions when using
|
||||
/// file-backed maps. Solutions such as file permissions, locks or process-private (e.g. unlinked)
|
||||
/// files exist but are platform specific and limited.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```
|
||||
/// use memmap2::MmapOptions;
|
||||
/// use std::io::Write;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// let file = File::open("README.md")?;
|
||||
/// let mmap = unsafe { MmapOptions::new().map(&file)? };
|
||||
/// assert_eq!(b"# memmap2", &mmap[0..9]);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// See [`MmapMut`] for the mutable version.
|
||||
///
|
||||
/// [`map()`]: Mmap::map()
|
||||
pub struct Mmap {
|
||||
inner: MmapInner,
|
||||
}
|
||||
|
||||
impl Mmap {
|
||||
/// Creates a read-only memory map backed by a file.
|
||||
///
|
||||
/// This is equivalent to calling `MmapOptions::new().map(file)`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns an error when the underlying system call fails, which can happen for a
|
||||
/// variety of reasons, such as when the file is not open with read permissions.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use std::fs::File;
|
||||
/// use std::io::Read;
|
||||
///
|
||||
/// use memmap2::Mmap;
|
||||
///
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// let mut file = File::open("LICENSE-APACHE")?;
|
||||
///
|
||||
/// let mut contents = Vec::new();
|
||||
/// file.read_to_end(&mut contents)?;
|
||||
///
|
||||
/// let mmap = unsafe { Mmap::map(&file)? };
|
||||
///
|
||||
/// assert_eq!(&contents[..], &mmap[..]);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub unsafe fn map<T: MmapAsRawDesc>(file: T) -> Result<Mmap> {
|
||||
MmapOptions::new().map(file)
|
||||
}
|
||||
|
||||
/// Transition the memory map to be writable.
|
||||
///
|
||||
/// If the memory map is file-backed, the file must have been opened with write permissions.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns an error when the underlying system call fails, which can happen for a
|
||||
/// variety of reasons, such as when the file is not open with writable permissions.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate memmap2;
|
||||
/// # extern crate tempdir;
|
||||
/// #
|
||||
/// use memmap2::Mmap;
|
||||
/// use std::ops::DerefMut;
|
||||
/// use std::io::Write;
|
||||
/// # use std::fs::OpenOptions;
|
||||
///
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// # let tempdir = tempdir::TempDir::new("mmap")?;
|
||||
/// let file = /* file opened with write permissions */
|
||||
/// # OpenOptions::new()
|
||||
/// # .read(true)
|
||||
/// # .write(true)
|
||||
/// # .create(true)
|
||||
/// # .open(tempdir.path()
|
||||
/// # .join("make_mut"))?;
|
||||
/// # file.set_len(128)?;
|
||||
/// let mmap = unsafe { Mmap::map(&file)? };
|
||||
/// // ... use the read-only memory map ...
|
||||
/// let mut mut_mmap = mmap.make_mut()?;
|
||||
/// mut_mmap.deref_mut().write_all(b"hello, world!")?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn make_mut(mut self) -> Result<MmapMut> {
|
||||
self.inner.make_mut()?;
|
||||
Ok(MmapMut { inner: self.inner })
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Mmap {
|
||||
type Target = [u8];
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self.inner.ptr(), self.inner.len()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for Mmap {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Mmap {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Mmap")
|
||||
.field("ptr", &self.as_ptr())
|
||||
.field("len", &self.len())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A handle to a raw memory mapped buffer.
|
||||
///
|
||||
/// This struct never hands out references to its interior, only raw pointers.
|
||||
/// This can be helpful when creating shared memory maps between untrusted processes.
|
||||
pub struct MmapRaw {
|
||||
inner: MmapInner,
|
||||
}
|
||||
|
||||
impl MmapRaw {
|
||||
/// Creates a writeable memory map backed by a file.
|
||||
///
|
||||
/// This is equivalent to calling `MmapOptions::new().map_raw(file)`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns an error when the underlying system call fails, which can happen for a
|
||||
/// variety of reasons, such as when the file is not open with read and write permissions.
|
||||
pub fn map_raw<T: MmapAsRawDesc>(file: T) -> Result<MmapRaw> {
|
||||
MmapOptions::new().map_raw(file)
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to the memory mapped file.
|
||||
///
|
||||
/// Before dereferencing this pointer, you have to make sure that the file has not been
|
||||
/// truncated since the memory map was created.
|
||||
/// Avoiding this will not introduce memory safety issues in Rust terms,
|
||||
/// but will cause SIGBUS (or equivalent) signal.
|
||||
#[inline]
|
||||
pub fn as_ptr(&self) -> *const u8 {
|
||||
self.inner.ptr()
|
||||
}
|
||||
|
||||
/// Returns an unsafe mutable pointer to the memory mapped file.
|
||||
///
|
||||
/// Before dereferencing this pointer, you have to make sure that the file has not been
|
||||
/// truncated since the memory map was created.
|
||||
/// Avoiding this will not introduce memory safety issues in Rust terms,
|
||||
/// but will cause SIGBUS (or equivalent) signal.
|
||||
#[inline]
|
||||
pub fn as_mut_ptr(&self) -> *mut u8 {
|
||||
self.inner.ptr() as _
|
||||
}
|
||||
|
||||
/// Returns the length in bytes of the memory map.
|
||||
///
|
||||
/// Note that truncating the file can cause the length to change (and render this value unusable).
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.inner.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for MmapRaw {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("MmapRaw")
|
||||
.field("ptr", &self.as_ptr())
|
||||
.field("len", &self.len())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A handle to a mutable memory mapped buffer.
|
||||
///
|
||||
/// A file-backed `MmapMut` buffer may be used to read from or write to a file. An anonymous
|
||||
/// `MmapMut` buffer may be used any place that an in-memory byte buffer is needed. Use
|
||||
/// [`MmapMut::map_mut()`] and [`MmapMut::map_anon()`] to create a mutable memory map of the
|
||||
/// respective types, or [`MmapOptions::map_mut()`] and [`MmapOptions::map_anon()`] if non-default
|
||||
/// options are required.
|
||||
///
|
||||
/// A file backed `MmapMut` is created by `&File` reference, and will remain valid even after the
|
||||
/// `File` is dropped. In other words, the `MmapMut` handle is completely independent of the `File`
|
||||
/// used to create it. For consistency, on some platforms this is achieved by duplicating the
|
||||
/// underlying file handle. The memory will be unmapped when the `MmapMut` handle is dropped.
|
||||
///
|
||||
/// Dereferencing and accessing the bytes of the buffer may result in page faults (e.g. swapping
|
||||
/// the mapped pages into physical memory) though the details of this are platform specific.
|
||||
///
|
||||
/// `Mmap` is [`Sync`](std::marker::Sync) and [`Send`](std::marker::Send).
|
||||
///
|
||||
/// See [`Mmap`] for the immutable version.
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// All file-backed memory map constructors are marked `unsafe` because of the potential for
|
||||
/// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or
|
||||
/// out of process. Applications must consider the risk and take appropriate precautions when using
|
||||
/// file-backed maps. Solutions such as file permissions, locks or process-private (e.g. unlinked)
|
||||
/// files exist but are platform specific and limited.
|
||||
pub struct MmapMut {
|
||||
inner: MmapInner,
|
||||
}
|
||||
|
||||
impl MmapMut {
|
||||
/// Creates a writeable memory map backed by a file.
|
||||
///
|
||||
/// This is equivalent to calling `MmapOptions::new().map_mut(file)`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns an error when the underlying system call fails, which can happen for a
|
||||
/// variety of reasons, such as when the file is not open with read and write permissions.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate memmap2;
|
||||
/// # extern crate tempdir;
|
||||
/// #
|
||||
/// use std::fs::OpenOptions;
|
||||
/// use std::path::PathBuf;
|
||||
///
|
||||
/// use memmap2::MmapMut;
|
||||
/// #
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// # let tempdir = tempdir::TempDir::new("mmap")?;
|
||||
/// let path: PathBuf = /* path to file */
|
||||
/// # tempdir.path().join("map_mut");
|
||||
/// let file = OpenOptions::new()
|
||||
/// .read(true)
|
||||
/// .write(true)
|
||||
/// .create(true)
|
||||
/// .open(&path)?;
|
||||
/// file.set_len(13)?;
|
||||
///
|
||||
/// let mut mmap = unsafe { MmapMut::map_mut(&file)? };
|
||||
///
|
||||
/// mmap.copy_from_slice(b"Hello, world!");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub unsafe fn map_mut<T: MmapAsRawDesc>(file: T) -> Result<MmapMut> {
|
||||
MmapOptions::new().map_mut(file)
|
||||
}
|
||||
|
||||
/// Creates an anonymous memory map.
|
||||
///
|
||||
/// This is equivalent to calling `MmapOptions::new().len(length).map_anon()`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns an error when the underlying system call fails.
|
||||
pub fn map_anon(length: usize) -> Result<MmapMut> {
|
||||
MmapOptions::new().len(length).map_anon()
|
||||
}
|
||||
|
||||
/// Flushes outstanding memory map modifications to disk.
|
||||
///
|
||||
/// When this method returns with a non-error result, all outstanding changes to a file-backed
|
||||
/// memory map are guaranteed to be durably stored. The file's metadata (including last
|
||||
/// modification timestamp) may not be updated.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate memmap2;
|
||||
/// # extern crate tempdir;
|
||||
/// #
|
||||
/// use std::fs::OpenOptions;
|
||||
/// use std::io::Write;
|
||||
/// use std::path::PathBuf;
|
||||
///
|
||||
/// use memmap2::MmapMut;
|
||||
///
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// # let tempdir = tempdir::TempDir::new("mmap")?;
|
||||
/// let path: PathBuf = /* path to file */
|
||||
/// # tempdir.path().join("flush");
|
||||
/// let file = OpenOptions::new().read(true).write(true).create(true).open(&path)?;
|
||||
/// file.set_len(128)?;
|
||||
///
|
||||
/// let mut mmap = unsafe { MmapMut::map_mut(&file)? };
|
||||
///
|
||||
/// (&mut mmap[..]).write_all(b"Hello, world!")?;
|
||||
/// mmap.flush()?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn flush(&self) -> Result<()> {
|
||||
let len = self.len();
|
||||
self.inner.flush(0, len)
|
||||
}
|
||||
|
||||
/// Asynchronously flushes outstanding memory map modifications to disk.
|
||||
///
|
||||
/// This method initiates flushing modified pages to durable storage, but it will not wait for
|
||||
/// the operation to complete before returning. The file's metadata (including last
|
||||
/// modification timestamp) may not be updated.
|
||||
pub fn flush_async(&self) -> Result<()> {
|
||||
let len = self.len();
|
||||
self.inner.flush_async(0, len)
|
||||
}
|
||||
|
||||
/// Flushes outstanding memory map modifications in the range to disk.
|
||||
///
|
||||
/// The offset and length must be in the bounds of the memory map.
|
||||
///
|
||||
/// When this method returns with a non-error result, all outstanding changes to a file-backed
|
||||
/// memory in the range are guaranteed to be durable stored. The file's metadata (including
|
||||
/// last modification timestamp) may not be updated. It is not guaranteed the only the changes
|
||||
/// in the specified range are flushed; other outstanding changes to the memory map may be
|
||||
/// flushed as well.
|
||||
pub fn flush_range(&self, offset: usize, len: usize) -> Result<()> {
|
||||
self.inner.flush(offset, len)
|
||||
}
|
||||
|
||||
/// Asynchronously flushes outstanding memory map modifications in the range to disk.
|
||||
///
|
||||
/// The offset and length must be in the bounds of the memory map.
|
||||
///
|
||||
/// This method initiates flushing modified pages to durable storage, but it will not wait for
|
||||
/// the operation to complete before returning. The file's metadata (including last
|
||||
/// modification timestamp) may not be updated. It is not guaranteed that the only changes
|
||||
/// flushed are those in the specified range; other outstanding changes to the memory map may
|
||||
/// be flushed as well.
|
||||
pub fn flush_async_range(&self, offset: usize, len: usize) -> Result<()> {
|
||||
self.inner.flush_async(offset, len)
|
||||
}
|
||||
|
||||
/// Returns an immutable version of this memory mapped buffer.
|
||||
///
|
||||
/// If the memory map is file-backed, the file must have been opened with read permissions.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns an error when the underlying system call fails, which can happen for a
|
||||
/// variety of reasons, such as when the file has not been opened with read permissions.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate memmap2;
|
||||
/// #
|
||||
/// use std::io::Write;
|
||||
/// use std::path::PathBuf;
|
||||
///
|
||||
/// use memmap2::{Mmap, MmapMut};
|
||||
///
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// let mut mmap = MmapMut::map_anon(128)?;
|
||||
///
|
||||
/// (&mut mmap[..]).write(b"Hello, world!")?;
|
||||
///
|
||||
/// let mmap: Mmap = mmap.make_read_only()?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn make_read_only(mut self) -> Result<Mmap> {
|
||||
self.inner.make_read_only()?;
|
||||
Ok(Mmap { inner: self.inner })
|
||||
}
|
||||
|
||||
/// Transition the memory map to be readable and executable.
|
||||
///
|
||||
/// If the memory map is file-backed, the file must have been opened with execute permissions.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns an error when the underlying system call fails, which can happen for a
|
||||
/// variety of reasons, such as when the file has not been opened with execute permissions.
|
||||
pub fn make_exec(mut self) -> Result<Mmap> {
|
||||
self.inner.make_exec()?;
|
||||
Ok(Mmap { inner: self.inner })
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for MmapMut {
|
||||
type Target = [u8];
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self.inner.ptr(), self.inner.len()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for MmapMut {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut [u8] {
|
||||
unsafe { slice::from_raw_parts_mut(self.inner.mut_ptr(), self.inner.len()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for MmapMut {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<[u8]> for MmapMut {
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
self.deref_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for MmapMut {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("MmapMut")
|
||||
.field("ptr", &self.as_ptr())
|
||||
.field("len", &self.len())
|
||||
.finish()
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
use std::io;
|
||||
|
||||
pub struct MmapInner {
|
||||
// Private member to prevent external construction
|
||||
// (might be nice to change the type to ! once that's stable)
|
||||
__: (),
|
||||
}
|
||||
|
||||
impl MmapInner {
|
||||
fn new() -> io::Result<MmapInner> {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"platform not supported",
|
||||
))
|
||||
}
|
||||
|
||||
pub fn map(_: usize, _: i32, _: u64, _: bool) -> io::Result<MmapInner> {
|
||||
MmapInner::new()
|
||||
}
|
||||
|
||||
pub fn map_exec(_: usize, _: i32, _: u64, _: bool) -> io::Result<MmapInner> {
|
||||
MmapInner::new()
|
||||
}
|
||||
|
||||
pub fn map_mut(_: usize, _: i32, _: u64, _: bool) -> io::Result<MmapInner> {
|
||||
MmapInner::new()
|
||||
}
|
||||
|
||||
pub fn map_copy(_: usize, _: i32, _: u64, _: bool) -> io::Result<MmapInner> {
|
||||
MmapInner::new()
|
||||
}
|
||||
|
||||
pub fn map_copy_read_only(_: usize, _: i32, _: u64, _: bool) -> io::Result<MmapInner> {
|
||||
MmapInner::new()
|
||||
}
|
||||
|
||||
pub fn map_anon(_: usize, _: bool) -> io::Result<MmapInner> {
|
||||
MmapInner::new()
|
||||
}
|
||||
|
||||
pub fn flush(&self, _: usize, _: usize) -> io::Result<()> {
|
||||
unreachable!("self unconstructable");
|
||||
}
|
||||
|
||||
pub fn flush_async(&self, _: usize, _: usize) -> io::Result<()> {
|
||||
unreachable!("self unconstructable");
|
||||
}
|
||||
|
||||
pub fn make_read_only(&mut self) -> io::Result<()> {
|
||||
unreachable!("self unconstructable");
|
||||
}
|
||||
|
||||
pub fn make_exec(&mut self) -> io::Result<()> {
|
||||
unreachable!("self unconstructable");
|
||||
}
|
||||
|
||||
pub fn make_mut(&mut self) -> io::Result<()> {
|
||||
unreachable!("self unconstructable");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ptr(&self) -> *const u8 {
|
||||
unreachable!("self unconstructable");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn mut_ptr(&mut self) -> *mut u8 {
|
||||
unreachable!("self unconstructable");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
unreachable!("self unconstructable");
|
||||
}
|
||||
}
|
|
@ -2344,9 +2344,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pyth-sdk"
|
||||
version = "0.4.2"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f262b88557d8f152a247e1be786a8359d63112fac0a6e49fa41082a8ef789e8d"
|
||||
checksum = "f5c805ba3dfb5b7ed6a8ffa62ec38391f485a79c7cf6b3b11d3bd44fb0325824"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"borsh-derive",
|
||||
|
@ -2357,9 +2357,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pyth-sdk-solana"
|
||||
version = "0.4.1"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb250e58d1106e47b4348af87f738b8381d6c98c33f4ad13401ab9d82300daa9"
|
||||
checksum = "286f2abd9fb718190206340f3d1f98a80b34df9d724887b907e7f6f24a31efce"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"borsh-derive",
|
||||
|
@ -2725,7 +2725,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "rocksalt"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/certusone/wormhole?tag=v2.8.9#e47f9e481ef84d4dea7a94c9eafbf3b180892466"
|
||||
source = "git+https://github.com/wormhole-foundation/wormhole?tag=v2.8.9#e47f9e481ef84d4dea7a94c9eafbf3b180892466"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"proc-macro2 1.0.38",
|
||||
|
@ -4001,7 +4001,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "solitaire"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/certusone/wormhole?tag=v2.8.9#e47f9e481ef84d4dea7a94c9eafbf3b180892466"
|
||||
source = "git+https://github.com/wormhole-foundation/wormhole?tag=v2.8.9#e47f9e481ef84d4dea7a94c9eafbf3b180892466"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"byteorder",
|
||||
|
@ -4911,7 +4911,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "wormhole-bridge-solana"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/certusone/wormhole?tag=v2.8.9#e47f9e481ef84d4dea7a94c9eafbf3b180892466"
|
||||
source = "git+https://github.com/wormhole-foundation/wormhole?tag=v2.8.9#e47f9e481ef84d4dea7a94c9eafbf3b180892466"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"byteorder",
|
||||
|
@ -5016,7 +5016,3 @@ dependencies = [
|
|||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[patch.unused]]
|
||||
name = "memmap2"
|
||||
version = "0.1.0"
|
||||
|
|
|
@ -1,4 +1,2 @@
|
|||
[workspace]
|
||||
members = ["client", "program"]
|
||||
[patch.crates-io]
|
||||
memmap2 = { path = "../memmap2-rs" }
|
|
@ -16,10 +16,10 @@ borsh = "=0.9.3"
|
|||
clap = {version = "3.1.18", features = ["derive"]}
|
||||
env_logger = "0.8.4"
|
||||
log = "0.4.14"
|
||||
wormhole-bridge-solana = {git = "https://github.com/certusone/wormhole", tag = "v2.8.9"}
|
||||
wormhole-bridge-solana = {git = "https://github.com/wormhole-foundation/wormhole", tag = "v2.8.9"}
|
||||
pyth2wormhole = {path = "../program"}
|
||||
p2w-sdk = { path = "../../../third_party/pyth/p2w-sdk/rust", features=["solana"] }
|
||||
pyth-sdk-solana = "0.4.0"
|
||||
pyth-sdk-solana = "0.5.0"
|
||||
serde = "1"
|
||||
serde_yaml = "0.8"
|
||||
shellexpand = "2.1.0"
|
||||
|
@ -28,7 +28,7 @@ solana-program = "=1.10.31"
|
|||
solana-sdk = "=1.10.31"
|
||||
solana-transaction-status = "=1.10.31"
|
||||
# solitaire-client = {path = "../../solitaire/client"}
|
||||
solitaire = {git = "https://github.com/certusone/wormhole", tag = "v2.8.9"}
|
||||
solitaire = {git = "https://github.com/wormhole-foundation/wormhole", tag = "v2.8.9"}
|
||||
tokio = {version = "1", features = ["sync", "rt", "time"]}
|
||||
futures = "0.3.21"
|
||||
|
||||
|
|
|
@ -382,15 +382,6 @@ version = "2.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b70ca2a6103ac8b665dc150b142ef0e4e89df640c9e6cf295d189c3caebe5a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.3.3"
|
||||
|
|
|
@ -15,9 +15,9 @@ trace = ["solitaire/trace", "wormhole-bridge-solana/trace"]
|
|||
no-entrypoint = []
|
||||
|
||||
[dependencies]
|
||||
wormhole-bridge-solana = { git = "https://github.com/certusone/wormhole", tag = "v2.8.9" }
|
||||
solitaire = { git = "https://github.com/certusone/wormhole", tag = "v2.8.9"}
|
||||
rocksalt = { git = "https://github.com/certusone/wormhole", tag = "v2.8.9"}
|
||||
wormhole-bridge-solana = { git = "https://github.com/wormhole-foundation/wormhole", tag = "v2.8.9" }
|
||||
solitaire = { git = "https://github.com/wormhole-foundation/wormhole", tag = "v2.8.9"}
|
||||
rocksalt = { git = "https://github.com/wormhole-foundation/wormhole", tag = "v2.8.9"}
|
||||
solana-program = "=1.10.31"
|
||||
borsh = "=0.9.3"
|
||||
pyth-client = "0.2.2"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
module github.com/certusone/wormhole/third_party/abigen
|
||||
module github.com/wormhole-foundation/wormhole/third_party/abigen
|
||||
|
||||
go 1.15
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ $ docker run --platform linux/amd64 -d --network=host spy_guardian \
|
|||
```
|
||||
|
||||
Or run the spy_guardian docker container in MainNet:
|
||||
For the MainNet gossip network parameters, see https://github.com/certusone/wormhole-networks/blob/master/mainnetv2/info.md
|
||||
For the MainNet gossip network parameters, see https://github.com/wormhole-foundation/wormhole-networks/blob/master/mainnetv2/info.md
|
||||
|
||||
```
|
||||
$ docker run --platform linux/amd64 -d --network=host spy_guardian \
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/certusone/wormhole.git"
|
||||
"url": "git+https://github.com/pyth-network/pyth-crosschain.git"
|
||||
},
|
||||
"author": "https://certus.one",
|
||||
"license": "MIT",
|
||||
|
@ -44,7 +44,7 @@
|
|||
"@pythnetwork/pyth-sdk-js": "^0.1.0"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/certusone/wormhole/issues"
|
||||
"url": "https://github.com/pyth-network/pyth-crosschain/issues"
|
||||
},
|
||||
"homepage": "https://github.com/certusone/wormhole#readme"
|
||||
"homepage": "https://github.com/pyth-network/pyth-crosschain#readme"
|
||||
}
|
||||
|
|
|
@ -696,9 +696,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pyth-sdk"
|
||||
version = "0.4.2"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f262b88557d8f152a247e1be786a8359d63112fac0a6e49fa41082a8ef789e8d"
|
||||
checksum = "f5c805ba3dfb5b7ed6a8ffa62ec38391f485a79c7cf6b3b11d3bd44fb0325824"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"borsh-derive",
|
||||
|
@ -709,9 +709,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pyth-sdk-solana"
|
||||
version = "0.4.1"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb250e58d1106e47b4348af87f738b8381d6c98c33f4ad13401ab9d82300daa9"
|
||||
checksum = "286f2abd9fb718190206340f3d1f98a80b34df9d724887b907e7f6f24a31efce"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"borsh-derive",
|
||||
|
@ -825,7 +825,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "rocksalt"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/certusone/wormhole?tag=v2.8.9#e47f9e481ef84d4dea7a94c9eafbf3b180892466"
|
||||
source = "git+https://github.com/wormhole-foundation/wormhole?tag=v2.8.9#e47f9e481ef84d4dea7a94c9eafbf3b180892466"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"proc-macro2",
|
||||
|
@ -1097,7 +1097,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "solitaire"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/certusone/wormhole?tag=v2.8.9#e47f9e481ef84d4dea7a94c9eafbf3b180892466"
|
||||
source = "git+https://github.com/wormhole-foundation/wormhole?tag=v2.8.9#e47f9e481ef84d4dea7a94c9eafbf3b180892466"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"byteorder",
|
||||
|
|
|
@ -17,11 +17,11 @@ wasm = ["wasm-bindgen", "solana"]
|
|||
hex = "0.4.3"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
pyth-sdk = "*"
|
||||
pyth-sdk-solana = { version = "0.4.0", optional = true }
|
||||
pyth-sdk-solana = { version = "0.5.0", optional = true }
|
||||
wasm-bindgen = { version = "0.2.74", features = ["serde-serialize"], optional = true}
|
||||
solitaire = { git = "https://github.com/certusone/wormhole", tag = "v2.8.9", optional = true}
|
||||
solitaire = { git = "https://github.com/wormhole-foundation/wormhole", tag = "v2.8.9", optional = true}
|
||||
solana-program = { version = "=1.10.31", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-program = "=1.10.31"
|
||||
pyth-sdk-solana = "0.4.0"
|
||||
pyth-sdk-solana = "0.5.0"
|
||||
|
|
|
@ -26,7 +26,7 @@ $ docker run --platform linux/amd64 --network=host ghcr.io/certusone/guardiand:v
|
|||
```
|
||||
|
||||
Or run the spy_guardian docker container in MainNet:
|
||||
For the MainNet gossip network parameters, see https://github.com/certusone/wormhole-networks/blob/master/mainnetv2/info.md
|
||||
For the MainNet gossip network parameters, see https://github.com/wormhole-foundation/wormhole-networks/blob/master/mainnetv2/info.md
|
||||
|
||||
```
|
||||
$ docker run --platform linux/amd64 -d --network=host ghcr.io/certusone/guardiand:v2.8.8.1 spy \
|
||||
|
|
Loading…
Reference in New Issue