From 9fce2e9dedf0408053494d8e9ddaf0fdab1e6e85 Mon Sep 17 00:00:00 2001 From: Leo Date: Thu, 15 Apr 2021 18:02:47 +0200 Subject: [PATCH] take an axe to the docs Delete outdated documentation and generic-ify the remainder. Change-Id: I4e11e59e6da00d49f9ea4fd5d1155769debda55e --- README.md | 57 +++--- docs/assumptions.md | 26 +-- docs/images/overview.excalidraw | 284 ++++++++++++++++------------ docs/images/overview.svg | 6 +- docs/operations.md | 4 +- docs/protocol.md | 321 -------------------------------- docs/solana_program.md | 293 ----------------------------- 7 files changed, 209 insertions(+), 782 deletions(-) delete mode 100644 docs/protocol.md delete mode 100644 docs/solana_program.md diff --git a/README.md b/README.md index 21a17cde1..372762d2a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,4 @@ -# Wormhole - -Read our [introduction blog article](https://medium.com/certus-one/introducing-the-wormhole-bridge-24911b7335f7) -for more details on Wormhole and its major design decisions. +# Wormhole v2 See [DEVELOP.md](DEVELOP.md) for instructions on how to set up a local devnet, and [CONTRIBUTING.md](CONTRIBUTING.md) for instructions on how to contribute to this project. @@ -9,49 +6,45 @@ See [DEVELOP.md](DEVELOP.md) for instructions on how to set up a local devnet, a See [docs/operations.md](docs/operations.md) for node operator instructions. ![](docs/images/overview.svg) -### Audit / Feature Status -| Feature | Maintainer | Auditor | Status | -|-------------------|------------|----------|-----------------| -| Ethereum contract | Certus One | Kudelski | ✔️ Audited | -| Solana contract | Certus One | Kudelski | ✔️ Audited | -| Bridge node | Certus One | Kudelski | ✔️ Audited | -| Terra contract | Everstake | Kudelski | ✔️ Audited | +### Audit / Feature Status ⚠ **This software 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.** Or plainly -spoken - this is a very complex software which targets a bleeding-edge, experimental smart contract runtime. Mistakes -happens, and no matter how hard you try and whether or not you pay someone to audit it, it may eat your tokens, set your -printer on fire or startle your cat. - -Cryptocurrencies in general are a high-risk investment, there's decent chance you'll lose your money, and you most -certainly shouldn't put your life savings into a Wormhole contract (or any other). +spoken - this is a very complex piece of software which targets a bleeding-edge, experimental smart contract runtime. +Mistakes happen, and no matter how hard you try and whether you pay someone to audit it, it may eat your tokens, set +your printer on fire or startle your cat. Cryptocurrencies are a high-risk investment, no matter how fancy. ### Repo overview -- **[bridge/](bridge/)** — The guardian node which connects to both chains, observes lockups and submits VAAs. +- **[bridge/](bridge/)** — The guardian node which connects to both chains, observes data and submits VAAs. Written in pure Go. - - [cmd/](bridge/cmd/) - CLI entry point, deals with the mechanics of parsing command line flags and loading keys. + - [cmd/](bridge/cmd/) - CLI entry points for the guardiand service and all other command line tools. + - [e2e](bridge/e2e) — The end-to-end testing framework (as regular Go tests, to be ran locally). - **[pkg/processor](bridge/pkg/processor)** — Most of the business logic for cross-chain communication lives here. Talks to multiple loosely coupled services communicating via Go channels. + - [pkg/common](bridge/pkg/common) — Shared libraries and types. No business logic. - [pkg/p2p](bridge/pkg/p2p) — libp2p-based gossip network. - [pkg/devnet](bridge/pkg/devnet) — Constants and helper functions for the deterministic local devnet. - [pkg/ethereum](bridge/pkg/ethereum) — Ethereum chain interface with auto-generated contract ABI. Uses go-ethereum to directly connect to an Eth node. - - [pkg/solana](bridge/pkg/ethereum) — Solana chain interface. Light gRPC wrapper around a Rust agent (see below) + - [pkg/solana](bridge/pkg/solana) — Solana chain interface. Light gRPC wrapper around a Rust agent (see below) which actually talks to Solana. + - [pkg/terra](bridge/pkg/terra) — Terra chain interface, using the upstream Terra RPC client. - [pkg/supervisor](bridge/pkg/supervisor) — Erlang-inspired process supervision tree imported from Certus One's internal code base. We use this everywhere in the bridge code for fault tolerance and fast convergence. - [pkg/vaa](bridge/pkg/vaa) — Go implementation of our VAA structure, including serialization code. + - [pkg/readiness](bridge/pkg/readiness) — Global stateful singleton package to manage the /ready endpoint, + similar to how Prometheus metrics are implemented using a global registry. - **[ethereum/](ethereum/)** — Ethereum wormhole contract, tests and fixtures. - - **[contracts/](ethereum/contracts)** — Wormhole itself, the wrapped token and helper libraries. + - **[contracts/](ethereum/contracts)** — Wormhole itself, a wrapped token example and helper libraries. - [migrations/](ethereum/migrations) — Ganache migration that deploys the contracts to a local devnet. This is the starting point for both the tests and the devnet. Note that devnet and tests result in different devnet states. - - [src/send-lockups.js](ethereum/src/send-lockups.js) — Sends ETH lockups in a loop. + - [src/send-lockups.js](ethereum/src/send-lockups.js) — Sends example ETH lockups in a loop. See DEVELOP.md for usage. - **[solana/](solana/)** — Solana sidecar agent, contract and CLI. @@ -60,18 +53,20 @@ certainly shouldn't put your life savings into a Wormhole contract (or any other pure-Go Solana client. - **[bridge/](solana/bridge/)** — Solana Wormhole smart contract code. - [cli/](solana/cli/) — Wormhole user CLI tool for interaction with the smart contract. - - [devnet_setup.sh](solana/devnet_setup.sh) — Devnet initialization and lockup generator + - [devnet_setup.sh](solana/devnet_setup.sh) — Devnet initialization and example code for a lockup program (the Solana equivalent to the Ganache migration + send-lockups.js). Runs as a sidecar alongside the Solana devnet. +- **[terra/](terra/)** — Terra-side smart contracts. + - **[proto/](proto/)** — Protocol Buffer definitions for the P2P network and the local Solana agent RPC. These are heavily commented and a good intro. - **[third_party/](third_party/)** — Build machinery and tooling for third party applications we use. - - [abigen/](third_party/abigen/) — Reproducible build for the go-ethereum ABI code generator we use. - - **[solana/](third_party/solana/)** — Build for the full Solana project plus a floating patchset we maintain while - waiting for features to be implemented in the upstream project. + - [googleapis/](third_party/googleapis/) — Google protobuf libraries end up here at runtime. Not checked un. - **[docs/](docs/)** — Operator documentation and project specs. + +- **[design/](design/)** — Design documents/RfC for changes to the protocol. - **[web/](web/)** — User interface for cross-chain transfers. Not yet wired into the local devnet. Uses Metamask and Web3.js to initiate transfers from a browser. @@ -79,10 +74,12 @@ certainly shouldn't put your life savings into a Wormhole contract (or any other - [tools/](tools/) — Reproducible builds for local development tooling like buf and protoc-gen-go. -- [Tiltfile](Tiltfile), [devnet/](devnet/) and various Dockerfiles — deployment code and fixtures for local development. - Deploys a deterministic devnet with an Ethereum devnet, Solana devnet, and a variably sized guardian set - that can be used to simulate full cross-chain transfers. The Dockerfiles are carefully designed for fast incremental - builds with local caching, and require a recent Docker version with Buildkit support. See DEVELOP.md for usage. +- [dashboards/](dashboards/) — Example Grafana dashboards for the Prometheus metrics exposed by guardiand. + +- [Tiltfile](Tiltfile), [tilt_modules](tilt_modules/), [devnet/](devnet/) and various Dockerfiles — deployment code and + fixtures for local development. Deploys a deterministic devnet with an Ethereum devnet, Solana devnet, and a variably + sized guardian set that can be used to simulate full cross-chain transfers. The Dockerfiles are carefully designed for + fast incremental builds with local caching, and require a recent Docker version with Buildkit support. - [generate-abi.sh](generate-abi.sh) and [generate-protos.sh](generate-protos.sh) — Helper scripts to (re-)build generated code. The Eth ABI is committed to the repo, so you only diff --git a/docs/assumptions.md b/docs/assumptions.md index deed89498..fce2c8e10 100644 --- a/docs/assumptions.md +++ b/docs/assumptions.md @@ -3,7 +3,7 @@ This page details various assumptions that Wormhole relies on for security and availability. Many of these are universal assumptions that apply to various decentralized protocols. -This document assumes familiarity with Wormhole concepts like VAAs and lockups/transfers. +This document assumes familiarity with Wormhole concepts like VAAs. ## Gossip network availability @@ -16,24 +16,24 @@ We do _not_ rely on libp2p for security, only for availability. libp2p's channel default, but we do not rely on that property. A compromise of libp2p transport security could, at worst, result in denial of service attacks on the gossip network or individual nodes. -Gossip network unavailability can result in transfers getting temporarily stuck, but never permanently. Nodes will -periodically attempt to retransmit signatures for VAAs which failed to reach consensus in order to mitigate short-term -network outages. Longer network outages, leading to timeouts, and correlated crashes of a superminority of nodes may -result in lockups being dropped. +Gossip network unavailability can result in missing events, but never permanently. Nodes will periodically +attempt to retransmit signatures for VAAs which failed to reach consensus in order to mitigate short-term +network outages. Longer network outages, leading to timeouts, and correlated crashes of a superminority of +nodes may result in observations being dropped. The mitigation for this is a polling control loop in the case of Solana or chain replay for other chains. On Solana, the -node will consistently poll for unprocessed lockups, resulting in re-observation by nodes and another round of +node will consistently poll for unprocessed observations, resulting in re-observation by nodes and another round of consensus. During chain replay, nodes will re-process events from connected chains up from a given block height, check -whether a VAA has already been submitted to Solana, and initiate a round of consensus for missed lockups. +whether a VAA has already been submitted to Solana, and initiate a round of consensus for missed observations. -This carries no risk and can be done any number of times. VAAs are fully deterministic and idempotent - any given -lockup will always result in the same VAA body hash. All connected chains keep a permanent record of whether a given VAA -body - identified by its hash - has already been executed, therefore, VAAs can safely undergo multiple rounds of -consensus until they are executed on all chains. +This carries no risk and can be done any number of times. VAAs are fully deterministic and idempotent - any +given observation will always result in the same VAA body hash. All connected chains keep a permanent record +of whether a given VAA body - identified by its hash - has already been executed, therefore, VAAs can safely +undergo multiple rounds of consensus until they are executed on all chains. The bridge does not yet implement chain replay (see https://github.com/certusone/wormhole/issues/123). Network outages -can therefore result in stuck transfers from chains other than Solana in the case of a prolonged network outage. It -would be possible to retroactively recover locked funds after chain replay has been implemented. +can therefore result in missed observations from chains other than Solana in the case of a prolonged network outage. It +will be possible to retroactively replay blocks after chain replay has been implemented to catch up on missed events. ## Chain consistency and finality diff --git a/docs/images/overview.excalidraw b/docs/images/overview.excalidraw index d86082078..f387c052f 100644 --- a/docs/images/overview.excalidraw +++ b/docs/images/overview.excalidraw @@ -5,8 +5,8 @@ "elements": [ { "type": "rectangle", - "version": 81, - "versionNonce": 1505853226, + "version": 130, + "versionNonce": 526326665, "isDeleted": false, "id": "sCKknWK5gOtk7aQyiPt0Q", "fillStyle": "cross-hatch", @@ -15,8 +15,8 @@ "roughness": 1, "opacity": 100, "angle": 0, - "x": 747.3333740234375, - "y": 413.3333435058594, + "x": 761.619088309152, + "y": 418.0952627999442, "strokeColor": "#000000", "backgroundColor": "#fab005", "width": 135.33331298828125, @@ -31,8 +31,8 @@ }, { "type": "rectangle", - "version": 346, - "versionNonce": 1166619510, + "version": 348, + "versionNonce": 980999977, "isDeleted": false, "id": "OgGKrVx53b487RcYG7GQ9", "fillStyle": "cross-hatch", @@ -83,8 +83,8 @@ }, { "type": "text", - "version": 24, - "versionNonce": 1225644650, + "version": 69, + "versionNonce": 785550665, "isDeleted": false, "id": "W-7TUzZOWaf-F2KGY00Ru", "fillStyle": "hachure", @@ -93,8 +93,8 @@ "roughness": 1, "opacity": 100, "angle": 0, - "x": 770.5000305175781, - "y": 429.1666717529297, + "x": 784.7857448032926, + "y": 433.9285910470145, "strokeColor": "#000000", "backgroundColor": "transparent", "width": 89, @@ -396,7 +396,9 @@ -28 ] ], - "lastCommittedPoint": null + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null }, { "type": "line", @@ -432,7 +434,9 @@ 1.3333740234375 ] ], - "lastCommittedPoint": null + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null }, { "type": "line", @@ -468,7 +472,9 @@ 24.66668701171875 ] ], - "lastCommittedPoint": null + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null }, { "type": "line", @@ -504,7 +510,9 @@ 27.33331298828125 ] ], - "lastCommittedPoint": null + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null }, { "type": "line", @@ -540,7 +548,9 @@ 6.66668701171875 ] ], - "lastCommittedPoint": null + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null }, { "type": "line", @@ -576,7 +586,9 @@ 9.33331298828125 ] ], - "lastCommittedPoint": null + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null }, { "type": "line", @@ -612,7 +624,9 @@ 24.6666259765625 ] ], - "lastCommittedPoint": null + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null }, { "type": "line", @@ -648,7 +662,9 @@ -11.33331298828125 ] ], - "lastCommittedPoint": null + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null }, { "type": "line", @@ -684,7 +700,9 @@ -17.33331298828125 ] ], - "lastCommittedPoint": null + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null }, { "type": "line", @@ -720,7 +738,9 @@ -13.3333740234375 ] ], - "lastCommittedPoint": null + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null }, { "type": "line", @@ -756,7 +776,9 @@ 1.33331298828125 ] ], - "lastCommittedPoint": null + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null }, { "type": "line", @@ -792,7 +814,9 @@ 16 ] ], - "lastCommittedPoint": null + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null }, { "type": "line", @@ -828,7 +852,9 @@ 14.66668701171875 ] ], - "lastCommittedPoint": null + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null }, { "type": "line", @@ -864,12 +890,14 @@ 4 ] ], - "lastCommittedPoint": null + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null }, { "type": "arrow", - "version": 76, - "versionNonce": 2014279734, + "version": 186, + "versionNonce": 233095785, "isDeleted": false, "id": "NI9ug2WbMsfekcmi3MbHa", "fillStyle": "hachure", @@ -878,12 +906,12 @@ "roughness": 2, "opacity": 100, "angle": 0, - "x": 861.3333740234375, - "y": 472, + "x": 873.7531240940943, + "y": 476.7619192940848, "strokeColor": "#a61e4d", "backgroundColor": "transparent", - "width": 96.6666259765625, - "height": 55.3333740234375, + "width": 84.49987369504424, + "height": 50.24227926232709, "seed": 767123201, "groupIds": [], "strokeSharpness": "round", @@ -904,11 +932,13 @@ 0 ], [ - 96.6666259765625, - 55.3333740234375 + 84.49987369504424, + 50.24227926232709 ] ], - "lastCommittedPoint": null + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow" }, { "type": "arrow", @@ -948,12 +978,14 @@ 78 ] ], - "lastCommittedPoint": null + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow" }, { "type": "arrow", - "version": 307, - "versionNonce": 52363638, + "version": 373, + "versionNonce": 985492585, "isDeleted": false, "id": "wltib7el8aYeC2e7nUsKB", "fillStyle": "hachure", @@ -962,12 +994,12 @@ "roughness": 2, "opacity": 100, "angle": 0, - "x": 853.3333740234376, - "y": 631.3557357130856, + "x": 853.3333740234375, + "y": 627.4385470299487, "strokeColor": "#a61e4d", "backgroundColor": "transparent", - "width": 114.78378071898476, - "height": 42.24405385173236, + "width": 127.94827892744422, + "height": 45.310365787157025, "seed": 1134318273, "groupIds": [], "strokeSharpness": "round", @@ -984,16 +1016,18 @@ 0 ], [ - 114.78378071898476, - -42.24405385173236 + 127.94827892744422, + -45.310365787157025 ] ], - "lastCommittedPoint": null + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow" }, { "type": "text", - "version": 125, - "versionNonce": 1629871478, + "version": 205, + "versionNonce": 73736201, "isDeleted": false, "id": "MT5NM89SFPtRsCoa3Lyuu", "fillStyle": "hachure", @@ -1002,19 +1036,21 @@ "roughness": 2, "opacity": 100, "angle": 0, - "x": 907.3333740234375, - "y": 425.666748046875, + "x": 824.0000261579243, + "y": 525.666748046875, "strokeColor": "#a61e4d", "backgroundColor": "transparent", - "width": 111, + "width": 121, "height": 51, "seed": 1650516385, "groupIds": [], "strokeSharpness": "sharp", - "boundElementIds": [], + "boundElementIds": [ + "wltib7el8aYeC2e7nUsKB" + ], "fontSize": 20, "fontFamily": 1, - "text": "Lockup\nobservation", + "text": "Data\nobservations", "baseline": 43, "textAlign": "left", "verticalAlign": "top" @@ -1061,52 +1097,14 @@ -80 ] ], - "lastCommittedPoint": null - }, - { - "type": "arrow", - "version": 192, - "versionNonce": 152796202, - "isDeleted": false, - "id": "_9zjwMOLnRasOCqFWujjU", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 970.000244140625, - "y": 602.6667175292969, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "width": 110.000244140625, - "height": 45.99993896484375, - "seed": 1622859754, - "groupIds": [], - "strokeSharpness": "round", - "boundElementIds": [], - "startBinding": null, - "endBinding": { - "elementId": "OgGKrVx53b487RcYG7GQ9", - "focus": 1.0111297059958109, - "gap": 12.6666259765625 - }, - "points": [ - [ - 0, - 0 - ], - [ - -110.000244140625, - 45.99993896484375 - ] - ], - "lastCommittedPoint": null + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow" }, { "type": "text", - "version": 256, - "versionNonce": 454663158, + "version": 273, + "versionNonce": 1579098407, "isDeleted": false, "id": "Al_x8qQKl3-glzn5YV7Ve", "fillStyle": "hachure", @@ -1119,7 +1117,7 @@ "y": 451.6667785644531, "strokeColor": "#0b7285", "backgroundColor": "transparent", - "width": 87, + "width": 102, "height": 51, "seed": 807351791, "groupIds": [], @@ -1127,15 +1125,15 @@ "boundElementIds": [], "fontSize": 20, "fontFamily": 1, - "text": "PostVAA\nby bridge", + "text": "Data\navailability", "baseline": 43, "textAlign": "left", "verticalAlign": "top" }, { "type": "diamond", - "version": 694, - "versionNonce": 1890457130, + "version": 699, + "versionNonce": 808025447, "isDeleted": false, "id": "inJcSRbT69V_jEbzmNMfN", "fillStyle": "cross-hatch", @@ -1157,8 +1155,8 @@ }, { "type": "text", - "version": 378, - "versionNonce": 1025246390, + "version": 379, + "versionNonce": 418882919, "isDeleted": false, "id": "RnmFmIKXCAaAu_SUnSttl", "fillStyle": "hachure", @@ -1168,7 +1166,7 @@ "opacity": 100, "angle": 0, "x": 834.6666259765625, - "y": 237.6666259765625, + "y": 238.14280918666293, "strokeColor": "#862e9c", "backgroundColor": "transparent", "width": 85, @@ -1222,7 +1220,9 @@ -42.52408563756143 ] ], - "lastCommittedPoint": null + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow" }, { "type": "text", @@ -1255,8 +1255,8 @@ }, { "type": "arrow", - "version": 1618, - "versionNonce": 56918262, + "version": 1675, + "versionNonce": 1099805863, "isDeleted": false, "id": "ZE6FhNQFYu6AYrvqdRLU6", "fillStyle": "hachure", @@ -1266,11 +1266,11 @@ "opacity": 100, "angle": 0, "x": 872.5969816218751, - "y": 330.5479426569268, + "y": 331.0241476652974, "strokeColor": "#5f3dc4", "backgroundColor": "transparent", - "width": 39.93017253984385, - "height": 67.45199630791694, + "width": 33.71238621455768, + "height": 72.21391560200175, "seed": 1727638817, "groupIds": [], "strokeSharpness": "round", @@ -1278,8 +1278,8 @@ "startBinding": null, "endBinding": { "elementId": "sCKknWK5gOtk7aQyiPt0Q", - "focus": 0.21367688360477113, - "gap": 15.333404541015625 + "focus": 0.05256774672249361, + "gap": 14.85719953264504 }, "points": [ [ @@ -1291,16 +1291,60 @@ 29.452057343073193 ], [ - -39.93017253984385, - 67.45199630791694 + -33.71238621455768, + 72.21391560200175 ] ], - "lastCommittedPoint": null + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "type": "arrow", + "version": 1940, + "versionNonce": 1700404967, + "isDeleted": false, + "id": "0rKBEZQ_Nr7_AXvWTouGL", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 867.3589663107703, + "y": 328.1669612116143, + "strokeColor": "#5f3dc4", + "backgroundColor": "transparent", + "width": 143.16811373683038, + "height": 246.9654650238051, + "seed": 1138759303, + "groupIds": [], + "strokeSharpness": "round", + "boundElementIds": [], + "startBinding": null, + "endBinding": null, + "points": [ + [ + 0, + 0 + ], + [ + -143.16811373683038, + 94.6901380489885 + ], + [ + -133.2635204053572, + 246.9654650238051 + ] + ], + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow" }, { "type": "text", - "version": 891, - "versionNonce": 222121718, + "version": 1093, + "versionNonce": 1178279021, "isDeleted": false, "id": "y2PJg0h4wOfU7ziwuzNgP", "fillStyle": "cross-hatch", @@ -1309,26 +1353,26 @@ "roughness": 2, "opacity": 100, "angle": 0, - "x": 674.6664428710938, - "y": 351, + "x": 565.6187853131976, + "y": 485.3808942522322, "strokeColor": "#5f3dc4", "backgroundColor": "#15aabf", "width": 138, - "height": 40, + "height": 80, "seed": 1348924047, "groupIds": [], "strokeSharpness": "sharp", "boundElementIds": [], "fontSize": 16, "fontFamily": 1, - "text": "PostVAA with\nfees paid by user", - "baseline": 34, + "text": "Call to target\ncontract with \nfees paid by user\n", + "baseline": 74, "textAlign": "right", "verticalAlign": "top" } ], "appState": { - "viewBackgroundColor": "#ffffff", - "gridSize": null + "gridSize": null, + "viewBackgroundColor": "#ffffff" } } \ No newline at end of file diff --git a/docs/images/overview.svg b/docs/images/overview.svg index c12451fe3..339dab49d 100644 --- a/docs/images/overview.svg +++ b/docs/images/overview.svg @@ -1,11 +1,11 @@ - + - EthereumSolanaTerraBridgeLockupobservationPostVAAby bridgeEnd userwalletVAA retrievalPostVAA withfees paid by user \ No newline at end of file + EthereumSolanaTerraBridgeDataobservationsDataavailabilityEnd userwalletVAA retrievalCall to targetcontract with fees paid by user \ No newline at end of file diff --git a/docs/operations.md b/docs/operations.md index b48e59735..03e6ff007 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -22,8 +22,8 @@ In addition to Wormhole itself, you need to run your own verifying node for ever to somebody else's full node would be sufficient, but you'd then depend on that single node for availability unless you set up a load balancer pointing to a set of nodes.\] -Do NOT use third-party RPC service providers for any of the chains! You'd fully trust them and they could lie to you on -whether a lockup has actually been observed, and the whole point of Wormhole is to not rely on centralized nodes. +Do NOT use third-party RPC service providers for any of the chains! You'd fully trust them, and they could lie to you on +whether an event has actually been observed. The whole point of Wormhole is not to rely on centralized nodes! ### Ethereum node requirements diff --git a/docs/protocol.md b/docs/protocol.md deleted file mode 100644 index 32ddd76c3..000000000 --- a/docs/protocol.md +++ /dev/null @@ -1,321 +0,0 @@ -# Wormhole Protocol - -The Wormhole protocol is a way of transferring assets between a **root chain** and multiple **foreign chains**. -It makes use of decentralized oracles called **guardians** to relay transfer information about token transfers -between the chains. - -## The role of guardians - -Guardians are responsible for monitoring the root and foreign chains for token transfers to bridge *smart contracts*. -This can be done using full or light clients of the particular network. -They need to make sure to monitor finality of transactions (e.g. track number of confirmations) before relaying messages. - -A guardian is identified by an **admin key** and **voter key**. - -The **admin key** is supposed to be held in cold-storage and is used to manage rewards and assign a signer key. - -The **signer key** is a hot-key that is used to confirm asset transfers between chains by reporting lockups of tokens -on a foreign chain on the root chain or the other way around. - -## Protocol - -The following section describes the protocol and design decisions made. - -### Signature scheme - -In order to implement a decentralized bridge, there needs to be a consensus mechanism to measure whether there is a quorum -on a cross chain transfer to prevent a single malicious actor from unlocking or minting an infinite amount of assets. - -There are multiple ways to measure whether enough validators have approved a decision: - -#### Multiple signatures - MultiSig - -The most simple solution is by using a *MultiSig* mechanism. This means that each guardian would sign a message -and submit it via a P2P gossip network. - -Once the consensus threshold has been reached, a guardian will aggregate all signatures into a VAA and execute/submit it -on the chain. - -The downside here is that gas costs increase with larger guardian sets bringing verification costs to - `(5k+5k)*n` (`ECRECOVER+GTXDATANONZERO*72`). - -To prevent lagging and complex gas price handling by validators or relayers, we always submit VAAs to Solana where txs -are negligibly cheap. In the case of a Solana -> ETH transfer. Guardians would publish a signed VAA on Solana and a user -or independently paid relayer would publish said VAA on Ethereum, paying for gas costs. This mechanism is similar to a -check issued by the guardians (a VAA) which can be used on another chain to claim assets. - -#### Threshold signatures - -Most of the disadvantages of the MultiSig solution come down to the high gas costs of verifying multiple transactions -and tracking individual guardian key changes / set changes on other chains. - -In order to prove a quorum on a single signature, there exist different mechanisms for so-called Threshold signatures. -A single signature is generated using a multi party computation process or aggregation of signatures from different -parties of a group and only valid if a previously specified quorum has participated in the generation of such signature. - -This would essentially mean that such a signature could be published on the Solana chain and relayed by anyone to -authorize an action on another chain, the same concept as described above but implemented with the cost of only -sending and verifying one signature. - -Since we target Ethereum as primary foreign chain, there are 3 viable options of threshold signatures: - -**t-ECDSA** - -Threshold ECDSA signatures generated using [GG20](https://eprint.iacr.org/2020/540.pdf). -This is a highly complex, cutting edge cryptographic protocol that requires significant amounts of compute to generate -signatures with larger quorums. - -Still, it generates plain ECDSA signatures that can easily be verified on Ethereum (`5k gas`) or even be used for Bitcoin -transactions. - -**BLS** - -Boneh–Lynn–Shacham threshold signatures are very lightweight because they don't require a multi-round process and can -simply be aggregated from multiple individual signatures. This would eliminate the need for a p2p layer for MPC -communication. -However, verifying a BLS signature on Ethereum costs about 130k gas using the precompiled pairing functions over bn128. -Also there's very little prior work on this scheme especially in the context of Solidity. - -**Schnorr-Threshold** - -Schnorr threshold signatures require a multi-round computation and distributed key generation. -They can be verified on Ethereum extremely cheaply (https://blog.chain.link/threshold-signatures-in-chainlink/) and scale -well with more signing parties. -There's been significant prior work in the blockchain space, several implementations over different curves and a proposal -to implement support on Bitcoin (BIP340). - ---- - -A great overview can be found [here](https://github.com/Turing-Chain/TSSKit-Threshold-Signature-Scheme-Toolkit) - -#### Design choices - -For transfers we implement a simple MultiSig schema. -We'll create a portable "action blob" with a threshold signature to allow anyone to relay action approvals -between chains. We call this structure: **VAA** (Verifiable Action Approval). - -A validator action approval guarantees eventual consistency across chains - if the validators have submitted a VAA to a token lockup -on Solana, this VAA can be used to unlock the tokens on the specified foreign chain. - -While for the above mentioned transfers from Solana => foreign chain we use Solana for data availability of the VAAs, -in the other direction data availability i.e. the guardians posting the VAA on the foreign chain (where the transfer -was initiated) is optional because in most cases it will be substantially cheaper for the guardians to directly submit -the VAA on Solana itself to unlock/mint the transferred tokens there. - -### VAA - Verifiable Action Approval - -Verifiable action approvals are used to approve the execution of a specified action on a chain. - -They are structured as follows: - -``` -Header: -uint8 version (0x01) -uint32 guardian set index -uint8 len signatures - -per signature: -uint8 index of the signer (in guardian keys) -[65]uint8 signature - -body: -uint32 unix seconds -uint8 action -[payload_size]uint8 payload -``` - -The `guardian set index` does not need to be in the signed body since it is verifiable using the signature itself which -is created using the guardian set's key. It is a number that's monotonically increasing every time a validator set -update happens and tracks the public key hashes of the set. - -#### Actions - -##### Guardian set update - -ID: `0x01` - -Payload: - -``` -uint32 new_index -uint8 len(keys) -[][20]uint8 guardian addresses -``` - -The `new_index` must be monotonically increasing and is manually specified here to fix a potential guardian_set index -desynchronization between the any of the chains in the system. - -##### Contract upgrade - -ID: `0x02` - -Payload: - -``` -uint8 chain_id -[32]uint8 new_contract -``` - -`chain_id` specifies the chain on which the contract should be updated. `new_contract` is the address of the updated -contract. - -##### Transfer - -ID: `0x10` - -Payload: - -``` -uint32 nonce -uint8 source_chain -uint8 target_chain -[32]uint8 source_address -[32]uint8 target_address -uint8 token_chain -[32]uint8 token_address -uint8 decimals -uint256 amount -``` - -### Cross-Chain Transfers - -#### Transfer of assets Foreign Chain -> Root Chain - -If this is the first time the asset is transferred to the root chain, the user inititates a `CreateWrapped` instruction -on the root chain to initialize the wrapped asset. - -The user creates a token account for the wrapped asset on the root chain. - -The user sends a chain native asset to the bridge on the foreign chain using the `Lock` function. -The lock function takes a Solana `address` as parameter which is the TokenAccount that should receive the wrapped token. - -Guardians will pick up the *Lock transaction* once it has enough confirmations on the foreign chain. The amount of -confirmations required is a parameter that guardians can specify individually. - -They check for the validity, parse it and will then initiate a threshold signature ceremony on a deterministically -produced VAA (`Transfer`) testifying that they have seen a foreign lockup. They will post this VAA on the root chain -using the `SubmitVAA` instruction. - -This instruction will either mint a new wrapped asset or release tokens from custody. -Custody is used for Solana-native tokens that have previously been transferred to a foreign chain, minting will be used - to create new units of a wrapped foreign-chain asset. - -If this is the first time a foreign asset is minted, a new **Mint** (token) will be created on quorum. - -### Transfer of assets Root Chain -> Foreign Chain - -The user sends a **Lock** or **LockNative** instruction to the *Bridge program*. - -**Lock** has to be used for wrapped assets that should be transferred to a foreign chain. They will be burned on Solana. - -**LockNative** has to be used for Solana-native assets that should be transferred to a foreign chain. They will be held -in a custody account until the tokens are transferred back from the foreign chain. - -The lock function takes a `chain_id` which identifies the foreign chain the tokens should be sent to and a `foreign_address` -which is a left-zero-padded address on the foreign chain. This operation creates a **LockProposal** account -that tracks the status of the transfer. - -Guardians will pick up the **LockProposal** once it has enough confirmations on the Solana network. It defaults to -full confirmation (i.e. the max lockup, currently 32 slots), but can be changed to a different commitment levels -on each guardian's discretion. - -They check for the validity of the tx, parse it and will initiate an off-chain signature aggregation ceremony which will -output a **VAA** that can be used with a foreign chain smart contract to reclaim an unwrapped local asset or mint a -wrapped `spl-token`. - -This VAA will be posted on Solana by one of the guardians using the `SubmitVAA` instruction and will be stored in the -`LockProposal`. - -The user can then get the VAA from the `LockProposal` and submit it on the foreign chain. - -### Fees - -Fees exist for 2 reasons: spam prevention and guardian cost coverage. - -Costs for guardians: - -Assuming no hosting costs for a guardian operation (blockchain and guardian nodes), the only costs -that need to be covered by a guardian operator are Solana transaction fees as well as rent costs for newly -created account (used to store application information). - -**For a transfer from Solana to a foreign chain (20 guardians; 14 quorum):** - -Transactions required: `3 (signatures + verify) + 1 (post VAA)` - -Accounts created: `1 ClaimedVAA + 1 SignatureState` - -Costs: -``` -4 TX (14 secp signatures + 4 ed25519) + ClaimedVAA (exemption rent) + SignatureState (exemption rent) -18 * 10_000 + (40+128) * 6962 + (1340+128) * 6962 -11569832 lamports = 0.0116 SOL -``` - -**For a transfer from a foreign chain to Solana (20 guardians; 14 quorum):** - -Transactions required: `3 (signatures + verify) + 1 (post VAA)` - -Accounts created: `1 ClaimedVAA + 1 SignatureState (temporary; evicted in PostVAA)` - -Costs: -``` -4 TX (14 secp signatures + 4 ed25519) + ClaimedVAA (exemption rent) -18 * 10_000 + (40+128) * 6962 -1349616 lamports = 0.0013 SOL -``` - ---- - -In order to cover rent costs there exists a subsidy pool controlled by the bridge to cover rent payments. -While the guardian needs to hold enough SOL to pay for the rent, it is automatically refunded by the pool, -in case the pool has sufficient balance. - -This subsidy pool is funded by transaction fees. -Additionally, the subsidy pool subsidizes the transactions fees paid by the guardian submitting the VAA. -As long as the pool has a sufficient balance, it will try to refund transaction fees to the guardian. - -Since Wormhole does not require foreign chain users to own SOL, Wormhole can't charge subsidy fees on inbound -transfers. Assuming a balance between inbound and outbound transfers, outbound transfers need to subsidize -inbound Solana transfers. - -Additionally, foreign chain contracts might start charging additional fees in the future. - ---- - -The bridge can handle fewer transactions per second than Solana. Therefore, the fees should prevent spam -by dynamically adjusting to load. This is particularly useful on Solana where fees are low and spamming -would be cheap. - -Dynamic fees should be cheap while the system is under low and medium load and high while the system is -close or above its capacity. -To prevent sudden fee changes, the fee system has inertia. - -Fees scale as follows `fee = (tps/tps_max)^6`. -The result is the fee per transfer in SOL. So at max capacity, the price per transfer is 1SOL. -TPS is measured over a 30 second window. - -The minimum fee is the equivalent of 2x the rent of SignatureState and ClaimedVAA to cover the cost -of this transfer and about 10 inbound transfers. - ---- - -The above design can currently not be implemented due to limitations in the Solana BPF VM. - -In the current design, tx fees are refunded, rents are subsidized by the bridge and transfers out of Solana -cost a fixed fee of 2x (ClaimedVAA rent + SignatureState rent + VAA submission fee), which will roughly -pay for 1 outbound + ~10 inbound transfers. - -### Config changes -#### Guardian set changes - -The guardians need to make sure that the sets are synchronized between all chains. -If the guardian set is changed, the guardian must also be replaced on all foreign chains. Therefore we -conduct these changes via VAAs that are universally valid on all chains. - -That way, if a change is made on the root chain, the same signatures can be used to trigger the same -update on the foreign chain. This allows all parties in the system to propagate bridge state changes across all -chains. - -If all VAAs issued by the previous guardian set would immediately become invalid once a new guardian set takes over, that would -lead to some payments being "stuck". Therefore we track a list of previous guardian sets. VAAs issued by old -guardian sets stay valid for one day from the time that the change happens in the default configuration. diff --git a/docs/solana_program.md b/docs/solana_program.md deleted file mode 100644 index ab29be0dd..000000000 --- a/docs/solana_program.md +++ /dev/null @@ -1,293 +0,0 @@ -# Solana Wormhole Program - -The `Wormhole` program acts as a bridge for Solana \<> Foreign Chain transfers using the WhP (WormHoleProtocol). - -## Instructions - -#### Initialize - -Initializes a new Bridge at `bridge`. - -| Index | Name | Type | signer | writeable | empty | derived | -| ----- | ------ | ------------ | ------ | --------- | ----- | ------- | -| 0 | sys | SystemProgram | | | | | -| 1 | clock | Sysvar | | | | ✅ | -| 2 | bridge | BridgeConfig | | ✅ | ✅ | ✅ | -| 3 | guardian_set | GuardianSet | | ✅ | ✅ | ✅ | -| 4 | payer | Account | ✅ | | | | - -#### PokeProposal - -Pokes a `TransferOutProposal` so it is reprocessed by the guardians. - -**Deprecated:** PokeProposals were a workaround for unreliable message delivery on Solana. Now that this has been fixed -using a control loop (https://github.com/certusone/wormhole/commit/fd6c54de836cb9f4c423aa334b73546a139c0ee6), poking is -no longer required. The feature is left in place for backwards compatibility reasons. - -| Index | Name | Type | signer | writeable | empty | derived | -| ----- | ------ | ------------ | ------ | --------- | ----- | ------- | -| 0 | proposal | TransferOutProposal | | ✅ | ️ | ✅ | - -#### CreateWrappedAsset - -Creates a new `WrappedAsset` to be used to create accounts and later receive transfers on chain. - -| Index | Name | Type | signer | writeable | empty | derived | -| ----- | -------- | ------------------- | ------ | --------- | ----- | ------- | -| 0 | sys | SystemProgram | | | | | -| 1 | token_program | SplToken | | | | | -| 2 | rent | Sysvar | | | | ✅ | -| 3 | bridge | BridgeConfig | | | | | -| 4 | payer | Account | ✅ | | | | -| 5 | wrapped_mint | WrappedAsset | | | ✅ | ✅ | -| 6 | wrapped_meta_account | WrappedAssetMeta | | ✅ | ✅ | ✅ | - -#### VerifySignatures - -Checks secp checks (in the previous instruction) and stores results. - -| Index | Name | Type | signer | writeable | empty | derived | -| ----- | ------ | ------------ | ------ | --------- | ----- | ------- | -| 0 | bridge_p | BridgeProgram | | | | | -| 1 | sys | SystemProgram | | | | | -| 2 | instructions | Sysvar | | | | ✅ | -| 3 | bridge_config | BridgeConfig | | ✅ | | ✅ | -| 4 | sig_status | SignatureState | | ✅ | | | -| 5 | guardian_set | GuardianSet | | | | ✅ | -| 6 | payer | Account | ✅ | | | | - -#### TransferOut - -Burns a wrapped asset `token` from `sender` on the Solana chain. - -The transfer proposal will be tracked at a new account `proposal` where VAAs will be submitted by guardians. - -This instruction needs to be preceded by a SOL Transfer instruction that transfers the fee to the BridgeConfig. -The fee can be calculated using the rules explained in the protocol documentation and `Bridge::transfer_fee()`. - -Parameters: - -| Index | Name | Type | signer | writeable | empty | derived | -| ----- | -------- | ------------------- | ------ | --------- | ----- | ------- | -| 0 | bridge_p | BridgeProgram | | | | | -| 1 | sys | SystemProgram | | | | | -| 2 | token_program | SplToken | | | | | -| 3 | rent | Sysvar | | | | ✅ | -| 4 | clock | Sysvar | | | | ✅ | -| 5 | instructions | Sysvar | | | | ✅ | -| 6 | token_account | TokenAccount | | ✅ | | | -| 7 | bridge | BridgeConfig | | | | | -| 8 | proposal | TransferOutProposal | | ✅ | ✅ | ✅ | -| 9 | token | WrappedAsset | | ✅ | | ✅ | -| 10 | payer | Account | ✅ | | | | - -#### TransferOutNative - -Locks a Solana native token (spl-token) `token` from `sender` on the Solana chain by transferring it to the -`custody_account`. - -The transfer proposal will be tracked at a new account `proposal` where a VAA will be submitted by guardians. - -This instruction needs to be preceded by a SOL Transfer instruction that transfers the fee to the BridgeConfig. -The fee can be calculated using the rules explained in the protocol documentation and `Bridge::transfer_fee()`. - -| Index | Name | Type | signer | writeable | empty | derived | -| ----- | --------------- | ------------------- | ------ | --------- | ----- | ------- | -| 0 | bridge_p | BridgeProgram | | | | | -| 1 | sys | SystemProgram | | | | | -| 2 | token_program | SplToken | | | | | -| 3 | rent | Sysvar | | | | ✅ | -| 4 | clock | Sysvar | | | | ✅ | -| 5 | instructions | Sysvar | | | | ✅ | -| 6 | token_account | TokenAccount | | ✅ | | | -| 7 | bridge | BridgeConfig | | | | | -| 8 | proposal | TransferOutProposal | | ✅ | ✅ | ✅ | -| 9 | token | Mint | | ✅ | | | -| 10 | payer | Account | ✅ | | | | -| 11 | custody_account | TokenAccount | | ✅ | opt | ✅ | - -#### EvictTransferOut - -Deletes a `proposal` after the `VAA_EXPIRATION_TIME` to free up space on chain. This returns the rent to `guardian`. - -| Index | Name | Type | signer | writeable | empty | derived | -| ----- | -------- | ------------------- | ------ | --------- | ----- | ------- | -| 0 | bridge_p | BridgeProgram | | | | | -| 1 | guardian | Account | ✅ | | | | -| 2 | clock | Sysvar | | | | ✅ | -| 3 | bridge | BridgeConfig | | | | | -| 4 | proposal | TransferOutProposal | | ✅ | | ✅ | - -#### EvictClaimedVAA - -Deletes a `ClaimedVAA` after the `VAA_EXPIRATION_TIME` to free up space on chain. This returns the rent to `guardian`. - -| Index | Name | Type | signer | writeable | empty | derived | -| ----- | -------- | ------------------- | ------ | --------- | ----- | ------- | -| 0 | bridge_p | BridgeProgram | | | | | -| 1 | guardian | Account | ✅ | | | | -| 2 | clock | Sysvar | | | | ✅ | -| 3 | bridge | BridgeConfig | | | | | -| 4 | claim | ClaimedVAA | | ✅ | | ✅ | - -#### SubmitVAA - -Submits a VAA signed by the guardians to perform an action. - -The required accounts depend on the `action` of the VAA: - -All require: - -| Index | Name | Type | signer | writeable | empty | derived | -| ----- | ------------ | ------------ | ------ | --------- | ----- | ------- | -| 0 | bridge_p | BridgeProgram | | | | | -| 1 | sys | SystemProgram | | | | | -| 2 | rent | Sysvar | | | | ✅ | -| 3 | clock | Sysvar | | | | ✅ | -| 4 | bridge | BridgeConfig | | ✅ | | | -| 5 | guardian_set | GuardianSet | | | | | -| 6 | claim | ExecutedVAA | | ✅ | ✅ | ✅ | -| 7 | sig_info | SigState | | ✅ | ✅ | | -| 8 | payer | Account | ✅ | | | | - -followed by: - -##### Guardian set update - -| Index | Name | Type | signer | writeable | empty | derived | -| ----- | ------------ | ------------------- | ------ | --------- | ----- | ------- | -| 9 | guardian_set_new | GuardianSet | | ✅ | ✅ | ✅ | - -##### Contract upgrade - -| Index | Name | Type | signer | writeable | empty | derived | -| ----- | ------------------ | ----------------- | ------ | --------- | ----- | ------- | -| 9 | new_contract | Account | | | ✅ | | -| 10 | program_data | Account | | ✅ | ✅ | ✅ | -| 11 | upgradeable_loader | UpgradeableLoader | | | | | - -##### Transfer: Ethereum (native) -> Solana (wrapped) - -| Index | Name | Type | signer | writeable | empty | derived | -| ----- | ------------ | ------------ | ------ | --------- | ----- | ------- | -| 9 | token_program | SplToken | | | | | -| 10 | token | WrappedAsset | | | | ✅ | -| 11 | destination | TokenAccount | | ✅ | | | -| 12 | wrapped_meta | WrappedMeta | | ✅ | opt | ✅ | - -##### Transfer: Ethereum (wrapped) -> Solana (native) - -| Index | Name | Type | signer | writeable | empty | derived | -| ----- | ------------ | ------------ | ------ | --------- | ----- | ------- | -| 9 | token_program | SplToken | | | | | -| 10 | token | Mint | | | | ✅ | -| 11 | destination | TokenAccount | | ✅ | opt | | -| 12 | custody_src | TokenAccount | | ✅ | | ✅ | - -##### Transfer: Solana (any) -> Ethereum (any) - -| Index | Name | Type | signer | writeable | empty | derived | -| ----- | ------------ | ------------------- | ------ | --------- | ----- | ------- | -| 9 | out_proposal | TransferOutProposal | | ✅ | | ✅ | - -## Accounts - -The following types of accounts are owned by creators of bridges: - -#### _BridgeConfig_ Account - -This account tracks the configuration of the transfer bridge. - -| Parameter | Description | -| ------------------ | -------------------------------------------------------------------------------------------------------- | -| VAA_EXPIRATION_TIME | Period for how long a VAA is valid. This exists to guarantee data availability and prevent replays | -| GUARDIAN_SET_INDEX | Index of the current active guardian set //TODO do we need to track this if the VAA contains the index? | - -## Program Accounts - -The program own the following types of accounts: - -#### _ClaimedVAA_ Account - -> Seed derivation: `claim || || ` -> -> **bridge**: Pubkey of the bridge -> -> **hash**: signing hash of the VAA - -This account is created when a VAA is executed/consumed on Solana (i.e. not when a TransferOutProposal is approved). -It tracks a used VAA to protect from replay attacks where a VAA is executed multiple times. This account stays active -until the `VAA_EXPIRATION_TIME` has passed and can then be evicted using `IEvictClaimedVAA`. - -#### _GuardianSet_ Account - -> Seed derivation: `guardian || || ` -> -> **bridge**: Pubkey of the bridge -> -> **index**: Index of the guardian set - -This account is created when a new guardian set is set. It tracks the public key hash, creation time and expiration time of -this set. -The expiration time is set when this guardian set is abandoned. When a switchover happens, the guardian-issued VAAs will -still be valid until the expiration time. - -#### _TransferOutProposal_ Account - -> Seed derivation: `transfer || || || || || || || ` -> -> **bridge**: Pubkey of the bridge -> -> **asset_chain**: CHAIN_ID of the native chain of this asset -> -> **asset**: address of the asset -> -> **target_chain**: ChainID of the recipient -> -> **target_address**: address of the recipient -> -> **sender**: pubkey of the sender -> -> **nonce**: nonce of the transfer - -This account is created when a user wants to lock tokens to transfer them to a foreign chain using the `ITransferOut` -instruction. - -It is used to signal a pending transfer to a foreign chain and will also store the respective VAA provided using -`ISubmitVAA`. - -Once the VAA has been published this TransferOut is considered completed and can be evicted using `EvictTransferOut` -after `VAA_EXPIRATION_TIME` has passed. - -#### _WrappedAsset_ Mint - -> Seed derivation: `wrapped || || || ` -> -> **bridge**: Pubkey of the bridge -> -> **chain**: CHAIN_ID of the native chain of this asset -> -> **asset**: address of the asset on the foreign chain - -This account is an instance of `spl-token/Mint` tracks a wrapped asset on the Solana chain. - -#### _WrappedAssetMeta_ Mint - -> Seed derivation: `meta || || ` -> -> **bridge**: Pubkey of the bridge -> -> **wrapped**: address of the wrapped asset - -This account tracks the metadata about a wrapped asset to allow reverse lookups. - -#### _Custody_ TokenAccount - -> Seed derivation: `custody || || ` -> -> **bridge**: Pubkey of the bridge -> -> **asset**: address of the asset mint on the native chain - -This account is an instance of `spl-token/TokenAccount` and holds spl tokens in custody that have been transferred to a -foreign chain.