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:
Ali Behjati 2022-09-02 12:39:51 +02:00 committed by GitHub
parent 8cea878c34
commit b4c8f2e470
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 501 additions and 2642 deletions

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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 \

View File

@ -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

View File

@ -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:

269
cosmwasm/Cargo.lock generated
View File

@ -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"

View File

@ -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" }

View File

@ -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.

View File

@ -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,
}

View File

@ -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 }

View File

@ -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())
);
}
}

View File

@ -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),
}
}
}

View File

@ -2,5 +2,6 @@
extern crate lazy_static;
pub mod contract;
pub mod error;
pub mod msg;
pub mod state;

View File

@ -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,
};

View File

@ -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,

View File

@ -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"

View File

@ -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"

View File

@ -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()
}

View File

@ -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
}

View File

@ -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())
}
}

View File

@ -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;

View File

@ -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,
}

View File

@ -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 })
}
}

View File

@ -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;
}

View File

@ -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"

View File

@ -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"

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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()
}
}

View File

@ -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");
}
}

View File

@ -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"

View File

@ -1,4 +1,2 @@
[workspace]
members = ["client", "program"]
[patch.crates-io]
memmap2 = { path = "../memmap2-rs" }

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -1,4 +1,4 @@
module github.com/certusone/wormhole/third_party/abigen
module github.com/wormhole-foundation/wormhole/third_party/abigen
go 1.15

View File

@ -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 \

View File

@ -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"
}

View File

@ -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",

View File

@ -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"

View File

@ -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 \