From d1cfbdf46cdb949b06afc8626e55e3333334e18a Mon Sep 17 00:00:00 2001 From: bruce-riley <96066700+bruce-riley@users.noreply.github.com> Date: Tue, 18 Jul 2023 07:17:27 -0500 Subject: [PATCH 01/23] Node: Base deployment (#3213) * Node: Base deployment Change-Id: I9ccd5cbc6c44d1891acb9d443e9e2b29b25afa3c * Add governance VAAs Change-Id: I426107fd7ad2e6bef063b8f178c1683a6590a54e * Update contract addresses Change-Id: I1cf92bb08d1c45b22dec195e2d076e7aabf3e369 * Update URL in repair script * Add baseRPC to devnet/node.yaml --- .github/CODEOWNERS | 1 + deployments/mainnet/guardianSetVAAs.csv | 3 ++ deployments/mainnet/nftBridgeVAAs.csv | 16 ++++++++++ deployments/mainnet/tokenBridgeVAAs.csv | 24 +++++++++++++++ devnet/node.yaml | 2 ++ ethereum/.env.base.mainnet | 16 ++++++++++ ethereum/truffle-config.js | 12 +++++++- node/cmd/guardiand/node.go | 38 ++++++++++-------------- node/hack/repair_eth/repair_eth.go | 2 ++ node/pkg/governor/mainnet_chains.go | 1 + node/pkg/governor/mainnet_tokens_test.go | 2 +- node/pkg/governor/manual_tokens.go | 1 + sdk/js/src/utils/consts.ts | 6 ++-- sdk/mainnet_consts.go | 2 ++ 14 files changed, 98 insertions(+), 28 deletions(-) create mode 100644 deployments/mainnet/guardianSetVAAs.csv create mode 100644 deployments/mainnet/nftBridgeVAAs.csv create mode 100644 deployments/mainnet/tokenBridgeVAAs.csv create mode 100644 ethereum/.env.base.mainnet diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1ffa6d0ae0..9eb045499d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -37,6 +37,7 @@ /testing/ @a5-pickle @evan-gray /wormchain/contracts/tools/ @evan-gray @kev1n-peters @panoel /wormchain/ts-sdk/ @evan-gray @kev1n-peters @panoel +/deployments @evan-gray @kev1n-peters @panoel @bruce-riley # Guardiand node diff --git a/deployments/mainnet/guardianSetVAAs.csv b/deployments/mainnet/guardianSetVAAs.csv new file mode 100644 index 0000000000..532320e447 --- /dev/null +++ b/deployments/mainnet/guardianSetVAAs.csv @@ -0,0 +1,3 @@ +gs1,010000000001007ac31b282c2aeeeb37f3385ee0de5f8e421d30b9e5ae8ba3d4375c1c77a86e77159bb697d9c456d6f8c02d22a94b1279b65b0d6a9957e7d3857423845ac758e300610ac1d2000000030001000000000000000000000000000000000000000000000000000000000000000400000000000005390000000000000000000000000000000000000000000000000000000000436f7265020000000000011358cc3ae5c097b213ce3c81979e1b9f9570746aa5ff6cb952589bde862c25ef4392132fb9d4a42157114de8460193bdf3a2fcf81f86a09765f4762fd1107a0086b32d7a0977926a205131d8731d39cbeb8c82b2fd82faed2711d59af0f2499d16e726f6b211b39756c042441be6d8650b69b54ebe715e234354ce5b4d348fb74b958e8966e2ec3dbd4958a7cdeb5f7389fa26941519f0863349c223b73a6ddee774a3bf913953d695260d88bc1aa25a4eee363ef0000ac0076727b35fbea2dac28fee5ccb0fea768eaf45ced136b9d9e24903464ae889f5c8a723fc14f93124b7c738843cbb89e864c862c38cddcccf95d2cc37a4dc036a8d232b48f62cdd4731412f4890da798f6896a3331f64b48c12d1d57fd9cbe7081171aa1be1d36cafe3867910f99c09e347899c19c38192b6e7387ccd768277c17dab1b7a5027c0b3cf178e21ad2e77ae06711549cfbb1f9c7a9d8096e85e1487f35515d02a92753504a8d75471b9f49edb6fbebc898f403e4773e95feb15e80c9a99c8348d +gs2,01000000010d0012e6b39c6da90c5dfd3c228edbb78c7a4c97c488ff8a346d161a91db067e51d638c17216f368aa9bdf4836b8645a98018ca67d2fec87d769cabfdf2406bf790a0002ef42b288091a670ef3556596f4f47323717882881eaf38e03345078d07a156f312b785b64dae6e9a87e3d32872f59cb1931f728cecf511762981baf48303668f0103cef2616b84c4e511ff03329e0853f1bd7ee9ac5ba71d70a4d76108bddf94f69c2a8a84e4ee94065e8003c334e899184943634e12043d0dda78d93996da073d190104e76d166b9dac98f602107cc4b44ac82868faf00b63df7d24f177aa391e050902413b71046434e67c770b19aecdf7fce1d1435ea0be7262e3e4c18f50ddc8175c0105d9450e8216d741e0206a50f93b750a47e0a258b80eb8fed1314cc300b3d905092de25cd36d366097b7103ae2d184121329ba3aa2d7c6cc53273f11af14798110010687477c8deec89d36a23e7948feb074df95362fc8dcbd8ae910ac556a1dee1e755c56b9db5d710c940938ed79bc1895a3646523a58bc55f475a23435a373ecfdd0107fb06734864f79def4e192497362513171530daea81f07fbb9f698afe7e66c6d44db21323144f2657d4a5386a954bb94eef9f64148c33aef6e477eafa2c5c984c01088769e82216310d1827d9bd48645ec23e90de4ef8a8de99e2d351d1df318608566248d80cdc83bdcac382b3c30c670352be87f9069aab5037d0b747208eae9c650109e9796497ff9106d0d1c62e184d83716282870cef61a1ee13d6fc485b521adcce255c96f7d1bca8d8e7e7d454b65783a830bddc9d94092091a268d311ecd84c26010c468c9fb6d41026841ff9f8d7368fa309d4dbea3ea4bbd2feccf94a92cc8a20a226338a8e2126cd16f70eaf15b4fc9be2c3fa19def14e071956a605e9d1ac4162010e23fcb6bd445b7c25afb722250c1acbc061ed964ba9de1326609ae012acdfb96942b2a102a2de99ab96327859a34a2b49a767dbdb62e0a1fb26af60fe44fd496a00106bb0bac77ac68b347645f2fb1ad789ea9bd76fb9b2324f25ae06f97e65246f142df717f662e73948317182c62ce87d79c73def0dba12e5242dfc038382812cfe00126da03c5e56cb15aeeceadc1e17a45753ab4dc0ec7bf6a75ca03143ed4a294f6f61bc3f478a457833e43084ecd7c985bf2f55a55f168aac0e030fc49e845e497101626e9d9a5d9e343f00010000000000000000000000000000000000000000000000000000000000000004c1759167c43f501c2000000000000000000000000000000000000000000000000000000000436f7265020000000000021358cc3ae5c097b213ce3c81979e1b9f9570746aa5ff6cb952589bde862c25ef4392132fb9d4a42157114de8460193bdf3a2fcf81f86a09765f4762fd1107a0086b32d7a0977926a205131d8731d39cbeb8c82b2fd82faed2711d59af0f2499d16e726f6b211b39756c042441be6d8650b69b54ebe715e234354ce5b4d348fb74b958e8966e2ec3dbd4958a7cd66b9590e1c41e0b226937bf9217d1d67fd4e91f574a3bf913953d695260d88bc1aa25a4eee363ef0000ac0076727b35fbea2dac28fee5ccb0fea768eaf45ced136b9d9e24903464ae889f5c8a723fc14f93124b7c738843cbb89e864c862c38cddcccf95d2cc37a4dc036a8d232b48f62cdd4731412f4890da798f6896a3331f64b48c12d1d57fd9cbe7081171aa1be1d36cafe3867910f99c09e347899c19c38192b6e7387ccd768277c17dab1b7a5027c0b3cf178e21ad2e77ae06711549cfbb1f9c7a9d8096e85e1487f35515d02a92753504a8d75471b9f49edb6fbebc898f403e4773e95feb15e80c9a99c8348d +gs3,01000000020d00ce45474d9e1b1e7790a2d210871e195db53a70ffd6f237cfe70e2686a32859ac43c84a332267a8ef66f59719cf91cc8df0101fd7c36aa1878d5139241660edc0010375cc906156ae530786661c0cd9aef444747bc3d8d5aa84cac6a6d2933d4e1a031cffa30383d4af8131e929d9f203f460b07309a647d6cd32ab1cc7724089392c000452305156cfc90343128f97e499311b5cae174f488ff22fbc09591991a0a73d8e6af3afb8a5968441d3ab8437836407481739e9850ad5c95e6acfcc871e951bc30105a7956eefc23e7c945a1966d5ddbe9e4be376c2f54e45e3d5da88c2f8692510c7429b1ea860ae94d929bd97e84923a18187e777aa3db419813a80deb84cc8d22b00061b2a4f3d2666608e0aa96737689e3ba5793810ff3a52ff28ad57d8efb20967735dc5537a2e43ef10f583d144c12a1606542c207f5b79af08c38656d3ac40713301086b62c8e130af3411b3c0d91b5b50dcb01ed5f293963f901fc36e7b0e50114dce203373b32eb45971cef8288e5d928d0ed51cd86e2a3006b0af6a65c396c009080009e93ab4d2c8228901a5f4525934000b2c26d1dc679a05e47fdf0ff3231d98fbc207103159ff4116df2832eea69b38275283434e6cd4a4af04d25fa7a82990b707010aa643f4cf615dfff06ffd65830f7f6cf6512dabc3690d5d9e210fdc712842dc2708b8b2c22e224c99280cd25e5e8bfb40e3d1c55b8c41774e287c1e2c352aecfc010b89c1e85faa20a30601964ccc6a79c0ae53cfd26fb10863db37783428cd91390a163346558239db3cd9d420cfe423a0df84c84399790e2e308011b4b63e6b8015010ca31dcb564ac81a053a268d8090e72097f94f366711d0c5d13815af1ec7d47e662e2d1bde22678113d15963da100b668ba26c0c325970d07114b83c5698f46097010dc9fda39c0d592d9ed92cd22b5425cc6b37430e236f02d0d1f8a2ef45a00bde26223c0a6eb363c8b25fd3bf57234a1d9364976cefb8360e755a267cbbb674b39501108db01e444ab1003dd8b6c96f8eb77958b40ba7a85fefecf32ad00b7a47c0ae7524216262495977e09c0989dd50f280c21453d3756843608eacd17f4fdfe47600001261025228ef5af837cb060bcd986fcfa84ccef75b3fa100468cfd24e7fadf99163938f3b841a33496c2706d0208faab088bd155b2e20fd74c625bb1cc8c43677a0163c53c409e0c5dfa000100000000000000000000000000000000000000000000000000000000000000046c5a054d7833d1e42000000000000000000000000000000000000000000000000000000000436f7265020000000000031358cc3ae5c097b213ce3c81979e1b9f9570746aa5ff6cb952589bde862c25ef4392132fb9d4a42157114de8460193bdf3a2fcf81f86a09765f4762fd1107a0086b32d7a0977926a205131d8731d39cbeb8c82b2fd82faed2711d59af0f2499d16e726f6b211b39756c042441be6d8650b69b54ebe715e234354ce5b4d348fb74b958e8966e2ec3dbd4958a7cd15e7caf07c4e3dc8e7c469f92c8cd88fb8005a2074a3bf913953d695260d88bc1aa25a4eee363ef0000ac0076727b35fbea2dac28fee5ccb0fea768eaf45ced136b9d9e24903464ae889f5c8a723fc14f93124b7c738843cbb89e864c862c38cddcccf95d2cc37a4dc036a8d232b48f62cdd4731412f4890da798f6896a3331f64b48c12d1d57fd9cbe7081171aa1be1d36cafe3867910f99c09e347899c19c38192b6e7387ccd768277c17dab1b7a5027c0b3cf178e21ad2e77ae06711549cfbb1f9c7a9d8096e85e1487f35515d02a92753504a8d75471b9f49edb6fbebc898f403e4773e95feb15e80c9a99c8348d diff --git a/deployments/mainnet/nftBridgeVAAs.csv b/deployments/mainnet/nftBridgeVAAs.csv new file mode 100644 index 0000000000..f9a94c409a --- /dev/null +++ b/deployments/mainnet/nftBridgeVAAs.csv @@ -0,0 +1,16 @@ +Solana (1) NFT Bridge,01000000030e003d08498edb0ec2cdcb714c3db62ac3a31f930ca86b4ecb42f53dd1181e9b75db04e3da6e1bffc79966d72797411c9808b0e96be34a912cd7b5e3192356dd5bc70101fb06d53aea390c42765952ad261defb6339fe7b4b38dd5ab7510cb034cdc5cbd27f070d32898ea315f8e87cb538e1ff1cc8b61632268b352088fa821b3145d960103eef679edeae1e95ed62f475b77af7cf90ca0df1588acdbcbca579ec997c9af40243ac4992e8549e6a09157f793422e7395122eb4137c7fdf816143adabe3db2c0005bdc4b75a34e78a110a4704cae3b3e3f2af794548539bacedb31fe89cda3fa78f38cfa88e9a9c0a1e298c9bd34d88f47dbb335350469aecf63179cb39e92281de0006520309698b61f0b2524b2e10bd8d0f48193b785c1367062b6762730be3fdf11b4dc3a31832f6b6e7baf77b537ba5535f328d16b64558e4117da6e3a441beb8cd000717d02e66eeda59d0b7aa5c3c671739b5944f0e42f41192e4c5c8ec18a8dd0ca46717f329f82c51bac1cf1e7ad7708c1e9524d80707ff9387fc8194746b7f18520108c8e17b152796dea70b9f8cda2fc97c07e7d01cdda09eb237dd318a6f82a6e07726ec7967ae1554796ce3236e74c0ecf08ad69267241a85821856e3f9b586cb69000928e5485962839a28eb265d7fc77050ad52f652b7489c12d1c7d77748f0068d5561a9c3ea57ab6df2fc3970631ed194ac429ae4b9313c3dec666c13b52a1fc90e000ae9004b26175eacf0afede4a13f7da92ec5ce6827d0a43867ae85c2ad106e30234254f7d6dc83666d0dea6393b9fe76980ac872cb176435a826b032e4a5c88ade010b737d418c571daa5b9aa70882ae8190fe1e372e6db2efcb5fc370902f2532eb7503e523945c5c1a60d6df2787daec04409ce2d4a1eec0d99ef1e7b47ff38452f9010c6b4551ebf0ac7688d0511a7c76047218a13d21f133e90996cbf4e5d6e21895bc7860d78bbefb526196408649ec139a91a133f89327b1415a4a29797e61938aca000dd3b615052730ab99fbff0bff710fcf7cd4841a98ca3becc300c0aa006d38927b54f94f1d10ef758d06cf6f81f2be19dc444deb8155dacd18f701d3ba1a6e6004011102721f4ef1805f14434b8ad0db898c90b310b4ae482dc1685c502c4a18d780d947428685320b12970a0793e6929c9e55bfe115beea0ee550eda824daeaf2836501123aaebf1634e67690ebc6e3b41e38a37d973610afcf0e36014be0340fd994d4ec75ca1a09a7b1a6a5ff40b1ce03ea71f2f824bf12a1a53d7ce7cfe3e95224edd60100000000edc0d3aa00010000000000000000000000000000000000000000000000000000000000000004d3534bf2413f13182000000000000000000000000000000000000000000000004e465442726964676501000000010def15a24423e1edd1a5ab16f557b9060303ddbab8c803d2ee48f4b78a1cfd6b +Ethereum (2) NFT Bridge,01000000030e00910b6ec94dbd932996e21cc0977fa85a48cbb0129b3b36c763a2c08e3ddd61031116c950f113eef2107d2f6dfc17b66824e430efb7e64ccbd38d432ab4ba2bee0001c12a25e692e892232c882770410b8be9383a78397750effd3239e7f7a043abd04eb4252d13d778b3f4c5198609c91600019a549c41d9742a11629209e31f50120103abdd6d661af1ba069918e738b4f25383fab7d90ff6fb18f26683f5edeaf98e8c221c783ddc947ca82369f3b0aa730758f40d90534fd2c1e9d81c5bc7bb473862000514e880f862fa62f3e03ab628be6d49fd8665e535bea84523b90dd14b67457f437bad99020466293339d639d8a6efb00b1ec319681e5c034a25939cd1cd2d8fb10006c413678a1c09384e8b84cecb7038b49ae05f245105ba83be8e6bbb5cb8a5337d217d7b11d783ddae5870da858cd2f2bc3879c3b6740d1cd5f5d06ffdd54e4747000723995b9eab8ebf634b1b1ba4233d10a0fab294923d43516720f4b727420b68ff1c9491716063eada512b150f7062fd32f280c78a7c88103c657d7856bb0ca55c0008698e27a71197458afed68ce81b231a1143c44b3efcdf86bd8b51fd0210b988ba069e51bd58953fb63da396a60ef3f5175f8f84aacac80bf655a8576232eeca0f0109d98685ac48325c430280599b07de908ba1bfbe052944e36d5f4aaf6615c60f2f2cfac50d74036116ac77a3f23f9f0c6f66c2eb5f60f1c80a92f62a8f14f7f4d8010ae650a73194ab3ba7d16566a6feefc1bdb3ce19b45314d4c063a557ac82a6764c2ff36a1caa29f173eaef1bfce319d77d6cca7e5195a7b99fa8eb02aa85789f4c000ba44d994d5e9ce519521980453268cd38529a2c39f0fd9fce141895e6f8e993fe5f087fd30dfffe9e3950a9183ca525dacfab47bc65cd7c3da5866734228def87000cb0b860885f58f78fe4a47f97c496290cc044a5aedb3c42a50ef8a34b9e8deb777249ddb1c4a66d7691dbf74c1c1dcc0ebb2f7c969c0d9ebb7f262d2fe75949e1010d240e9fdeb52b7edec1d27d29c7ecc1387f6c7acfa7d5d5702ef9b998be5172b57d793afbefceeaa06e47e9ecb6f9ad6dbe3c536d81e4eb560087a1fd3fde254501115dae2eea11cbfa28bebe1c0700eb2222d204fa04543425103e2f047ad05f8618027711359008891729daa96b6cb84eafbd11fdba34322c581ecdde9927e42c450012e06fb7d67a6bf36848d7e73a2f54b291b2af4580a12acd8e649033e6ba45b6f77eea47adc62916996946f713b874f9763151bbff671c51954dcdecf6a630f45a01000000003dea7525000100000000000000000000000000000000000000000000000000000000000000041d9d3f0241d610222000000000000000000000000000000000000000000000004e465442726964676501000000020000000000000000000000006ffd7ede62328b3af38fcd61461bbfc52f5651fe +BSC (4) NFT Bridge,01000000030e00405987b630335a7ecf909127afd49795f72e488cd2573cee526053aa4622f5150bd248bfa5abb44b6e5945e1794189db168b0752e21e63c14b4c8925253cfbac000133cc833f880adcc082eb18ec01bd2e48fb30032463bd6554f5dbe9b93d56bbef02b2c0dadabeadc6a3d7a5e0514475e5d5d84530755b9d9e2986454fec4f9fe90003234b5e02c0641ff9c67ac11346f4404c2952e0dd69c69dc0bec1e5b9b3a45e66068d9bba8e2f3b01462e2eea102c5a8aef18eb9b6956687cb9d57d8bca984fa7000582e0d5a62a551fe0657e8a4b45f82ed74ade21e26b4b8c076faa6c4e3d4ac51171116fac8ec399c7a24f453fd62fdee85e6cb915f3aa2dc9a0b434b93daf61340106a74cab721794c6e3f9b9e223debb0516f61d8a475d99c9a21d88c61d3681758062ff1b79bff355212c231824b67b40e29df57d14dc0bfbde44bb534a2de556e60007733983c61e14e7e258789d037df606966c25eb663653dfcea1b3a536d91dce80390efa7494cc7f9351625311af368bdbd941fa6630655a490e72df64e454e4900108c7d092656b0f47ef9f1281d8ccd61ca2cacd7a2b17b5d1815f6c28345a9be70b4b2426a0e6aaa72aaf21bdcb53f70f9b3be9df228e8be009b69a8c322e4d0a490009dc79d47305a0b01878e09827e8e0ee85e0b97f9083dfeeb7c31f4624ea4c30170d92fdcb7077f44119b0bdf14aa3afd36f6f8db4d9dffec93e39701c702f63c4010ab8ec4c816b7fb7c2d92cc8506290a65ae14cf7dbc834cddd387fbb8cd51d86577690991a5694de6205032720ddff9257b2bc78c975c421e706b0764d3d78d17e000b55d03d04aaaa0296dac1c2067ce3aa95827e9acbf47cf80d0ff959c91790dc5e46ac5c7bfe909a93781721914ffc09f77a04b8bb80d1a91f0e64a381a4443bf0010ca7bba1acefed88e1523c9d927e0e05f9eafeb807ed61cb867114100bd92d4c3c2b8aa65c88315b09a34e52b81960129a1800a6eacb88d1930c5aced1cdb3157c000d75ed494a4253519a67e412929e274716e55023fa3338cef49cfb19b9bf8cc90b69e2ad8a7dca37c479e9a51b37dd0a0b673039f4ccd66e015bd3b53c8221503b01112267dbb8aecf72c0562c05f4f3c8a9aa4dee9ec35729563eab8cc00cd6a40fb07e1dc7c980bb33abd719c5629f4b91797c10aed5597be1eba96442c64dacce400112a85c75988219e79ca8e94c07f4f42727a4e72050f01d11e07f9d00d30003680462cede6149744d7c31719081518dec2a7891fe630aa5d0ce57942ea05ed26c2a0000000000200b11ee000100000000000000000000000000000000000000000000000000000000000000047db62ec55a3a80b52000000000000000000000000000000000000000000000004e465442726964676501000000040000000000000000000000005a58505a96d1dbf8df91cb21b54419fc36e93fde +Polygon (5) NFT Bridge,01000000030d029870216fcecf164288c74b4b72d314d2eb085bda962b2d73a355bc0311700db9568319f707c82e77146f35a463b7508c7a796dd3f2d398fb45d8fd815460cb5b01035cb4ebc6aa6abbba96c43c1572b49cad3071b33736f098af64d2fb76fc0af77d06238ba6187d57a4d1190a0960a599bf35c82da8b7ffbbd6630fb98e00bf325f0104aae206ebdfbcecb91312782dbf8fca524097659a121b346b084f16070e9372176e323fee8a8d72104e66a19a2005e42c0b63502d1abe422d3c73aa4778e068d600054b910e01dfb4e1c7fe0a02895506128bff707152bdade01be534b4661f68715e1ba08718e423fa93ef252d3db67bf5fa925f81ba19b97387377132fd537d9777010616a60eeddb3592b8d5c98a54dfb50d352b743f40dce8968f8bc0e97e5635053e183bfc7f6ba6b6bd703f45ae287f49001acf4336509a388817580d33de2619cf01074ee3da111097f344dad15183fe1c862acc58ca2758ae3f01309c249cb4ff394a03537e9754ab56942de0b867d87e9eb750607dcd723aa2201e9e7101d6b4337900080c105df6ca7e393281acbbbd82c8c3a24a0f716023c1e91afe302776c3225b2703930181f608f726439531ce53f0f4eb65d4714f97fa5e956562baa01b0f848a0109269132fc74929307f41f62cf784dd6b1edeeb256453d374c74dc852274fce79b366b48c0d6b238739c8d92bc90b69641573c9190630c6bb5e1118f8e26879da5000c9b60496f22fd7d0e6d3576bc7964c4f7087dce1f8f447d9ff26654549a73c9054ff1c1a8c7f58e80c91b6c9815e3034d36e686782a73214dabfa08b5fbde8a02010db9b96fb23dadae69c19f520f36786651010b9d31f5555318d502d9650bdf54d51e55fb2a56ae3e634479727a8b4059ce748561a87ce766a02cd32dc5ed4d4b3f000f2f1bd5abe4336c3cd85e123e8ec2bfc96f7d6c28034c3e9b827346c93e6e9a035e6e204d5668caaee7bd5402938ab2ec92ea0d97a1c48936e2c9f3b876f38dd80010fc19c39652064b22085519a78b4a9cc0a97a1e805b3ae4234883d5810e4eff222f2f018e5a806000693162426375ec0a06f4ec2d4f1312296ed43259dfc05c740111dbd52f95a447766493202198bc4eb1c71171d807e6d00b06a6ae4e2c86663dbf3d546c614ce3889a1ed8f67383fa75f4276df16b820f8be6ea2092aa71d691700000000000e3f29ef100010000000000000000000000000000000000000000000000000000000000000004dd8feab99f206c4f2000000000000000000000000000000000000000000000004e4654427269646765010000000500000000000000000000000090bbd86a6fe93d3bc3ed6335935447e75fab7fcf +Avalanche (6) NFT Bridge,01000000030d02588d94676ecffc0477a60e85b1b91df7c4c02fbc5de18d0d34e1e17ec7030f194545b8e3b0dd199e0baddd23a54035607b81c45a43f6e2d002a983763f44c96a0003ea1621562db9a1d74263da31815c86641e57373f1b802af9d83426d40f9c54de69f4b015ad00550cf6942330d2d9f2e15573885deb5e9e7978b3c069bcdd729500049d7fdcd7d424255d21ddfc70be718a4a60b2057df859bb689efc32c5d7cc8acb2bd0f01f57668b9b9b7b11f0378f8c9a9ebe6408f7a66fe24743c46dfe981c7801055b4c552d7c55830eb59e2a091d4b84768c45a1d12fd82e9678398f9b9f18c01e196ee047247e199c0cb3984a281560ad2809641f8b1c7a820f218dc22071832e000686b74a7e716a19470ecaa2f39648a6119ba8cebfb5061baf0ffaae511a699b951c0828bd8ce060d6f1b778b2649a9f3d93c34c61f5d81d88d75df8a11f096ed70007e9195ce3afa50df5fd34b652ae41277efab03d6e58d1ac3f26e348ed87a5fd5e4b353b349530c690ed017a82d8c2697649699887605c3667421ab1e2480e52760108612a557c8e85e8fb220a437e6d1bdb084400111f8222c188f7f20c01cc172a6c78b9bada665ff865f71eb525d7a9726f0c2930643fd8f2f1b4d09f0875cb40580109ce7ddb7f9b894fae5b6334d8f8c3c6f5ba294d608f6db053314a20409c0050dc3d7c0f63b8ab27de3becf0c184911edf522e44740176f6f701eead5ac8e48dac000cb29c6167666e2595e3f1cff3db6939bef828fd733065de48e1322929e7ffb0cb0ea338ae3493aca5bc511f101dd80cea461f60e9c87757c15062c89e603dffa3010d9f51472ab79a63105c139d63bedb631ad226b6addc804dd30fd85c146a18bdd2483f0c1ee985d5cefee86a8a17144c012588029b98a9c398c0582638a555dfc4000f68e7506ce50342eaa3132c9daac585d5ac5e5c7c823c5a101fb5b65614b3632711f3c1d06cb8db6f233bbb00c3d96296118e7b10d79d0865b8cb1c5b84809dbf01100f2578cb3f3b0117a5a0c34f1dbde86711c8a1fd58f97ceb2d624d414b00f4e2243c9088f37d0bddbc6db7a196c5aa075f289b92090ee8f26654af6f220a798900112840355495766434a6349cf50a6ac852b50a94f28dd3a0966c0af239455290c61ab3a834a1ea8191cdd33e64677d07cf8afe0d3c5baf392167832556e5ec4e2000000000000d97ebe800010000000000000000000000000000000000000000000000000000000000000004bf91d2125c5819ca2000000000000000000000000000000000000000000000004e46544272696467650100000006000000000000000000000000f7b6737ca9c4e08ae573f75a97b73d7a813f5de5 +Oasis (7) NFT Bridge,01000000030e017109755fcec2eb28a842a456a558b6334878609de89cd72de685aa319ebf7fcf35c6f9d9d51c91d873c282567e2073aee2c05925dcd02d8ec2b9929de3315f110003926ac9cf97045688ced347f8cfce6082efc3cd0395b5b848046fe40101c9ff3f1c0494f4305b1968e774c40642e4c0ba8bc4241a9462171cc75306cafce810660004b36b415a53f74ed510a9dbf3fd751dd12afcda067db0c67c812ab816b456a8b12a9aef8f9b45530df9a9dee6f59ddc875ab944aa4a59ba1226944215c275da9801050c390651359be29cf3c736a9d8b21349594c3d11120415f2996d852d40d012b11977abe732c7adffb8b9e13e521849b4030ff44a7827a2ad2b8692e7470e94af0006033eec16033ea9f83a6f97847dba0e460804a2d4d164571a41a8ace2952905cf4ec7e344fd6e47377747c5c8c3e44dd360160e51b1838ecc9081158310802c9901078d06e14ddfa0c8a250a095a993ee62ea3e8403726bca204cac45c4982701e1940355799c910053f3fdef8bf387fafe36666f02c0d531a1d5fbac239692d9a1b30008e9158ddcde9e19095b0f6a5516b38d18870303262a28ff7a4bf08fadb9547acb0c46122e9bdfd84aaab0153d7ecc0cfa8c263e5c82fa9f3edd2758b7a8035daa0109147f1236bacd2e76bcfb0790edd800b07aded11a50272242c046376f8c4788303b7dc0224bb9c849196603d28c3466d63adf168098b9aec42a834bc587d07835010b4ad5d0dc002dfae2e9f688fbaf264d37d85aae0125c3672541f92cc39bef8f8f4ef6aa15696baea9867dad40e33f118fab702192edf045c1b3c34f25d6cbadb4000c45e066114d4754e014b332cd0f42ca79a1622d5a76d7c8ed1f4546ae3d4a42f36797e955c938944a4d30adf33d8e991856d55f3b00c364dac3d29a8b0294b2fc000d45f9b17b2bb38b0fb4bccd32dfd2ac5c9b72fa8b0271b621b804186413188af341b66932b893838ed3d299b918d56f5c282803412bc08588b9afbcac6bc35a8b0110abbf400ee2383b3eca236934ae791194f48a4e88fddeb2463380702f6498250c12a50c8e5443c84504a1b806e197d3cb5aae41dd9e5da662728f6d740cac18f80011886df1556c5fc54eeadefd24807af2335219d89c20b139686a5f8b69f2bb6d8e25323b826ef7517ee82b5f36473d92a1f949383fa9d829ed9a15874acc3659f600122154160bf7550a1ab804809983f09df7df894f23f8ff72bfcd1744deaf43c76117f05a10218630bf577ec6bea4e1ee3db0c64f42c6292ad4532976f6fc67fcc9010000000084488bf600010000000000000000000000000000000000000000000000000000000000000004e1042ecd769bd4712000000000000000000000000000000000000000000000004e4654427269646765010000000700000000000000000000000004952d522ff217f40b5ef3cbf659eca7b952a6c1 +Aurora (9) NFT Bridge,01000000030e010abe89b7d7ad56c3cbc76f60e6ff4ccec88b99fa5c5f43271e556807c2b22ff048087d3c9dace435c8c52933c1d94a315c6bef76becfc8e635aaafcaf0739c560002a92b1fa0046e1dd995120be864d6f43492b748988665ef716a7da764781529990f39a1868e56619a9eea650998e1f5c1cdc30221c8ff94593ad7b70b3b162d5f00038189e2d2f76540ca3e145f597e3373ba3f033f7108c3aa66a2ef4da22725d4866a9aa43ae53b086191c6d4d983a9d90c45fc369c2179e79c546ee0b3f3825e54000535dcbeddfe7ca716df09a2652d12354bda4f82ea3cd0f71e74459a95e91bcf7d608301ec671a74d3c94369f138911fc1e61f3be177df29cc27c515a7fda62b9c01065ec6b949018e8ac1e2465cb75935d8e85980357d6cbc8fd63d093023eb275cd6453638e423599fa1a63f61a49225fa6de5dfc3735bc15055b23f0c8375fc71f60107f53f7f1461573297053be4f5e96bf8677126e90b46e12b81e935c4a609edfbb83a2ee7a648951623841aefee18f5ba82768aec6076da700ef5687394733ba1830108c6cc8684293d7357f741acd13248105bc4185bf340b1cc7e0d0db17c7e9be12a310e19cd75f9fefb5d2eb6379aae93e0021251fd0e5ee064b874cc9ba4526ffb0109a169c44c7c80fb0a4172768c32529c1dafcfd45cabdbeb02f10340fb17192b28528e8a1b3b29815d8eba9a328b41fd8477a13e408f59852584aad4f91858bd13000a53d32ed287e6d37631ff0503525a34aada229692cdfff8ca5ae465ca36f2c0022abda5e6149196f10cb544c775f36034abd37fe79666ed29148316880030b2c7010d5a445659ade25e8e292a0b97e3ab1305cb0909c86f713683fc2c665a194b9dc5766eef404b158c5258f6b0d220a5e74493275b6095cc6e2f51c391a65f2da990010e07b259e831ae4c3947220369627c34e30099ace8827dd46f0934d3f7e46609f232c5a04d78ade5d380d7fe2196f12c29a73088e8558ffd52f68b8fb2ab48d1a8000f655781c7dea5353100cb9eceeae4017fe74b0cc25d7e50b92dcd9b12d769c9290625833b02d78cff001b11f415f07fdc4a11691e4e3b1bb559cf1970c13a22120110f981d94239efa1d4e214e268e8e9ee653d7505d5ac1cca5966ee69eb0270d0846d7b0a906f92dd0e9e4f47f57a2cb61caee42f44c0068ddf8f01ec0741ac6f07001121258885951134bf3aef17ff7752a38d99bf78cdea1cc8aef3e69b2abb28c1b440831b923ac4fae86ee0c1df2a0815f41dfd2ef90f5c752fef1249fbac312aa20100000000ac9e0140000100000000000000000000000000000000000000000000000000000000000000041ade1863fed2ae422000000000000000000000000000000000000000000000004e465442726964676501000000090000000000000000000000006dcc0484472523ed9cdc017f711bcbf909789284 +Fantom (10) NFT Bridge,01000000030e021ea05e929306fcab7497b7692350f126e07a4737c093fb86294adb109e16aca118aca072d206fff54a763c4564c70361f1dc758e8d2694507010a9a7548bbb8d000319a8a8b008d0bcab6710fe0dbd10c13f5df53d2315a2b32aed598be0eb00b7d44f496f5d91930ae6145bade4c9f7c76fdabd8bd859ae932db325b450c1090ba100058022ac71e0f00e6c2a5f380d860ec3bc19cbe73846fad14c24808af6fc1a94a722ed330a355bf6ca224a3f13dbbaaf48d2e3fca4fabe6d3c4b49fa694baf2c190007d676a45bb38cb38ef756a2cf615c8fa7f0f9063e9bb53e34a365583f0906dc367749ff94c5ecca978fe8cdf5f1ce632daf0834a972f43c8655d7720af7b0ca1a0009293681c86b8f9c5f5e61ab4676aabcd2ae84fb54902dbe31e85cda9052f0d7fb4fd5be80d5d0b16bf22703ce4c2618282f396390f5457180e742cef371eaada0010a9342327f5f025aafb403b08d7be2170088f303260fc50e47a5d032f7da5e9fdd478c367e5fc5466057ac84476633e8c80170b9e2df88ce9d27c4fb11bcc149fa000b5662723c5377dd452971f10a061e869cc4b92c182a1c38af3b1548c1757a47e72daaaa0ed8ffbd9bd0ea822969d9cd2b9b595301d20174da4ef968690e6dde4c000c249b9d50bbcbcbf1ccbdf02c79d916132ed62200ced011972790aa9af7e9bb750c80faed03f56953869a1e54242b718349d64e7a9896a0f93146467e554699e3010d638b8bd09783a2ae266800c84fd52350504820a16f9b34321a4e17ba27071ff318d3fd8dc1138c0cbf98ecc200cdcd9f31fa64028f96ff3eee9dbe6190c2055b000e14234c6c9e218bac1da3074214159e08783c9359bda639c64584e1db5013e7145c98be841fcf74c45b74e6ba736401435b7622d280feb7cf864a6153df255734010f5b57d33d3f3f2dd7e7f48f3d951468c4d8012f8a6b6e62a3093816f33a51e8d40e0ade8e6eaa57001cfec98f145eb2e0b5b8be7dff9612f5f7f1e0cf35e1d2990010d8bd7c78f129d0af15f300fcff2128e93a39b61f95f6859561349baf65af76947d09fb44a890fc20b3aed5477f98cfc1bd85579b0697109b6e7a28bee6a2d69500116d973ac246039476bcb17c9ba539e95df1488b5d3029a396c4aa237c13dfdb8a28384c9a7d2edf6b9af26290674c00df028fd917a183071fec33b531e6bda8580112b2c9469445cdb21ff0215a0c06946927054e929a9daa1fce3f47209707aceb3a1c0496d31f0e0760ca96ca9fd7b3a6cca8db8e6e79fe6c91b74becec53aadb0d0100000000da013a7b0001000000000000000000000000000000000000000000000000000000000000000424b358920a0e544f2000000000000000000000000000000000000000000000004e4654427269646765010000000a000000000000000000000000a9c7119abda80d4a4e0c06c8f4d8cf5893234535 +Karura (11) NFT Bridge,01000000030e03c7061816e6b876c9124ab8cbeda591ab52e126f5c4027aa936a91cce4bf95c1339fc52ceaa95127810b7cc2586aad4a4fc852b07fb6b4cbc5d46e3a66f352c01010468b1edb0caf545c97b366e133483a957865d0353dd62839974dea6063b7a1daf000a5c5aba9d4fc31d9706dbec255d9b61739f86b30cc30487c2635d7fc31e1c010566525e95418d391cc04ed1ced837be7e4227956fe3996b66f5a2275c58d912d80c463b8c2d119506ef1258e787cea671df0da9cb19761c65011af48e355ffc8b0106d1e47f342b87770ca98a6882c074880537f98e5a4f5190f0da435b58309e892f6aab0ddc4e10756298454f179536711c70d711eee04f761df7e6fbb62e1e49610007446ef58b08a09f27b961cdca17a5fb88ab18879048175a79471cefc7bc126ff037da277e83e197114e0978cb1330050d16b7b25da36e90d4b72423f67b502e5e01088d1bd14a95d746ad746c50a0050205689b468434863a203152210acaf5ed037c15545cf84583f2ad4e7c1a1a8d7dd2aeca55449ff576a39f26331441f97ccf700109044695f6a06d54c41f7e45b3d0ff808946dcbc596e0fcc964780278800a15b34569c2853ea20c1a1edd35fa3ef69190969e417dd4ab29fb074b263d79b1662f0010b5c7f1ba71e49f017c902d8d70af4fe8fc71b70fc8d6149b72f0da0bb62cf5fab6c39fe273ec99f29987e3d4b651f5bb8f63fba6c041629eca6543ef9c8bca6b0000c36ebaaa8e495ed35603838c9d8192e2a16349d4e016e992b83125bde6313b0663983d9613d775b70e76c4cb1d92a4b1ef33258fda145d6b939b2435d6260cb0e010deb34f6f6b1ee350f44f4b9d705fc4973f59d8cc79cd2511a346ee9e1d95fbcd950582491eef214ed5e198fa65b47be3efc259b9e307884643abfdc8a5ae83337010e328bfbec90f5bd3eee9b726a278213abe33b19f3ef67de87326447c785780b1505f162335a3bfe1dffb030a5c566336e28539823363612a785c65fcf7cb31e7c000fb3d89ee934ab8d61c26eac2099091edd4d243a68cfe3ac25c92e4d44634d977a0df6306b30acd16d4a1dae65febc18488f0d9ce4f9f563af9eb6da71c6b6b19601100cde4e50514ee34106def7ee7f9407954015a47d5529d1f77d50065a9e6c98d5211deaedfadf86c5bab393ab33e45c3124d9a03810825d533341cfbfcd443ffb0011fb627f3a9967dba40ca3e304a16fb5b76b30014a06bf9407f31b74b878a2b90209f014f159660d409b9f6e9e93e4c876d811f877d51f80cde0da7cd7a6f464860000000000a071934e00010000000000000000000000000000000000000000000000000000000000000004cd30c7284ddcbf352000000000000000000000000000000000000000000000004e4654427269646765010000000b000000000000000000000000b91e3638f82a1facb28690b37e3aae45d2c33808 +Acala (12) NFT Bridge,01000000030e00b8185efa3ca656b6bcd848a534cf9b1d101838b99d4e8873a0b8e61ada0ce6b70131d3e348162c3e0d052a70cad65e1c9868a13ef5ef58bf74dacf1ef2e487ef01027a4b2d311c6dabf97bec39eebb6f60404484987331d96bfcabc26cc97788e65c7bc332fb582114f4e580ebbac753ebbd53a9709bd64c4ee9537313f5ac7fc8ce0003a0f6bc5aaad8aa52159c5c026e8dbf933dceb10b7a54fd49deec0f5816901606048296e8f7e65d84f50671df526baa43c2c9da3e4450ffe927d2adbd4fd54dde00042253e18ab1f0bf3203dad87a4b5947dd9eef93e96f8318c781c3e51fe643b0c74ae4499f01e3bec4f0852ff66c17fbf3f176efb7180db4c48a20061d2679929e00057f2290c34d9f3fc3c0c4a8a86928f36c3d615f5314323c7a50ae30098c4007b7066581528571f7d562e84f519d455ee9d638ba82127609021932a52c648afd4400075fda65c2852f30129a69dd7ea104b5e2d360effa367391a8dfb3dcce418fcc680428d4123bcf9672dcb1ceb17c56adc41f68de8181d829dd80f27b463c51c68b0008d65944ac6b17ee1e9e7dec3d6ee1e52fdcbaf16bae508054251d5ec16309458714210a9d59144c5a425c92c1f19810bb5aaea0cf75b808430a91f76444ebd52e0009864557225536b7503959343898e25ba2541657b4004d13368729f1bbc481a557725f20e3a62dec6eea56de9d64fc075627c40d74436d8fd9c5f752e047afdfb3000cc139742d82e6c8f1e28d9b0807683e0758e83c1895078d646b47eb48e3fc11c5345ee4e3e01935ca5c405ea231977cc41efddfc2641b6051d152a6baf2d7afe0010d86313d890822fe11e07f6d58055813786a68a48f0507deca3f64fbe92ca693a71938bdc62d1a6621a28e0ced2a8865f706e121c5b5d06b62426b48a8ae02e982010ee8d59244926cbc0e21465872b08c1b0fb21653c653b0006d4ce0a26467a6d88a067afdcc30944e228a4110a9b818766fc5f982c29f1c6406fa4f9f159fbe313e000fcd567005e788cc703f3a77295ee8387ddf638140ee9eac42c06c489d233b47f64a69dde3203f5b2547f34d290af53c76e13ca5cba24bc2f58cbcaa99fe52d8a60010bfde55f8c4f57e53934a18d334d226a070b1e632ba6b189b6d57a20dd85fa009020e43da1d872d0a20a6542c8f488ced1ca9965aa947823c0152383ace1144c60111e8464893e1772792b1c044360cbe147a582433d8960ec10950e2f48b9e9aa5e01efe162ac09dd480ed7e0a57bcfd6290d018f243e862c8ef7cc6b40e8eadf66a0000000000315e8444000100000000000000000000000000000000000000000000000000000000000000049892643a64f708782000000000000000000000000000000000000000000000004e4654427269646765010000000c000000000000000000000000b91e3638f82a1facb28690b37e3aae45d2c33808 +Klaytn (13) NFT Bridge,01000000030d02160252d67f3152f4974924ce685dde722ebc39407be0e8d4ca91b5e1d50649aa3caf1323e8e68ba2708acaa1952c39b8fbcf497f9df843162d65e26333e0a2a60003526e3417a725aef59f91561b02ac6e2a753fc25772d7fb7791d41280720c31c1238b4ffa1dc12ce76644d3f79fd6be4020da4a094eaa5e2fbf606d73a4a37cf70104bdbb28ab63cd115c4437b429f0b6040dcfd7889150f4e545a3db3b499de8f66a2345a78a701a029531a1da5259f046b534e2a8af51c2bf87ebd609fcd4f6e1d801059fed2e15fa196fb78750833a189c9807c0952623e3b84e469a9812652a5756462a0dcfa234e72288b4a184b3950dac04c9c541511b0f1d6e5801a074bcaea9c60007ba524642c2143d547c8d0ef0e94333cf6aa815a0dcb73022865a227f2b1e75184abb4d60f742612ccc98bca03c30e8b72404468b45fb7234ad682b3a19e12fed010832772a5bfead1b5f0b08cde45b940b9aa73769cd932283a6ea036538b5e37a36715f536d2c89a9c2512d6764b2f236124ec9a3bfa2035a29cc55e87b0e15f29301096dfcfe1f685158af0455c1ad8558c6b697acc23edb45e57ac180e157ee4442ef53fa2499268bf0e0e99bc4ef2a717abbe32c4652709cd6b10c8653ae0b4b8dc7010bfa0250eaa7825cdb38bd22cdad750c8aa6410bede7f176bc66918f79966a4de34f28bd2841c7409cc49c7d67ac4caeef85d8318340f0944ad9c0ca8460b4f1ad000dcd7630dcd3de61a20c68816aea237b8292eb8a5a3f7d3bb66739dbd7b9ef61fd3eac57c5cfaeb55fcd01c10c88b35aaa70eba20e4b21be673cb5355f53856cef010e9296f3fbfdbffa8c239a3ebf87c05e92963885fe257578576ffcf61fd07c63676cbab741d57d8aa509b335ba649d1282648c2eac5eae627d9581c179f8923d5f010fb9ca88e4e36e91998e4dfd2d83675f6f568e681feef4c87b07250fa7338d9fef53dddb545313d57effa86f20f599bfb7dc0100dce7f732b98a58691f399b3ee9001042a89834f920522fb6ecf26f4c79a1ca323f300df36d898f5b3418f4e08b08884e084262f5da2baa1b39097c4bb608cf429ec004e5d2da813c7b62b7e4e8d3620012ee91f25c4e34a82ad5af908696d96fab14cd7abb0ad9389493b875cdca37ecfd520bd4b09a03e36351500bc42450fcf21ef8f5e45de7c2c0c8310780fcaf6090010000000004c8a56000010000000000000000000000000000000000000000000000000000000000000004707148cf1a4bd4142000000000000000000000000000000000000000000000004e4654427269646765010000000d0000000000000000000000003c3c561757baa0b78c5c025cdeaa4ee24c1dffef +Celo (14) NFT Bridge,01000000030d0263d5c79a244b703002adc5b73a199269ee18420a8351819951fc45c0041204f00281522ddec96ef14c164127942d84cb2ac27e2c35a6d146a2c7e4f66d71d5ba0103f73bd61082d6c8f7f61876a638fd26203470c1802429f7bca017705adfb657124c19f33fcdcea486cbfc46553559a42d87fc7c3bf33526e3d6dadd604cd848940104c0c47b9bd3c5d513eede16acc63f49d6e126be570bb9390d8f15fa4a2ecb9d1b68289a4e457aff12d89bba3424281f27d8c54c3b87445ca138bf5b06d6bb057f0105bfddf55ed379da0d4242cad8b7b4dbfc3eae689eb69e0f4a282e9f65f3b98aec13db06b428b8d89bdfecec0a716b299308070cd8c16cf07b8e569997332e722201070635e9a5258b23e50e757f960aa415dc76d0c6faf0b095a67b6d178947cd74d834773cc4ba817f544bf130785889a709a8dba0cbe83a8e99c288829ab416200900083478d472ebdea027ac7c43d1b8be020db7071db4a955c605d91cbb8ac20ef87434a6df7ff7a6ce4027163406d79311f820c2a60fb9d20ee40c05e2157177ea5201097c8ca8d53a01d1a047c80ab5ceac581aa25c694004958ac497802fddf59869c2488154a18ecd202ce0ad13fd57e04b49eea45e889b9c1136da06c7adffe59937010bc93f245408429d0cae6acb9a0cf60c1d501199f5d2db077fc85b477b9bd6e2c836a0159fce5cb3c8648decf25136db0f7322edf49da5fad757c866304917db0b000d4fbc2a6afe5f60ad77f02d83a13311e13717e75a4c57ec872d02c80fba1354264576b539abad11594cf0e1600b8aac1324ebad040397d4e6d6221a384a9275e0010ece16fc3cea71163b1d499a31100f3be89511c0c8b96d1452d0d4ff4444d9c27f754cf54378495537f9db7f27ab25153737bbcfcaf191a0d674dca1d2364d5353010feb0a6f8f7d08ef3dbbe5add9f6cb61da2b62d886d166a59f9304c6a576c97ab933957aae2655a9b8b0279c052c65acbc74a36ce4e9a5df5095c073c6bbfdbb3501104ae4ac221ec11478f8a51d0e2410d0952f3eaa9757bd407ebf18f13714bc15de11afdfbfa2ecc6c84acb54cacfaee08d3fd60e3baf66b018c13182015a0ce2ce011241ca5045f222bd567ccd345fefe4940554ad942473caa5ff38f1b83db450c9403fae89759e3f84df4f828b624e270fb5aae231c57406c29995958f4b7a083ce600000000004e8a8800000100000000000000000000000000000000000000000000000000000000000000046974fbbfb48cd7c72000000000000000000000000000000000000000000000004e4654427269646765010000000e000000000000000000000000a6a377d75ca5c9052c9a77ed1e865cc25bd97bf3 +Moonbeam (16) NFT Bridge,01000000030d027c278a867a1fccaad86abda98823e88f72beb5d24443f73e54aa54a18d940b910c43e937f301f71631977fc372ec0c16e81f52f5bb68999ec76a9888a46b82df0103475264d19fda61894e6733818703882b60574472b58fadf06f57a7898bf0a1ac19be8da74b552e99a70a00f1d384c45ce6c860bf5595b5a8d3f2a3c8213d2ec90004c1f29bed4587eab2e79dcebd510b952565a3fa7724f3e9d418232c03f30723a96c46589c5c2520a4a8099f4a9f681be5937dd97e41ee89aa47c4af802f3153b90105c21f38d9e5ffdb145e31cc5986542294528a1d904b037c733cf689f57a108b6d27e7f0acd5936b39bba443b9924effc4cfa1c8f79926d632f080c463c6092acb01069d5ea984697349505dd3b7d6cee7450deb957fbaf5c399ba37a4b2813e26728e08cc0182b02b2d283d9b7ea0e655b3e377f09446de9a57c47f0aa393cb22f50101075fa7fae6f405c08b876711341a96f391bbceb99c158f716d4545ded8cf2a4da51ec8de6ee8f70cf0b59a3efb9bb424308041974ced4f338e41c50b055b822686010847b3db69dc6f0f0e9ed638800069f036902cc1bbab4644bc0bade650451619eb438675e5aae57777747315b315d3c484cb2789d942b98bc299ad2b6de89263d00109cfb0a27732f55aa5052dd585bf94b0f26b961e76d39a3b9d11ee3207b9b73eb81789286b01fc82f87a07efd03761f9cd7602371d80f2afba0fb8768b6e596e7b000c338f9035c27a0cff17be1890dab4dafe92580385238db217683cafc30c9ff1e96f3dca3cd9eb8824ae92be655759334a7985b0f8f8afc9e672b023ff51c316ba000d0c5f5a36ba8128f14a6faa8b9de50ecd542a02ac5050b1efb9dd405f6fc67a7c07f271f7131578b1b7e9a4bee01b45adda565d1490a985f9b610e018e7222167010f4c6873e8cae7a937d50dcb14f99117bee6e3b2876ea877a19212d1dfbd4789e46a6185c4f854ea3e175469a9b369d5a5d129296fc3d41f55135eabc7f11549c401106c97984735b6754e0c40603525c188a1fefc4f07715d3e5c9fcc42bda40bccf11e673815e7515d56c69c4dcedcea09fff1c3ca58d57bbde8a27dfaa7ad77f96d0011da29584ca47e223d3068d91101068f96651aa0e1b8bcebb2c016960b58fbb979396f31b4f9a1a1201f7cc44fd88ec25dd739111510f9e5e4548172d6e4a081fe0100000000bf64a8c200010000000000000000000000000000000000000000000000000000000000000004b09124788a3e42902000000000000000000000000000000000000000000000004e46544272696467650100000010000000000000000000000000453cfbe096c0f8d763e8c5f24b441097d577bde2 +Aptos (22) NFT Bridge,01000000030e00b8f342f2ff02181898963d527dcf9be957bffc2ac99e5a2843c384d927e4eff443e37df202e58723c8b5089365b323e32440c56510d4bb31d7ba80d0209b678a0002a2f5059713c7f4296a8d68ffdfdd069ce5298da796b25e8a055f93e79bd70fdb5399ee82bf1ef0feea0ce574139a01d85c167ac4ddd334bcfa4bd2a863d28ffd00031477fbb8cb8574acb482efac8464b55f788b3acbea52e5c4705b9d0fc561b1cc19a4113e649f574823c03f56d309e824dde34ce91a9808e5214b2b220f09085201042819a071eb387495e7e57e790621ebe00057b5657004becbffb5164981bcc8f9470183439f62b9f7da7f3b09d1e00742fdb5601e84327fd969f22973eaccd2a9010562ec32ea5e4c327e69444c6553befa6de6453311c97d4a01e82dfd63189e6f73581af86f622708ceb563d63b05666b09588e4233d2edd6c1dc39742e4943dc040107bcc05555fc96d4966ad4c8387768ec312d14ad724d62f3206879029fd6415c3a402e0634149e0a12544cb068ae660def87a1d2e4f7e23e3ea422ea386c2fd5d5010809154249651a415705658d5f5d7f6049fa23c1f002bf00e3e4d49d51c597fa8b7ab0f5146a226d56a36b95efac59db27e4ee0741c6014fe55004e4a234f6842a01097e289f8bfbbfa29c9c4fc4f3c0d2b64e8bed9921dabc7362a6c125093a41af8a1daad45cb8b0eb18d3f6684368812f7a904b3203081a7b20aa26a7b4726dd483000a47d5067e1955034d732722120e5bfd074a1142bc7619c98d5b9cf37a856c92401618bda2b3c0e2d7a9eced68e4867c83b96bed9d3630c2b276e714be65f1cb39000c24c594eec0802315e67b7074954c0b4a050dc24e5b09741d15154bbe075aec8c015c86968ced954ada7fa9799783f495302512dc5c45a38c6ce778f0b6da73c8000d00db3542acac0b5dec0f63dcefd3d7411dc2c7e48feeb7efcfd28ff2172f39d139811ad02184376819a5b2c582cfb6b92d414af31e04526f02568eb6a49027b1001013d822a17d5af2021e6f7075508749478cf3b081ecfeb698ce4f679cf695444c55818d9e8c003a05674bda642f119f0483e9d2552e6b833fe4a4baef056c687800111b8900571c773f2dd6b11f0ca728d6f52489239a9aec11f47504b3935035b1806703d9ae7857b62f891587c5c9610a16ddbfb245c1e43f8841db5210ec2437eb011266f914c5b1fe8af0bdba9e4c8060453d951d8107bc78764f3eec110799c7b75a1299d51c80e155464f0cc490272938f182cf51bededb7b26fea96d00b1e54011010000000054d9b29b00010000000000000000000000000000000000000000000000000000000000000004d498b724f89a5faa2000000000000000000000000000000000000000000000004e465442726964676501000000160000000000000000000000000000000000000000000000000000000000000005 +Arbitrum (23) NFT Bridge,01000000030e008bde59c741a9c84df6e5e7306a63d21bf5d3fcf83a081be4d8bbd5c355b37b4f597852bd173eec2f67d78e3993335e70e9e4900cccc3d9d89d3656fb17f1346d010304208fdc8353c494ef9614a90bda9a7a1dde7646317b9ebb607840db37aad52309168433dc688fad3980728c46d380d2aeaadc827bbf991c2051aaceb0b8074301049e66babe85d4ca61acbf0afe42647f7a3c23abd95f5a28a8e914d2805247ecde2fbda69d1db282c5ed517e40191b7a77859baf4c9d18269015bc2bf213a616d90005d9168152262d4062d02a4791e7158c128a85d5cc438ec80a5726a3d4d5c600ef61900b10a1d89b66d2d14a9897d2e3e80ff37e2a5ba99d5b0738ba7c470f908c000652c13a2267f139ee1c71841568433bbc5447e21ad41cdb5a2e2e39e668b024b9708f7f1e17b4a89dea05410e57dedf32ae301d05534d8a783bfea31de826f76d00070ec67d3bd18b94402553e1993cdfd0a3f80e1e3043967283d8f87a95bb0b877e09040fdaafdbb93555ba23a347f05771c27918732acf2f5a7736cada13097bb4000894f73504ca75547727ed276d8c9e52365ac4b4f56bf43e85c1b8490d6a1bc6f6319c70ae2fa519dce95e2abe0126a2a8a2f298b1d9f44449dec5bc492e8a756500091991a74bb229aca9087e313f91af986c4d0ab8d039f2273c27b663277fcb47f308b931816d1f1390060c03482234f1a706b7f6a83746223ba4f7b306a101fc98010b75f1019131f0030f6f56eb8d4fcdbadf6369f488da38a001b22351fbccbe2b2b51d6e5c9b82cf29acddefd5add8b05e6c30c175bb9466da335e63be9d6d4c518010c54e7ebe13e848457624e674748013997c48d9507cbcc12173c89c5b9b6df82014ec99ce0c3940da296bcca623181a26f715d854ad648894cfb74039fe5fd85bf000dc92b22236a1195bc83fad9ee475a32c8e18f88f956a87a160f0da08f31354d2441b1952bb7cd290a93cc55cc783451c3f020f642bf63617234f7a35dee0c4076010f32db50b935d5c3384a038aff7b9727fe41e73bc46db6ccb73996ffb362d6405966e2731e48339691c33a6dfe4a6dd3d374e88e466d80177aff223c5056423d660010078747b0fa64ff87bc34b6ed544439d1634754ab99ad49a3c649eb21567ede0c44ece6965dd55c1dff495f1ed3b70f78d6a872489a89560ad8ebf2461aacf63500121daa8ab0680206b267ffaf532ff73b1bd54b7e40e8ba0da72180cc4dc0705e6441f6a5f0f8d3498feca84c89112dac88b8edce7186fa476ba65e6a5cde9196cd0100000000cee78285000100000000000000000000000000000000000000000000000000000000000000045d30edcfd5559bf22000000000000000000000000000000000000000000000004e465442726964676501000000170000000000000000000000003dd14d553cfd986eac8e3bddf629d82073e188c8 +Optimism (24) NFT Bridge,01000000030e009d60423b92b72aec94e491b16a1856665cf65a5e3328564a55c92abe3ef564bf3153e541366eea5bee0115e3ff3e9da42b97b32dc04ee99f18d33265486a5dad0102adccb42ceb38d4a8ec6a8bc0c118520484264967e9c0bce49373fbf54d4980357b642cfb949128760f36ccb9f574afe301a363545c3236275dd4e7cb8184f329000397f9af5fb1423c6912d332de9ad9f0a144f1cfb71d40aea91fb1ba2337be50250171419d89edc1656ba45525b67abcd202343cd00df1ce965859536554dce3040104559761877722562b0a39cf7817a7dad52ea794e99ba3c202e8303144cc195ba14d305620b870ba96a132f30299a9f6d27793550bad4ab16ad8aac91c72838d7f0105658436eee5956bec76402f01582013155c0199d76193fa74f7926ed1c84c93d0224739e5aee380677d94b0adf864a1e2cdb77fb6740bc000dd2207e16421aa780107904cf4c0150ce0662916a3ba8e11186b09ce3da06636dca0788dd578ed4667975245bdf2741c93240a9709bcc168d09da04377350545b2303999650932ce7abd0008912993e689dd37b7e4c33ecfbe34c5ee7d00b2774e31608edaad9b6b77bca80f7ab94e5e136b35fd329b68d5d690df1a4a22c190989562a0411b3d9456f86c8e01092a2823aef77c0fd2c0a685ad703d637afb58554025ad0c2d5ba534896f019b98098b587b44f7449b3f98057aae69d43c31eda8af31f008947e0d9d22e4badb51010a8d5c207e4991b2781902c428cd52dad3d953ce55c2590c890b6670e16eff9a27011813b0b2c41cc7c58501ccfbcea2d8cf187b20b94ee7fa4194b1a060871530010c81261db5d93cb7673b22bf35c6b0d4f943ba7ae0f3f0ab47856dc0f93d1da0796770ec54c17c9ca9b1cb61cb3103a9f1807ccf94610d19e3f5f59177a99f467e010dba5dc0c654fd7299d4dead1ced1f950d326cab0bd1028ab71e216ec95a3aa66b555d7af768244ce3ab3105fc31378db13a981d6e5f8c640d63bb58e393ef15ba011058b3076008534cbc8b6432068e9d666375a66ce6d1bdcdea71a6854897dd7dfe40f62304d641ab467832a88a963465affcd82ad5d76cdd413bb87482f2298e6e0111f09630696430245be1ac67c2c346d0823d2ad539d6d6439874a1a7a7a81abbbf50c6228d13c896f59ef62e26b2814a35de4c7c2d07d3d28ae269bc62d444760a00121ce1f3eaa677b04b64ab07ba92d8a3fee5f4312c64ea521fca751c68f2133c0b460ee00235b4ef7d374ce3aff4d91e1c304f6c16fd94f98b74fb6b1079073e6f01000000006a2a60dd00010000000000000000000000000000000000000000000000000000000000000004cbb704b14864918a2000000000000000000000000000000000000000000000004e46544272696467650100000018000000000000000000000000fe8cd454b4a1ca468b57d79c0cc77ef5b6f64585 \ No newline at end of file diff --git a/deployments/mainnet/tokenBridgeVAAs.csv b/deployments/mainnet/tokenBridgeVAAs.csv new file mode 100644 index 0000000000..47e040ea09 --- /dev/null +++ b/deployments/mainnet/tokenBridgeVAAs.csv @@ -0,0 +1,24 @@ +Solana (1) Token Bridge,01000000030e0005d041155878a79b0c8b48aaf3a6266d85a808df5658de5c77715802ba2e38b54374a5a244b43c1a4129d31b47192cb80a565484e55f171c00df69be3107e32e0001fc342ba5227e2319c36fe7771d4626753960b5f6082770e57120b5057eb56c5066bab012670d532c259f6162f311458e187d7137298fa984a41d469df817f88f010349b730282809e94fdeb125dd30490e116ee6ebffa73296eb10a36848351a12c77e846daa5f6eeb83103a2d7325d7981fa4cae43fb84a91851c400c2573abaad30005ac798fcaddd7090a41b718f5786f02436f30d631e64ac46ccd058e87dabcf20f03dd4c7ca0e71d2bbd9c0cee90809a5cdd0bc72519abb9313a4d81763b48c79c01061cdc7c59590231f6f580c3b002ddfa5f41bd0118c67fcb4cb295ae239b74d0e322775d1e78c252eb605230e040c5af176e9ec5ab5f34dd36fcb202c894b6e4fa000701dbb19913b44fb2295a0b52e2eaa839478dc15c7d56de51e5298a9e0cdefbe22a0883f1cd8d7037f9f24e4eed1352a67fdacd30531f32021b3e349ee2a868a100087621ef01365e4d1f2e81ef9ce46f0d620cbf003ded06956df3e87a137640310037e8d605f9208a5e17719001fa662d2dcee1c500f0db1ec99119dd326140cf240109a406c0fffda03ffc741ac06743bc507d2a5bdb95eb1c6e3d24a41d99281ce9af20881add8926faec5ac33d3bf85c61a220dd0f699d713f4a453f18b2ad735b6b010a42fb4729a41b99029d06fc6395e1f676654002a28e08c073fe905e87883726b444aa70742c6c53ee8efdb004e756fae63bfe9a91d20971853cc0a8677ec1b0da000bf472a82642b1a3c7872a6594786fc803c8ed8948719f85d1ecfbf903deaba8505b02ffaa488b09e066a0d7210d8ee0871dd10d23b4249c942c64bd79a08d5f6f010c308bc162680851b2a94a888e068394bf4dcb5d5afab1faa350bb086884c20a535366302c37220db24cf6bf2de5abb904f1ffbb13afa624f0d413049b1cb034bb010df076e04e691e924bc9b4e855cc87ef67c237455e1cc3c96d5325c45f5c6002eb68740692e35a49a9e5147d30e796b184df6e7fc0633aa8286fabce5a1f99f0a00011d9cc70d9239887c638aa5f28f268e1c9993d34b0d33550ec373acd921d461eab31cdf77fb7c08f999d4ea7f32f48396dc1d0c3cb77beffb58e3ab5f57333a4b00012d64e7e0459fad8067a73e7fd0cc5b97e6794b5761db7b3b7d15858843ac044d112d2fa713d9c29d1c79f73434e2af1cd965c93aac3502b0f368652266d09d34200000000003681da22000100000000000000000000000000000000000000000000000000000000000000040d9c82f1591753eb20000000000000000000000000000000000000000000546f6b656e4272696467650100000001ec7372995d5cc8732397fb0ad35c0121e0eaa90d26f828a534cab54391b3a4f5 +Ethereum (2) Token Bridge,01000000030e00045ea9d9fb9a10f83716100364a9c3d4560b00b1f960251ede94f78c0fb0d6c439b4fb60aaecddcd69f1b52886791eb340238d3a7e3fda3d372e20384155dcaa01011164f9336028b54d0c2183d6097e979da9ca30368102f5d4437643470dbd4c0c3876fe57223da274b2bab8746fd8535156e77cf7d8f78f7db69735f8814ab48801031903a81caa6dc37469a6e4ed1375de0d390c0dcbefc0ad9f5f912082610789a2687a65d91e7a100793a54bdd3b731c49ed38fff73e4e1d703db1111bb97801330005bac64f9a0642251ce45e7549aba8b2c9331be29d75b1ba77a9fd410346fbf65e68ef971c516f0423ba3b39c45bd5ab46f819a29d42b9ee8e261aab36affae8b501066ad5f52d3958068b82595b38aefacbb7247eb7cf31600f5d7f00b230f77f34f257b606586da3c1ecc6077799141a3573e9e274e0ca3f2ab4fc4b7a80c8dd21bb00075900227b2c63f7996332fb02f67f5a9779621625f4525e333a4da5d668780ec04cd2fda48c517d6232db3fb6d47b888785d2e3521c13d5f84342050633bb6f070108574496322f38da956ed90b56c54b4f6886083b201f555a3d764d9e43e00cf1aa5e2ef7c64a27384591458010ee8262ec1cec072625c3930202dabf909b4825460109857209084b4409ccd3f16ccbfc7aea3ea451b5dc90f8d41fa807163917b70b195140d93f53acb48f0972a94c90778dcd3479ca3ff0a7fd8d3892b50b57b671a8000a79f78e3af2e6a095bca2123e83e56bbddbddaadffc801c27c98f41bba92c4ac22a6c71d3f48616b9fab0a69af23e89c659e57fdd7a5f64aca0488b4ee83c717b000beef015fb5ea5c3cf7254818765295e0a61d1e2e2b712dcc4c1ffb3037869a4d561702e460650fcca55135a4f564d07469f79a4729e26bfcd8f804e672baf5a97010ceaf4be34eda4fb97b23c3ca74137e6c1f55f7f1df34a7f645d4beea457a2dc61637f2c8b6a9f35f3e13ff8f3a5cf9dc74a6389b7f19c453b578b2a7c35024d97000dbe6e237a96f4f7219f1d31f74d07ca76b9f11d3781f612ae684db6c6b000caaf4e21caea9f1ad947e8450aed7cd62599c7a6908ffcb900b91af2d80d7cd7660901119855d08e9b5c2ab854944ec5dda0f281c8d9d6408e89d43f6e2df91a82fb8f8714baf8daf7f7ad39c04ac19b39383a49f2db351d5c181c3f35e9b3a569668dfe0112e0a0a193c269f8d2db4acec3ed94fc6603050435ffd733c5f5ab6c9b12245af77b42adfac773a1860eda4500a266fd61292cc54652ba333bf294a6ded053a3f20100000000d77ea04400010000000000000000000000000000000000000000000000000000000000000004c550f77728915b5c20000000000000000000000000000000000000000000546f6b656e42726964676501000000020000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585 +Terra (3) Token Bridge,01000000030d02bbee9e7948d48240e3073429a7791f4035289b6520f174c261bebf6ea54b381a5f7d9402cca78d75ebbb3c607ed507443e96b66513e8903ce4e19028954a1ba901035c8ce8fd98f6d195f61ac14d11c9f7b32c6e94def8d8e3408dc823d4c9ef5089684e405bc8ea772c73206b5789657a7118fb4305e17e95989fcb7bc0106c45e40104ef231b924c6aa072ca7759542feea1991329ee7074b473f36fb6284b2e0eabdf074c16c99c77596fc20c6e9e5b046c3aa5ab40e7a9ce5fe11de3af1ba14219900105688905aa06cd3ff8f615f23566194500126fb294fc1130e246484fcd1205a6701c674440cfaa91ea559d28ea25e957e001f40156d38f061155efe48c87e7136601068b9fee5035523e329219b9ce9ff3128591654b85d39a3aae75695f5de4230a81594e2039d4808a7e72ced5ffdeb4d5c090f2e070676a064c4938d300c51df65e01079e67e3950f2bf4c08e48449d42cd4fbd8469ce65a3236fd55d17c5c3bdd83105571a4146ff7ac338f5b5a7a8e417ff6aee0eb1b15d0822c397552758fca2316f0008bfdf7cbbb1cea6bab576e02bd7928be74d1a38f6892251ef02e00dcafb3eb41d78aeb3aad2693d6dba2c493bfc83d729a65f81a8517523e1916624b8bd8e67290009d8b0ef6910a9abe3e118ae387185647367a6e8ca059bfabf88500b780e88f4f960f2a177c924abc9ba9ce8e8d2e96b969d3fd229c0c10a030e36f808a92de9f6000c5ec295fc7976a6bc80802d4cbba29df0eacae53e223bb7e0e64021a4787454832ba1be31d2157536b7e1cc2348bfb1fec63cfcb26815967450ddf2b058fa9575010d33d1c1558052c0866b204cbef25f9c6286a61b96dbefa426e85225effcde9ec20c0e283c794b18ba9d28311e0548a7c808456a15e57a52f447f8094cecbfa271000f7426339442b83039f2d0c68ccdce4952a3898956ff21edd54b6c237c6c7d801a6b7a11d4e5d5befc7e1a4dc9debaf6f74d1860d262d25efe1970bf70ac124f360010322d3808532735a14c6c523a9fc2da458ca9049f4cbc6c3b7a2f037275a472450c7c5d9a112b4869251392b62b2a82c1271e5e50a9ee1a5f255eebe6fca97ac6011172a1c02539bdd284b0b21c00841fbec1c4df9e52f98bdd2243c7c3dccced08c67659a26d9be3ee4eac2e2b1ff05565207ca305ac67789f0b45984f82af6a32cf0100000000eafd93a400010000000000000000000000000000000000000000000000000000000000000004b4e6895ee716695420000000000000000000000000000000000000000000546f6b656e42726964676501000000030000000000000000000000007cf7b764e38a0a5e967972c1df77d432510564e2 +BSC (4) Token Bridge,01000000030d026f8d64ef762f1e7126d0ce149d9fa7c8e163acc4134cebd569e0c6e8158a7d3b45d440125e031184bcf560872b1dcdfbdff69363e0e41f0d97cdd1c8b23fe3020003e5fb20bbda884014f0a52c533dd55ab0c1a1e5bf9bd254bc363574034536424d136d1ba6635132eefd09aeaa14958b551cfbfac26a82fd6c9d41094086bc209a00041dd6e91875257971f47523df8ae3f9b7f19bc5a770c9ef8f895e8aabd34266405ab0455dd60d8526730cd74e254eda1328cc668ff6e609efc19ced8f93a9decd0105780c3128d5fd867773ffa699bc6391197bc4451392f8e123fe001aa2d47e443e1aba56a28c51e3c13497a8efa5ff601ce201a3da611c4d986dfb6ab99ab064e5000616a054fa2975984828f20debf639d15d4c8ca4dfbb19db31c2c70c81f36bf1bd206a8e02f5202707f2b762408480a5dee4676328ac83602a1b55619df56b81a80007ef9fd13737f9e7d787a18329718748ff951950cc8567dd0d77031ea7b70dc1184bc3933e259e87d89325be9c3fa27fbb9d61daf00e2a3fca8b49d63e6fd2831e00082b051226cb2e1b89f2f94fe301759a1e559edf0fb19b2d260cdb052e705f0fe62caaf19eb07d7c1a0d7470922d1f9b76b01def4dbab4df2d4a7736038f2069f300096c96c851f7386de4f628bede2be2c8a85530a4e079e5e50b7810d639939430f67b3d4f773b2c4151a481d0dc05b137568a995d307a4a1310db56814272f3286e010c8dc4208c78ae9ec3c6bc65c568a8cde4b0d2ea9687725354a557e3fa0f1529824f201fa4a5cde61b79870f574626ead8abde1ee08ed32e89f615398ccd782fcd000daa821071e0bfaab8f0e07db8be8b1d2725df6d91c93ce1597b3ae49baf3b7c22714e0a61dbd7c17cd2daaeae05478db53e41967ea000226d5a9680166c1f38dd010f46cd84cfdf7127a31ec4dc9d8dd4d67666ea1a4762130b11b9c92d3ec68713ea04b6171aea974fdc42b38a1823df3bfa2b5a23d90e8454622dd3a9a69c27cca901101654668dfa8a0506d4a787c02fd9ebf51549673ad589b4d82494ef15ce38c9547bbbd3c0346f65e38b415b39be5a99462e192b2b80dfcb89796fe18d68a90ed10011c9016546925c19d5672e4de9f93f8e96e09192ae6458ce59522a39536191e3d4186b2118eab268584cead31d176351e213ae46ab13faf52f0f04c96c296d7632000000000085d1925200010000000000000000000000000000000000000000000000000000000000000004fa79fce49a7b317420000000000000000000000000000000000000000000546f6b656e4272696467650100000004000000000000000000000000b6f6d86a8f9879a9c87f643768d9efc38c1da6e7 +Polygon (5) Token Bridge,01000000030d023e3f20557912e47e3f8bcfc92d425d1c8f4b059aa552892f7960c61a5f5d0a7141e15ae77d380043686a8e41fc9edb4ef79931c6cbfcbe5d5a63c80116f8834f00030523eae2a37bf181e5879870dd6bfd31bc752e32696b1d06e0a2c7140e0112b8784a12b0da6bcd65913a8b411eb1fe35654055414ee90a22d983e63cdec5be610004f0446b5136e4a413dfeb09f7b050453f6db8341697ad7ec7bb1bf6a7bc003fa730df7f3b7ae8b65b8c03de5749290b9f6ac5db41fa370c3fa68a5ede6c6d03d10105bd59ae4996c229c40f8f0fd562042fd7b83ba049588296d40796d10d705c40895b3d3b0770dd17fedc024d1440b6d55f238a8a6bc303be5b6f851bb140ce2af30106d72414644ae17ae51a065fe66f110a2fe7993003f46e2e8dc396196de7754927761c56a57a88300fd4e812de2a3b9efff3df8f16ffdab273f7fbbb05eacc668601071896111b0e093d2020ddc317dd8756136241fb72a8b0a5ad0c89dbdbfdd93478137a35cffe77980f9d9894fc4ae0df6bedc530efe76621daf9f04cfedb2d7d1d0008a16805c8804d82971b10a0270edbfefa0a8e218e4c7bbd57377037678f01865a4822c0d2df6971936e4e283d443a003e492daedbefab64167282717ca74e7d8f0009acb88dd19c6bb63bd539c9021c09c17b2e7ca9897cadb353a224bbe052af6e3d5907e528c128d56df2d2050c52a33b29c1dd990e75383d7bdb084a64e4484085010c0aeadd15bbff6e5d3235b0fb4893000fbbdbe3f3420b71705ce8eae3a83c3bb47871b7dec96b82885d13748d49f3668729d08205db29ca24f3e307362bbd4b70010d504f8c81f2635e01221a3b6ad8e6d16995d440f2927f4aab1eead23f33df8cc709dd21609c562e359c1199a7fcc36ab23746c09f60be39abfdb8b594fe7f3474000faf74240ca2e5bf9254b28b85ab3335ce95c5c8116a7018de2b76e346f878ed5b6cb96f6aa1b7d378c93a897ca691a3cead5a24df946b0cb8978558f044aefac200102239ed032bbfc704cb8be157b3328c81f1c6d227a2eaef8eeeb78cb63ad46aaf4dca8ea91b92c17c7f11ad5e5e1ea05611dab7ca6bfd341c02b81a7608ab25a801119873e624878824285a8fd684effdb0ab24d994405913d09f207c5f4ee86d33d452a9feb7bfcfefd64cad1b2885ad3178b4dfbb9f96982e868c18523d3b04f9ac000000000006896149000100000000000000000000000000000000000000000000000000000000000000045fdbfbdc28c8aeb320000000000000000000000000000000000000000000546f6b656e42726964676501000000050000000000000000000000005a58505a96d1dbf8df91cb21b54419fc36e93fde +Avalanche (6) Token Bridge,01000000030d022fa91e5df8ef1a320710730c63143c72e8ac8309546c7b36e3a974cc42ae3188169bba1d62afb5a9d04d09cea0b6fdd5fbff9bd3828b9e43d40c66b4a7ac4188010355e8a4edb5de6ce386ba67204da0fb15559b43788cdd0471fdd560c4c5aa81307a6be777fc8f525fee0f79faef62f1a91f99465f84dad8ebb53b4df68a5322390104570a49a223c855bf61fefce149e52efab20d8be703f5a0decfb0cdcffea75cd9418ac7f375a8ddff481eb19494f0ada6532aa2bfcb8f5fe7d79ffa42bcb69d8b000559cf3254735ba37c09512da6cba3417892cef2d8cee64721d85bb652233965ce38d9ef1db109b138bd4a8a75bf05e414ea0123eda9ed673295d16aee13e2b0cd0106b9e38f4a941c104b1b2077061cf5212f4684795b90220f078fa4d38d08fe2080526c82bf9608197e594bb1e575789d201408ccdf56bf323b64ef173c6c75dd44010761d86f30d709fa3304322eb68d5e319af6696291a2c27d3d94314d3a94af6f9b19b60e64d4eabc8a3334f5baf6b087605b51ed3d0fa1049a6711645d81773bff0108216485ceff70b54f363e49c00a5418cce3f460092f119e2049eb5ca56b8bf4d358e57b7180a97ade4700bee8b77d047515e63e83849039a4b2527a349ef6d1640109c8990d7c964186a264ffa01291a23ef8ff644830d76a6b9d17dff6455bcb67271913c099e3597656ab2016152f55af78861e513787847f645bacaa3193c053bb010c847815ab7ce77f9151fbd35fff9767f73035dbc76c0683e1efedc89eb16c93284de384ad6de521200a1cef67279a1d41b714b3e3e558796bd287243453b81a2c000d721ffed26dc5f06ee8858b9595c8a57870f663f10f03e2ad078679a6373b24ee457eafe4bdcc7005f2a2cfb674f0c29394a64c21c81a59824965566e6859dcf0000f28f1f759bc52cc011fcaba0482cd4c3012e9474cc5089b185747d848e998e72231a0c8dab79041bbe804c13032411afd78a386df08ac517939828a62effb22b60110720e9720728dd34858fa83e292730b64991ec9bc8c2f999ab3bf3873f2254e4972c097153e9dfa4cb9b6ab2e2ffd8d9483ac7d6002754795e41fd4aab001f9c6011161cc391d56aead20f0e1169e759cb320277afaf9cf08adb31db58040eb2c97042cf92037df376c87c65e386ec0eb26e22e4ce5d9ace143c02a26612a3d2cab81000000000032bda66200010000000000000000000000000000000000000000000000000000000000000004e2dcca3b0a7d091320000000000000000000000000000000000000000000546f6b656e42726964676501000000060000000000000000000000000e082f06ff657d94310cb8ce8b0d9a04541d8052 +Oasis (7) Token Bridge,01000000030e01d73eb7f4cb191bc1be510f3157a0ab1a568782c56431bddfc0a31e930e7eb82a0745c8c99f61c8a5e9adc4709f627b5c72725b8b5c71d015c155004b1d4c7bbd0103b669e11ced2a091e2f0681364464e226942d1082c4f5e45b6055cb686aa7c7a22b50a4f144df92e61b98597d5cdc5621eeb796eff82a629a487a802f8824ed060104e4df25a48526d61f02926c78460154e4993903b35eb84b8fe3190a67c282919343849513da06fb162e063b25e9912cb36ad55e24a6c08764284728cc99cf4f340105670579b525e67ba5b1191e81b12e9774f5d758bca780f0845999b0e37c4f55464cb69a673acac0cf250be652912296f383bf0db11e53a12ca69eeffb2cfba10800068c44ca24448478a41de9bb6b389ba27459ea948f942d59b1d9190d92725fa551649ecad869f0181e495fb1e6a38f1a80b9a89f12361ceed4b3e69469525604800107bf84aa456ee80232c6994dd85f27936811b69d7ce6b0e9558d1b7f67715536f55db9952a60203c0ff2e292f8bc7c1f1639469bb965f6b920e48204a68bb0689900082010fa5ea37d120ddf723a48c51e408afb696c4abe9f23b029c3d0dab928465a509ad0ec98d26c65e579d1800bb0ae8b2888d19c9f897363abaab90e028a6a8b0009a65fd7ecccdf861913a1336bf6b81838d051379180a1ca84dcba27b3b0912a143695432fbc6449c313b54bcb0640d5c3f8ff9853085a5d94be7efee256e8fc3c010b99f198f424bd9fba16709934d38fadd0d99e17e7e5311baca827f283e0c1524934fe1a040fa3900b89836f9ade5defddd288fdc051b564067bb44d7620134a15000c8d1801f3767441a998f1e8b0250aabccaa7fd00c246aca4e487dc0d93ea2a95f20cc7f8ed426f605dbdbb75915004e7848d402414ca035d0cb30015d4d57ff9a010d42d17c5cd8882d3bb6189974b97f3bfc6ed1466aa1e3392a5389c7c9b2874c461a7c2e432a0ea4642a59b731d78f180a1d60aac54cd33c1a90181798ea3d326a0010b0b79171792d1c328ce7270e55834efd38d5e64d3f78f03f4195d83764a00e7904a1160341a5aee387fc7123ac96db99a5abb57942a3261a2a8d4011ab4af7590111fae3b5103e44fcf295d94958985a709b87f74a0fe553c614dc0a1f8c52ac1aab5af6f4f99ab63977462f54d002a77b80fe580aae7567908da7cae9d6770414dc00125a65a56f6fe4c03d86f95971a3792c70408be4ae4ba3b3859d6f85feeb9e93af1be80071b4eac4cee1bb4c652d14f9aa46e0c6f5c5c239919a58dd243a12bdb30100000000324615c900010000000000000000000000000000000000000000000000000000000000000004e9ed430721bd2d8d20000000000000000000000000000000000000000000546f6b656e42726964676501000000070000000000000000000000005848c791e09901b40a9ef749f2a6735b418d7564 +Algorand (8) Token Bridge,01000000030d039e5bf4669d56988cb0230c80ad566f8a24f31980424da3d1cd8c43e480c2ce9502cfead322a38ba14c56c3ebb8f6bc4654ef5c6e9d9c42288f47911e173c9ec20004cdc3ad95be19ae8cf7bdb2ce6c72f3c2b216926210d3c8ac1714f6ebb84e48e92dfe44e7b7263666b8ddea39f15672b6934fc0fe91b238043da6255db3ffdf2b0005022956d962de2bd9113a90a50d2f15e6ba333bd4d0895edbdcb8c0b1420c1bf00f296f5e275cbc5e23ada26387d060afc24d23a489c5d365bbec9a23bc13494301072db072fbf3369f9c7847f40f0d094c8db09cc676eb289957612483ef457808885284c915ecf079f623ecc3e32102bc60931b1bc00c0d4f2921b2d87f1163113b0108dc7424cb82acd69812fc1b80c8282fca998a95a8fc998fc54c9d6a908f1eed99629e45af20b3888ce570bbd7b7d9225c63acd3d73f6dfc85c65b34d4bf72ec3301095cbf8412e12db817ae2cae65d69d5c753e223bcbe12d554373af005eccf4131f718ced9d3f63df04ff75c7c1dfa0979ebc0559c8ec91a0955d4044b1224e9f0f010ac182cce8ddcb2503ee4331d7ce8f4b182195692e13efb989c693c78636b0cdd951a9231923b2b3a6165fc3f023bfa4b396358eb938694792b2277852adb3d2f8000b82b0c8cb647e9a19a20a1dd19250195ebb4c21a863453d425b3215f71e020a31613683874a5583b2d9fe280b8fdce016726edc4b887d9685a6cfaacfdf28a230010c5a2573e0ca2c3135f7b8b59e60b28522ce5f85aadd19e18639685a714c1e67004a9b5f22879912f7bfab0dec7a377247f1f3c9f8ec2c947a77f47ab0fc280f63000d5c74fb6dc919817401ad67c54cce5b1f2d74637177e4b742b05b4b36457109252968cd223cdbb737c2393852b5db2030bfdc0240b628945665122a1ac5c35594000e1e3e9f45e82b444fdccdce0d1c3827098cbdc165e84000b2bddec505846d32cb4fda5f0ad42f1ceb4f34f16d243df56987a6692bc83435eb8938f50a49e0b87601115721ba2b656b5f7292040fcb8a82a7f30d0267bfd9309823a6ad910fd6c2752c3d4853dced57c10ae8cd949258b194eac4e9fa1c92ccd8ce26f0d8944a9409460012992833ad7d51bdc2d64ba6cff99d2f278cc30411cd7dc14a7a0986b1529d471132281d00109125116e77c20997ff783e20ceb3e60dcb357992967fcc17d7979b0000000000a1829dfb00010000000000000000000000000000000000000000000000000000000000000004f55042d3c512d39620000000000000000000000000000000000000000000546f6b656e427269646765010000000867e93fa6c8ac5c819990aa7340c0c16b508abb1178be9b30d024b8ac25193d45 +Aurora (9) Token Bridge,01000000030d02a2d7f318a1c6b67a1451a4aea97f9ed088dd3f6a77664c0d622b4dcf345c0e1b3067c3d2afc699cc9325b2806ebe9a06e18f4c03521ce83be029e005f7d8b0730103623c67f397d0033deebbd88b90f0245e38f23151acac8d960df68289ae1a3cc930a99d34715b477c1ad86e8c32710fcab86ba8367143805130564a06e3abc256000434476ea16abcd146ad31c0958dbdb7e902a6a33b96ebdf7db3563ff6447bf8244ce1e7ad7107db14662d81b3335bf13a5cdbdd4bcdf64d58ba32b728f0ab260700059f65d58322f99b02e82228751770781d8f6a1c6b15f8427a31744e4e486e422e7ca28c95c52af3a18ff2b04acf2158a544f3e9fadf73e04686f5bfe3e02ba6440006024ced58b330ddabfc3876df8721614e27e5cd3238b3fd11f366d11ee649af502d1cae5478db1658eb207ec82ba9b554d22341731f8523652dc5cb6810793efd000749480371bd9f827408f9c644f1a972ed0d3e1d80009002d5bab1269a6ca81b193f429fdebd1ef4847d0421c6e3a508f6d64c2b5da1912ac2597fcca3ca6b9e76000864d76959392889f1fe8cbd2a7a1f14fc18e843fefae458c436e3a58b24e99a9578537f126150576886ec9810c295b95b4d3066f61c81a172a0324149f8ee1e8a010945ac357751b9b3ff13be9d88dc2079259eb574b91391cd75e7402c399e157a612658d4d9e5282dcb2fd8b546fc9a4e192f3044c0a32864b8afc05ba838c7048c000c31ef015209886d42bda4fba51634dd51d59888b8008ed60ab27858a74a952a3e2994384e51ac9d13e73736e568e72430227b85cd0ce275498bde942cfb8c9484000d5e1b9586c226b8172add8139a5920fc5b02456339f419319959f69a59beac3c41c08dde812341ad855311fadd56ef1f2a8b48def97fce137fa718aed50632fc9010f85793957c2fc6319470f745f440a2ff4893a3d9145cdc6c1624d86a929e3ad004707927092a5951db71aa958bbea7d96026cf70c18771cfb0f448b2fb188b7f90110f8e40975afd07984d56563c5baee64bc39f776ccd055a1ea22322adaa4d2ea6e63d6c469d819db644d80013a3410d963109a0aaadf50bcbe7ac1848e6809e06100119d72cd35a66fdd5ac892aa61a519bdad6b06d2b51b831e174784491999826f8867c981439654a4e064eb1ea48f74f10a81fbacca86be9087bbb182045aedb9e40000000000639496a5000100000000000000000000000000000000000000000000000000000000000000042c2817cc9b0f620520000000000000000000000000000000000000000000546f6b656e427269646765010000000900000000000000000000000051b5123a7b0f9b2ba265f9c4c8de7d78d52f510f +Fantom (10) Token Bridge,01000000030e03a82a32d9a0119a29c5d5d1e6bcba647c82c60678dd02169c2c566bf99fb65d6a1e5f2403e963471b5e224f4821d1408b6f699d17503fb3e18c1092a926b277b40105b9989551ce1e5305f89a401c9fbbce1ddc5021fc909744fb45c3c4a973e14a957d017028eeffd884e9ad1247118c391df8395b3d48dc57703835fa78b3e968000006f8f70c6c33c36d010bb63d12eeff49a910f2cb0f20c8a1e141b95620be27afd42bfc2e4ab12278e4edb12e14fc6178d96d43400c80fb44e1b058913ce2903fea0107de9d9ffa18f7edbbbf3aeb3e4725779e8a90ddf1d4ec1aaada4c775c3781c85c455d37b4e8a3528527fea82c66fc57d543eac00356b008e8c3d1aab811ae5c7c0109763779ccb39f23ebaea4e7ce578f4042c262677cdb3ebc5fbf6ed2aa684606035baf518a0387b1803301c1d694501667283cea9a9dbc6bb086ef0f7f499dcaf2010a1cc87b373480fa0d17366d9660a79ea79bde2862bd74eb1e70ed5e3add45745e560a63254d85d0304886b4cc7cb84f43ceb0a2c67ed27059b516d0243097ab9c010b9da56edffce7fa25683702fb17a8f38905090d7a56f623e2efb4ce2465ca8c463aa3df6126842a743f9c8e63abc62d16ff2308a5de19f10991d378615551c9a8000cb63a1128063575f87a7b50ccc63b384d80fa80f96287705387d3eafec22cb0d70a55a1453c55ec17f868f274a047f750be6882474fb624dfd7f59f3665e6627a010d5a71dafeedee56cae863738c55e75fbac9903bde846770c4bd40d9864ac31fdc6f717bee177f2f380545b8c78e8fd09c60e2e726775aa948bdccc8ab33d8bb8a010edade1cfc075fdb4ef48171faeed7769aceab3eaed09581dafb6cc1c8c0ac944839772132bd9b4ea80817c2f08a8242900450db0d30369c9a5e70d22553ece0be010f573ddf1df2683cbcf8b68779d6a594712042ea077d6e4436c2f72fb114b5ffe31ea23daf21a1f66864b8c120c1eadce70c7ad1b77240bf8f9898c68f1b4dd623011046eea234a7226e0a902b5c429a074800e53b7ec3dbbfff5d1f37f99dd19318b242937e35950c6e4debac3dbac76405d77162dbb845942f5462101faa4c5f3dd80011bc0b5c1a809b20d2b115f930a41a56021e351d98203529fd5830fa6e41c0c66527ae11afc2f6789e2c5f637880525f38492f496b18d57e428b29ef9e7b4d2fff0012c62db363757e91f25a8f2619e0b370f0ce5a77ccaef99c95fdc46bd76507d2e97ca433aec7f3a023acde2d612c504ecf3b28a7ad817189236e8e2109920254d600000000008c6837d400010000000000000000000000000000000000000000000000000000000000000004230366eb9e6d3c4920000000000000000000000000000000000000000000546f6b656e427269646765010000000a0000000000000000000000007c9fc5741288cdfdd83ceb07f3ea7e22618d79d2 +Karura (11) Token Bridge,01000000030e0238826e6d875ee99c28e1a7290ca018c62ef04fe0b1ec7f1f30e4d22c8dba96e101299f1bb5a21f66d8d07200088ffd9da08d1c995af13bfe07cc47573b401b1b0103272ca0805c8d7c6266c9a285ac39f896fb6db7771a92cd49b268b58fafc35beb3d1287a6db989df16e9ea61161455f0562e4251982be5c64f859696136ace32900046ee228d67e986f71b97b5003353fda9c1a739fc65fa632ffd3a9d9a3074585116241cc44e39eee1ce2597ddd615a0414d056519026cda7f5854b7245cc01101300052a0c536fe881773bdc285e9f01cf750f71651c143082fbe687df28d6f985b82e5f2b9442ee28cc1821d15133fe50d0b45682d55e1e601ff60f180a277224c51600060065799679aa61b64eee17a443cf00b39334e1589a959caadbfaf4707e495bee6c097373b80e57d2833727f948c846402b02fd2a0659a2b65fa37599a02243d4000793f964e8cdf46de0871ba47a0ec2cbf728289510cff0c8658ba443dac7b8f45f26b20314fa8dd8c0a9f3c6f8f8367ab6c839c375427b922e42e9c8378a5834890008fa46f6017f2846fafcfd9d974ce7d4821a022b85cc283c5dda6db38de6633fae12906a667952c6b4745ddde3896af7a7ea3e803723aa4796b27d64f8f18ed6610009126619c71961e3b5e9097220ebd470831940ba515672f1795f47fd31376d42b5220838e1a54bf7afc271bdd7b885c3c554ac8a2bd231cd2164d53e7ada4caa07000b72b042c82fe4c6269cd41b8c71d407dba851066855964cf05fe1a32038dd41e140ba200592476512b09e08d25c668955fbb3b0791c6c2da1fca41ae9486080d9000c2a11fd2b89e33af9471bea6a1cd7a999212ba273cf157677362ed0bc9e1af14a7edcb02795d3a0622cacd5190b3f01699db0de569f7cc91876152fb89a044208000dd41f9cf8e861a4d736e7997dfc70408842e2c99451531ee42a5a3c99902fb77a07e2cf8fe2a86874498ddd12c031ec835558522a593183792286a2c05195ed6a000e2f16ee8c8e2098ff422ed7e87a23804b9720933e006c72d84d51760dd3fb073e352f12a7b163edfde86665a2f915c5b651e186112080806c5fbb3db3d84fc337000f93a1bf75deaea42014dbfdf98614446e6bbedb530d34d11ba1013f9e287669d03ca535a973deee020cc777f8b725f07ef0829038270616887e807941e321f81600100fbc0b5bd2b5f87d6718201ab2cb3002b76d30f0c2ca07d3b63dd1b32c2eacd8198097ac1a7b83d5ee1a678c4c01f042916cd0276a278c383b15408b3e60511a0000000000897ccc42000100000000000000000000000000000000000000000000000000000000000000045c9fa2e92b0e73c520000000000000000000000000000000000000000000546f6b656e427269646765010000000b000000000000000000000000ae9d7fe007b3327aa64a32824aaac52c42a6e624 +Acala (12) Token Bridge,01000000030e0086612b561f657f7d859fe080ee1447b97f13b6e72fd68cbcc91e5ac5b04b616e5a1b046272f22098dac0a63677b430fa156e08b385f06a7f5c8c2ccc453fcdff0102c2dccd6d5f83ec6ef4ba9f367f234cb08fca7013b9470d7ba157650d72c8d0f222cdc722b8beef49c5c005acd2aef221823bf51c22a792a7641ff84ca72875d3010398f091e8113bc28af6cef37a9f97ef893bc537fb6cd4597b6ff204bb8748773b5a222ec0870de3510ab0640d3974d3c76420cdf8c1916a8dcd3cb54a6745ba000004202c5c5b6632a800b91ccc3058e5ca5f39aa20f192e66d80a0b55fbd0360a27a2fd1e79fde5dbf6c34b91f49230dd0e032bb54f0fc39126af56137588c13bf400105e53de0114eff96a0c88a7b6ea3f5b01560fad040d239aa92acfd0950e402ee217559bbf1ab6138fac9c620bb9cc45f5f2827e56dc94b82c43978d7a97d5ddb8001074bb303a628a01a95f371a6d5312194e7ff47f7237e012f8a2ade6138bd0acd89395f40bd7c2e3bc3851ddf47209e7dd0e98fd773663ffca4dfb2e29c24adb73b00089bd171de7bac23b5e012e2f4302d5a8b7469f0e80589ff46f3422e55d4b205a41718e8e5315ac735584d50cce4cd1f7b5b997714d176991100e9150730187d330009984bbf980f2362bc0972d8f8fb323b10165de0827d13c3cb2ee95c1b71451ec6338397add1b3e57b85a9e0c219ac5c029fedb77cd3ef9cd797dfbf2729b1ad29010c771a4ec393e4f0545a5af2bbf18480807c6ad3d576f8e96b801e0269d4dba88e4e076b64300c8bb79e68b4a9ccec607135acab27fa65d136a0d20a75c60ad477010db6511f312fdb577d3542b4954225f32270645fc23405f4efda151073b53f54aa19c921105b24919504dfdf3ca0a27cf5df9128a67cefde2912425d3ede5db19f000ed08d351c0ad4a659a431edecfa8a1b0eefb7266f7f8f34ed836d9a9aa934eff0410f8e915a30528db04bf2a2b981dcafd41ae0f1dcf994fd76fec58ace113193010fe714d92060df8e8d48d89f9aa9d003cfb6fdcc6790c26e9aa1747f2f12b378b30817fa0f43b4a70bab18783853cf63f218f73c1fac5b5cea92f7b8400544464e0110eb41bc27c79098eb77cb7d270aa9b3b8f66f1f75ec48071ae7f39c0a803b63bf1744039b5e391f248b3439d767516329ebfeda9505904ed76c903d5aafa6905c00110094060ae0bd5121e051ab1311916d320e67a06b5a4eec3c08d31ce3795c7e195e08b73834ac75ec50949a28d83444c4e73ff07ef7bc1947a4b9b42b78565cf10000000000f97fd62e00010000000000000000000000000000000000000000000000000000000000000004757383b2c2ade11820000000000000000000000000000000000000000000546f6b656e427269646765010000000c000000000000000000000000ae9d7fe007b3327aa64a32824aaac52c42a6e624 +Klaytn (13) Token Bridge,01000000030d02e48a9b823697f08a4806c8a5a08783ce10f26aaa60a3bc69d2a705ced4e4a06675cc9b0579dd2ad5a72f65500d7e5cd8ecc6f5e6991cc832e75fd250079b8817010392e65ddc65e5cfec721ac42e02241ef4d6bb457428861e32ab8d888ddbbc742b0eb1a5dde8b06ac3173f9f2a8c709271b5ffa3fbdb82c440cac17d87932d907101044ab8e98639fddd70eecf342c40eb09fd2a9ee9c84991b186835c6438e0a8d88a5adee107bb49aae0ca58459b14de61101694d7e2c88fa2fa1a9f6af05e062fc40105306e14a59e4937a7414150ce73211377ebbc23760bb1bb55ce4d5b6e6fc2b2ed0864c51045a3f4f9d6fed0ea1a3270eb27ee5a34d19337c1012649bebbb76fc40107694fb51dcf869265617d7c6b18ebd12c8a1d378c4221ef89aff88a76aa54b2355dc974dd439b673702017c44fb30a68ec43530c41e0dace438146a4fcfe102b601085549585e70aec53d929aadaecd6cf5204ec78fbed837a2d30c42a9fc5daab8d2246b1cb8fa389078f23b602631bd1f7af08c546192a855ac4413fcc586b537ab000958a29e434e654233942f2b71843f163f4b07cc506bc37a082055ed418dfca4724b035ca21ba94ff4b51ff88d1656dc0ce8035a61036a20a262aa23c4df99d514010b80f02f0074a784ae3152dea6340efc005a0e101036b18a7cf89a0102be1d8ec728a0e183caadc8c22f17a5e69818125819710fb7d982c1dd0ead6e854acafdb3000dc1f7f209f0c5ae96fbac7abfe0a8cf5a5df9ad6ebebc8bf789d8f9ab4ccc6b3c2af5abd076d211d0d6b6d06c0af7a91275e5cb002df29e322c18a5d0732e8283010e974d4c33abf76da3ef99f6e688f344c30a6016f509faa46a089cdd890937e28e1022e5e9fbb1fb9ebe16c1ca54896c6e1bcb14ca88abd2331eb07eaa2dd258bb000f1a4f45d9070a205c0ea242be3a638ad10e327369028879f6ab47d7185baab56165706896bfc8ca80993d3c25c9a80037d4323401b0dcc3b1919b5701683bd2fa0110e27822f43c0f3eb555330ed555896715b306152b0d42d2ad4267e10c7bac21055850744344fc0e2385bb34f7b2aaf2465bfa0a7c667b6dc2e7410824d3fbd94a00122560fde65296ee41bc03db3c19c76b6790c593c80aadae4271f80a42368e857c58374a3cd72dec321bd67ca8e08dbd2ec4576a1094e20fc508e281f0bc721ea60100000000e3e3c1f8000100000000000000000000000000000000000000000000000000000000000000047799d8f9d5ca5ba820000000000000000000000000000000000000000000546f6b656e427269646765010000000d0000000000000000000000005b08ac39eaed75c0439fc750d9fe7e1f9dd0193f +Celo (14) Token Bridge,01000000030d026b48da063e5f751a91b900d8e1aa0d15fca3b06969157453e545463b3cca72de56a2e67d4cb351b0d43a7835f9aee14a1c0eaeffb485d94898614a2a6f640f2e00038e707b662d2280e9b0215f642b751b1e4dbe38a4547a6e3cfb8f8302db145a2a115ea1d6ba9d5444290df3f634dfdde350fbefe7a635cd60095972c433ccb7d701049015e40397387e38e8299d60dd682db7df67a52b1f2b0c72cc70a6e47745aac868fd54d41b7715aa87a213bd8c84dc4b518632d775801310e17af09d0538a2bb0005cce55406328773d6d5d19868d2833c341f9a89c566e50afaad0d1c5eddf18472227c932c0ecbf14adce6932740c6043007c294e0593ed417787d7dff4ccccf6c00072de934434d336f37935889690961696b09709249304409d23b767761c46d3ab408242de2a353c8a3c09a98c78de7b58a2a9ea3a0bd8190a9e54d30227bc88e390008eab5ae5c18cdbe75fa68ee0d100b62d01768f38dd80b5607c6b9e1a12d9918b154af7b441e00835de46289648fa2ec7ad55dc73496a629ad39b26a067f8600f30109fddfeaefa1b180fb6d552cca5c80dda8e2d6a250652d920520bc2a9fa9ffabe94a42a12507062a4015591c2e9a9bcdd68cee2409481e053ad73ad6b857855b7a000be1d2843aa4d70db32d371df8bf380ada002b75c12f516ec28ac772d043748d7237aa5e18f674ac31f8008e1459b5678227a57e32b14591e1215a124825505a8d010d6e426df2e7ee7cbea91333f2eecfb19683e194e5d3fd3fafeb1876351f62776f6afe25a418e55594935760ce4886c62d08fcc411acee7314d88c7ecc38d3facd010e68cb769c5d42d681e059eef022a912125e4d86dd391f1066b74bf2af92a371dc0e2363db8d2a7cd9165af95452e6664b0727d72b823e0bcc76e991bac7853cb6000f04df79dfe70e8f5594dbac665a05b86fe0f292bf9a6432e0ee53b1dd83c846041efa83c8495a727d47fb49643482a15c08a02a8a029638f55963ecc910454aaf0110d8a58b36c86b8260510bba52de40015bd9d0a99b788c563c7405426488bcfb447a26da34e3151cb0bc4c8405655ddbaa873dad9db366e136ae2e4f72a5fd92e70012eb3c585880c66ca92bb8ec12896b44d65cfbd09631e1c9b6b8aebb5e129aaf0867b0dbd5d22590430973f921966dd307a9fd47710b0bde1b30dca028c5c414ce00000000001f77d2e900010000000000000000000000000000000000000000000000000000000000000004d918b69ed0a8cb4320000000000000000000000000000000000000000000546f6b656e427269646765010000000e000000000000000000000000796dff6d74f3e27060b71255fe517bfb23c93eed +Near (15) Token Bridge,01000000030e009391f4e4d74d097dbaae292a2191e1b3e47505d444f1eee2e7727a5d1541de0402fbcecfcb0898b61b1422a3e7c541ec8c84472c38e6ffe38fdad9ce32e2f341010388096b8c078d1a7930af3faddbd22480da3fd5df5830adbd4ed05ebfce6b9f7b5968b5db7cbf8ad3900af6bad8622a8c02324506b810f57c02602b6b98018e120004613497daf624ce8be2ff970781ea010f13e1ae73fbaaf82d4f2618189c91606269a2b99bc2e39400045d4d677f71349178c1578ba07b24f466448dd5098202bc00062087876f4a43e2f055a7a2b81abcb2816939ce32a2ce95ee39fa625b7bdfc9022e4c71a85267f2f920362c655247cca60819dc1b354993996f2e9a78888eb81800071db4d32a97b3914fb476dadfbf703dbd0bc86eae39dbf867b824b506f0c7b5482283b7e87d16ce515a3ea8196f0b625637c6ae6088ab299967f634ae66be5fa9010881c3aab03ef0206be2f29527fdd4fb8ad98529f21f46ee2ebed71705d34c5e422028a1c80ee0f87e1494d9221c411499ed17f273c64ff1458473e781fba5066001096b3bb76d7c3ddb5adf8468cbd0a3b6a6d9ebadcbc10e29a0d4f30e2905ad70a16f9f7d9fec52e90ef8f48ce0428a5a85d8cfc008354c4ad9794f2b96c0c614de010a4e23ace80190db70f3e615313b496a00bc80e40a01e25dbf57f1804f92185bf9771c1ea60165ca0d95a966b31184e6ab4a4b6b83e82a13f9e7466758dbd8ac44010bfa285466156cb293354ab79769e630085398e3abe0253c8b4a137c3bf96f901379ceb2f64f81f4d2c84ee7b14b2fcd9d73e4b6ed27197545035cddded73f9dc7000c42734a51ab194546481119c903947e49889b850540bb6d323222fed217e52cc535bc298eeebf99ce3921b96b71efad06959381d88b439ccc4ba466e32d5fb231000d2c561108c7bb1ab410f99697d58326f0e8af256ab988f98b4e5e3193e4bb9ab9427f0ed9a6dad9c5ab39fce63b52cf514c1118822a90f17fa89ab089ca480a17000ec8487d16115af83481d4615eec4a19afaacc191bb096f5fc010bcfe263bfc08e03748c79148784f53012e83fbbbaed66e4dfe3430f92133ae1d09648b444b915010fb5eba88f84850b20b9d55e96584e6718f792c53356e1a6b31855e611b41d7f706a0943c1d436ca7f279bcfdf4774819ec347aabf6d4a4bd97d5ae31f43ab25060010a0598060028e75922dfc01258dd8db848ab05418f0b94fd13ca35c4ab0b7d64f4e2737a104d8e2bd33f3626f9aad62440d6f1f20da1bd6baae3fa505d8e107ba0100000000bb04e8af00010000000000000000000000000000000000000000000000000000000000000004cd25ad66b4c6a6fd20000000000000000000000000000000000000000000546f6b656e427269646765010000000f148410499d3fcda4dcfd68a1ebfcdddda16ab28326448d4aae4d2f0465cdfcb7 +Moonbeam (16) Token Bridge,01000000030d02f227a85a34547b42fca183d7f60177f40756acc43da1e07ef7ce97ed049e484e6f6a608e3ab2a999c5ecdddcc0ff81f3d512912ecd48b409733af707b6dc06bd0003355de163fab102bf7cd43c25d7798c700aceb0263c6391b85c40c51c6d99be6b331fbdc608af5198180cd9dee0be0c9cd33dc513680eff1600b2717b95eb50f200044b06b8bf20fc7227dac43c351a56e84895c2df902aecd7fb21b09b362a43950c417eeb96244f154805eb0e3e7aeb0e912eb04c6f91b5c93235fe657f3b2968450105b8f0069024af922ec28203751d85bcff3a0736b61e553e50b94aea6b003e3bd538dabf99df5eb02f4390b36c975cc22d9ec1ed9d66774e8e882283cabe7b03e40106c22fa08c2eb0fc8e28640a2e7732d5f06ac656b613e8861b1e342790bfc7b40a3b981fdd0798b4150c8ac3b05e34e1c940af78fd9500e7d2c75c10b8084b420f01070568079caca6aec4b781bd7e96a8fc938140abbc640579290bc197e1b89ffd9d6cb1fe2973d599a035be652e0f8b1d32ad40a067de875088d041287edfc4c6a001084d62ae956caecbaef4b8ed908aa6f817e48856e2e7f7446a349beabb5c159fc863cbb120957f7850f989f30922dcb17dbaf0bb98cda0b493c9fbf073077342fc00094cb45d8970a5c44b72cdc8ebb689fb723b5b9f659e87f1d67e287e0b699b0bb24cb65f3840ee5a312c63c38ed5f1c8e4c047c245b5e9749687c241125bb6723c000c208a03e49cd9e227650076609bb008d35fd055ac0bba9aefe99b05f09a9d758445c619447437c80aa08d8b2844998b3e38e69be4cf5aced1d260e7e809ce5557010dd5c5d0b921dab5c3de31059ffd1fba74d641720483a70c5c91c3db59cf6d15f85f02aa9a44e3e8b251aa2bd40d7710d8fa676782b3532db336c18a0f0d1a2c26000f32577ed3aaf4da367aae825879f0f51fa8cd71bf728b3d4de63d3e5dd10059310766b33d8c2cb90be1d801503edc91b5660eb7335e0bb19d8cd9a3c8a3de0cba0110f0462f01960ab426f8d916e264e5343e3b9250925c3779b2e204176063e06d4509ea88141d8e1e3b614ba913221cbae44d336c2793e71f501ddcbfe0aed57b1c0011369224f3ff8e81c6a9fb261e89917ef6d30aea54993c41fedac0ee39a321bb3b559a42a08a98cd98509aa72975dad806c85d0661b8b50915f73437ed42bfb83c0100000000c06d7c3d0001000000000000000000000000000000000000000000000000000000000000000455f2d6158fde6ffa20000000000000000000000000000000000000000000546f6b656e4272696467650100000010000000000000000000000000b1731c586ca89a23809861c6103f0b96b3f57d92 +Terra 2 (18) Token Bridge,01000000030d050b4ce2af187dc8d7b0cd6d36d4eba90887a81fff30aeb057c649bc265fe06621114a78c9558e390565dac4d837d5cb857860f6d370c9e408f1af0762791d6f7b0107f004a0b9f5d4736031b93e76a2f7b5219c90a3740f135e0c11a60c8c5cb181b740e6773a8bdc7e9f770e1e6ecdc3d983ef69e1b97181493c8b57edde4bf717e00108a8d52257078e804104c2ecc72b23a0942b3df2e01d03b7740a66010bca0acd000eba7910884f929c6318a0e160d0dfd677d035ad76ce42960b3dc681458c755400092de35a61294d434906c78bbebe47cc30b8cca40fc43f6f4c76df0d6d894819e9229691826fe6ab0688989cdad2216dd6fc82a73dccfb7fbe7128da47e5aec032010a585eecc77475a17b3cc52fba9c00c8a9f9e17cc35c5102120ef241412de4408c5a5028b84fb51461f291f017b46d70857e27b2158461c31604bca90a7eae3234010bb0b038d88f809b03132c12ab42168095dc9b2adc366b0c2e9f35822c20a2db4a0cfe9505f156239cdf77bde81e4c4b117fbd087e37087b2eb19292895344dbab000cdd40be5b51d5d7eb2c2fd5fcfcc418e5f2b89927129a2e62c4af17b8d1edcfee490b13f507b75d70a96daa7480d196e5126bba2fe5040ee6a4d9f552d017242f010d0b7a14c0150e6a18e83112a64f44df31554d9fa2cf0dffd525df58da0d49bddd362c872fedce7adf9bfe96f165d9276226b74e4233e768bf120740a4ae49f80c000e913f08c48646027cbf517b8a1dd5fee70c067b880911d1eda8bbf11f7d3b895f1123d5abe503e1bfcb238290bb166573a1566b0e537301e78645cb41f53f6019000f805f3572820ec64d6000190e8a6e1f86925b0fbf2df700a595a3bd9ec73cb12901b8c41c662c95d34914657036259cf1ea45ecd6d37f31f3e2816f897d0388130010380ac2e62d0180a862fd46144fa51b84f4771890dc2c466f50be97561d1ca823685c621119009671e6b3adcb1f22532a2d924c5c92630c8544bf7b718997e33a00110f20c75ac1398952c4743d864705d9230d3f9dd1de25d77a0a14bbea00d87b973fa56cbe59762629e2f243c2e4d142dd34937fb7841dac6d0b1636fdd21bb0250012b9472c22e57dda221e2955cb8c11c27de067cf0cdd0475905ca92ea903ea121d1db07e1fb8cdbff46a142d42d9d51f758a88954241b388dd77a9e1e35f84208e0000000000e63a60d800010000000000000000000000000000000000000000000000000000000000000004b8aff76d1bfa735920000000000000000000000000000000000000000000546f6b656e4272696467650100000012a463ad028fb79679cfc8ce1efba35ac0e77b35080a1abe9bebe83461f176b0a3 +Injective (19) Token Bridge,01000000030f001f8db0ceaaf637f4240c33eb389b54f4df08be7411b875cbc6e188834768001248289d8647079faea4a16ed4d4d657e7257779e4b118f49e4aca53256dc080560103875834d7ca6b71a8a0379332e8fc22a001ec8ea157053c6b168ff1a51501381544ee3a07874d41f5917484f0f1acdc85109de614ab12b3483b49d91dbdf7d49b0104e37ae8b219845ea97c7e071b0ba7acedeae3eff782c5f76fa66dffec61d0b9857ce7e805987f05eaea0e297faa39db40d9469aba572d3b53b859483417585a430105c318ab2eb560e28b87cccb44358b5b7aa7a9fc69f63c522480de0ddecdd7831a3cc26b29caf7f500cc7315a7aec338ebeea3153ac7ccf0e040febc759bd969fe01063080ac108214643650b1cb5cd88ae539a6bcf7156826032b8b556f43d5cdc4f810e4c3e2c619cc4577baa37e7276d1b80c905518a19cef1c4b6b6c756ff3ace801075059b85d63cc7ed7c1e7bf52a0e7891d50b9da9b478e35f0b97fb17cc37c61a30bdc8da4be112b05e1b17d64fb500948c75783b73d0ddcf74afda56732cc56d90008b8c3ba7ec35b724b50d2389df250601464e20aae52140177ebe05bad3c6fe4304139269e7e768bdced7771cc1c580e1d71acd4312f3dc18eda6d34bdc82abe160109e85b86a73c12b5bb4d3e1b00aaa3a17dcb2eb49651a9cb9a40514b97f350c3da4fac79176fa8fcb585ece4285fd653f9928833c9fc4d0ac9e85c4488c0f8fd63000ac37445a0b06d43a264edcf70e07e9603c5bdaa21e89649819f77e881db09d3560de902d5829a2b0db4239e919daa98ce9dbdfb518ed28b753177b821d9fc819e010b6f7883cadb448b2cb47ac77bb08f15e902097051c4f676436fd34744692e7ebc509cd545e043d5b4f543a1316abfe1bd9c3acdca49593ef9cd878896bc8feb07010c5e4a4647ada88624dcb1e52d11232dfb9a737ba7d46327975356d3fd4b3c8038448ba7124ae20e52eaccad10685828b7222d8be5cf27aecab13677458e30263d000d36a5d20d958a9c7a1406cb51b9e9ff4bf8cb5c8eef497acf3adda3f6c8b42bfd7deac245ae487b8c5dce504f2ce6c59d5824a57a288b709bfc3b422f58dc6dfd000ea114aaeb26b442a9b7a8e54f25b1603fc4c5f1471afb470b87df8346a5a9b70e43af3ae8850acde01788090912d80aed633648d936e499d5a80697dd4b3ed13b011049b889e51cd0131ee86b1980c4a156852fcfef66c772ba985da92f8236c1915015fa51361f6f8656ef937ea5976fca9a1c7cd3d8a646c3cd548a8938b7f4317c0111c0e4e03e67ff59e0e0f454a5802e7a7fc01fbd2bc728c2a8118d4168d807ab7f62efa069b343ec483ba45224d091c2e489439f21cf66bdbdb9c6abd9eec3376301000000004cd9a76900010000000000000000000000000000000000000000000000000000000000000004843ace2efe3b606720000000000000000000000000000000000000000000546f6b656e427269646765010000001300000000000000000000000045dbea4617971d93188eda21530bc6503d153313 +Aptos (22) Token Bridge,01000000030d038f407720405fb547c5c28e6351584e2f3dbdb812769f2ad5a85299d8e130d9f472db0499f5a80654ab2e77de3ef482561e543f0355faf7458e0c9d1e4bdd16090104d3dc920740fc097c5e1c505bdbfde06d97ffc6f9e222b7542af499878f5343c56a3dfbfb4fea8faafc2542138d36378e7b2be3f96b61376911e107089b8a0659000634a1bc02d1e73dc3ee726eb909014607994b7bb0efd96df798f497b385f0139a19ed6506f588db060f370cf5ee1787720a5cd5de445f633afdadde94c462cb570007fdc715d5365a0a88ec48c891b78a5abfb3c111b0cdbe72b997de8e50350b21287e0d037caba40e87ac05dc4417f00d76818a72ede08c21c7f28cb95dbe072e630108ecb871d82ece81276b023353259d54884b779f26a5f1ac4b6f2d816a23efb8295a04173a7f170cca5a60cbbf2f82bad20c2eb5ead8e94f8af317d7663b7a0d24000a06c66cfca7a9299b3b447a9b783ba8d91469ea64e4d1a66d50ce722a89cafa5f2237e2902727befca958e87e4cf8261eae22f25ba4cab8e5a596de1e1f1b66c3000b4c314bccd334d0f51e8488e58338854dc50311e453ed1464c9bae8136d7ae70d521bf5405901c6c602b8f49ee9d77f0dd3d3eed61a1b83048ddd08957dbd855d000cfe19332e5a416e337eedc0dcc780f468b1a8803fac37c92389051e0b8c6408f15ce80eed5457514da8331d426a3754bd4df47554829ea45bec74178083144a00010d0488696901450e768c10e35f702357226a0885a78ab4ccddbdb8b839ebbe365e4310cb5f578fb5bb402e03fa66a9f5501a99a913b1a3df7eb9cf42f163f1673e010e20c84116a46af0ab0b57b1dbda4a26879de4423545f5a56e1ec4a0dee1bea92b5441b92f24f1484b6f48ac1c420fe5b76b8baed78c9e49d65314a402a2570876010f001b7585d62bb4f0835eb56b91c369d3519698b33b1e0dc31b05e3a176b193404a8370421624588999bb0ff810f29a0a944eb290af8d30a79b236942f368d9170110e2ef3b45dd8a4c176d77a0b49f42837da8921aa3da1cd97251480cfe2e90253274c032297b3ca92a53d5f91c5d5c5831d4527ae57814f7d6298326ffe5adbc0c0111bd2d79db82c6584a77a4e397c553433488bb1d5028b2df6a804266e3fa83d6610d2d40d24a67ee343fe51bbad1c5eddd2c6cd92183f4746f7b6d9a7d8d91bb630100000000acee7e4c000100000000000000000000000000000000000000000000000000000000000000046facf77f2615449520000000000000000000000000000000000000000000546f6b656e42726964676501000000160000000000000000000000000000000000000000000000000000000000000001 +Arbitrum (23) Token Bridge,01000000030e00e3b6513ce65007fd3bfa7b082e481ac527659a806d5f7d4bea92ee5e9a18380c275bad7f8922d63f940367486d73486a52ccebb4b5a778781095efd9325957de010307b6f95c2222a1eaf80424c2b03972a67604bdd356c5cdd653094679baad36d65cc332836f4f6712a051b505741f2cc0c619b175531a37ddea0aabc3698c551501045bf91e61d1e8591e0982e96c2f30c56353b9e76cba87f739f733cb189b8ca3fe3f866abe1bdb80d15626a50943ec9ab8a721cbdbf7ee1e1e8c35f640b51796e50005cf41529805953769cb1ffee78a4464d1f0ac7c685e1a8efa0e802a60b3c71d8f460ec38249f49cca39df3b10f34456f31600620fb3abf4d463b6df44d6a44ec30006545e16e9dd2290239b93526a963a0f4692b87af29ceca768332b3adb64e3bdd361666c0059fd5292202c6ae5d44c28d661197368b7d03f2e57333eeb12d816c400070dc4bba6d86eabe5a3ea6ce75a4bc0c39860ddcf95d7e333f0cbd6638895a4cd172a5c9df97c5bd92ff82bb0302681dfec1d7223f729279ce0b279f0253d4b160008ca122a92a690553293a518a22888d39f2883a38af4df5712f17d06244f738a643d3e51be2dd01da096cc6c7256f28feab2e303612a9a89bdc7036fe3a66de2a30009ff57fe55e105b62b1fe27d77a4b95033959e7a5e7bf76a23ad7de4420213350e43e8ca70ce18fe5cf4fa7722a28ea23099e37d9384b119680ff5fe213f9c290c010be0fec1e4febd3e2272c9c572ae21c4f2bc64b0ba21e3d5d633fb92d5588356127d377591e42a18539bb824c952790d6c7c6001ab8c4506d0706b9778ed2376f6000c7627a74c1ade8a15135622d3bdbf5b221eb9396e852718354de1a0a833d959321038f1f4960bca12849e27abeea28afc5fcdec1d220ad2925d356c4ad3de95e8000dcccb8fc9b86b2d1283908a1366a04d8dd8bf13b85601b6fad97a003f53094f181e09e10dd89d15c152457d6e1671102cca4b939534324be5e6c0eb8f48f54af5000f87ea7871f65faa3c61909942efe63a5c5687a78a1c5700d70ab77d08770b90106a203b05e7286944fe59ef8853d8af317beaef088d8652a66ceae2012cbb9cfd0010763bb571f7c2294f1edc5fa6619c23960eb775f7a4f98daa3c640909dac0e73e31aa8e004559ff8e70c968202f59d1b1f1fbd78a341e4e145cf79faa3486047e0112d9cce60a1e689dbb340ccc53684b1d93c3c2263610950e5c87559174514bc51116163298c0369ba416169d9d7ee6d14d6d87a794df94c943b40b2ea8a6269c4b010000000065b326e500010000000000000000000000000000000000000000000000000000000000000004b98f7fb0c87e6ef020000000000000000000000000000000000000000000546f6b656e42726964676501000000170000000000000000000000000b2402144bb366a632d14b83f244d2e0e21bd39c +Optimism (24) Token Bridge,01000000030e0034614d6c092ddab582ba61976b34697c2f5bfc22b6dd070ec45db9a7df409f78586c9cde3b92fe9d7b1e6b12a954177d70125af0285c8fe7e6003483f95946370002c0d0e7d5841cf2448159e79e1c1587c79cb91aa926db2dc885c62a4639da3b2064ecfbf9827daca8cb2821a8e54ac927cc3aada700789dea3aa0ea0420e9f0fd010374e8665ab3d4da057625525eaee0ba008606c5cc3ebd4d8c1415f11593b1e19d7da43269cc199da176c51601767a7241d21c9673e18d4a3af42c33d7371152b700043d807c4f269e41b225873be372417f8f5aac00d2cee5d07f0edac1dc6d6f77b63b372f931ded0afa47ddd2ee1d9a83776397c29989b75b398ca1a51175c30ac70105d15c6fb69cc99df796feb08a7c2e6c1c28a4fdc93d2672a42858c797f23e5831529a3e152e814ecfc63f10f1d57887256475d3cda24c22eefff387411b610e3d0107ad3d9208100418b6d264da75b25044e8dd122d92b5f85e1a8ecb6a0f5a988efc465db0ea31b8274d7957618111899799c17c93a4a3752352eb4cb462ae0c915a0108800de72a5099a2c973ac9d4dd8a3a820ac574bf7a5826e7a8eec1f268bff39da59e86e7e5360ffc5b61591a40497fb6093021926ab5fa7499ea4f43fdbccaa0f01091f167188bfc31b575948edbe5adbf1c532226f0baad6973c34f57d5b81a54dde3c5826db5b543442e17ba8fc3e3524dbba0a9abbbde8b4808078702c0c5fced2010a4e1f40f6ca78ff3132b9acd9596f4d4257bdbceb34beac05d8fac07e23e5a62325621f1cb74a0a39850621a7b81d5fd08fe56c88257a4bc2b4f395145f37221a000cfacd7f14083511dff87b62f41d35a02c8d379927f431d8aa4f7cf2c594566ed30f1f951d3687bb7b86bb05726f1302292889c32376e2eb3349c3fbbb2619debd010d76d65f7e10775e5a76f50913042abac05261ae233451c6db1b0c4e7ca9311b4a05f2d67f65fa9766f6c657a92f384f47199eadce527b0bccf6378e5b21fef86b00109acaef1f871dc6223b7a3a6c628506b29b117023718e0b34aee888ad7eabdf1552b734d16c52350b9c2250d713f7f50476a370c47c0172a4510c0efd409602af0111b0dad38dfe1ae34cd8651d17a762459b9e2444362eee6fdabeb8e8ba08b5c37c120fe7d58ef31267238afae085e6c46f35996d6700739372f7940eab4c9b6b9c0112c682c5b9b09a22e7330c9646fcc88c52e6016510450c02e1d2fb3c9db43302604f7fd50249074fd61e6fa21ee2ee42b421ff22ba6f2e9feb8a0be3edd3c0023b010000000017c4c6a400010000000000000000000000000000000000000000000000000000000000000004562b3615eb987f3420000000000000000000000000000000000000000000546f6b656e42726964676501000000180000000000000000000000001d68124e65fafc907325e3edbf8c4d84499daa8b +XPLA (28) Token Bridge,01000000030f00e94b4808ace06e1795157c10dd9e23f3936e326136f179a3fb7b00a18415f5a361c72b195a76d51928b1ed721bad2ef63f509ac852fd46f83fb5800cf2c9fbfc01036ca0758b0992c1a5f18cb5bacce13db20c18e03946084c36478e905d6e36e30b6ef593515ae8b0768a59c4d2632d52aefb8e4921449ba6268bb447de67e75d0d0104a2292bdc92363c459019a820e8834643f35b1477689e7e4a33e62ca9839e44ab3bc7562f893ed75a64afb245355f5a1155797de999aeda645cce8faf885547a0000523fa45cf92d7eeb11c36343ba1c5e17f99e417bee10d9f3a6b73a0e9b251fd46772fa1687d5d0c41e8480c511292bac1f521ed8d329189747abfce3da1f1128f010783727dd2756b30d7b77cbc179915ee150e9647105002d46e4f1d4ae44b19e7434aeef2bdd46c911fa4fdf01dacab3f80e246b752f24633218d0994adaf9f1cf10008192ab5ea8f2206d5b309a9834cb132067b588ab005bc38e181353b8ca9a3750d21be49e99ff8618a9231bec03e78a332379857384a0a130e93c01e6f6f8e32d300099ac58b0f16b460cc987aee0d06256875bbbd541254aefecf891946d93a8c70080183dc2255710acc87a8ccc1dbd41b14fefde1bbd5af2d28077eef081e8b22da010a20c1f57cc59c9fc896ffb347e0a3d4b8d2d8212448d9519a52b1fc5d0e6087f74913fe890c344c0369b30c41167ca19a405159e3ba0dacaa72406b23b0ee735f000beca147e892548b2b771061fcc7674fc027a4cdf86a273cae32be91d3d84a7620421f91562b00ef96da7c2f2a6e4513f1191a93f56d221d52724f3b3b7edf3f48010c24d08d8a33663ce9756c69c744b47ffee750d0f7f1c1a3cea105873f0c0a0279563f4b92b130ae3debd73c7342461d7d177ec9a9fe45d2b6175ba106ec292c86010de1694567f84b429c0fdeee88b0d65b8fbe0b1eb0f9574d6b6d99001bc8532d79203571d8360ef565c7108a25e2f7ade1d61cf808c0a2b436eb86ecd510e652c5010e7079a3a6c9f32ce20e726463d03aa650e81a677d452f76787e639984d6cf3b3e2cfea642b08c8ae6172e5e2f6c6114b787c6d9b2770ad17d1edad2917334bce8010f86d02cdc1cef14d43ddcfb47c9a16020da4feba940b4cfffa474365a3d9e31fa14dd4b10026415178ca4bf9449b9b30f86b4d79664e2f4abbcf183f221539286011136426e8ab92889c310079e1c85e5249d8ff5de32a9576edfefa4950379d6b2002c1ddeb03682d5b68e69b1c1a5aaf54e1763e6ac0ed1eaabdf8d90ab97e432a40012454e859e894e375defb136926a692171a3ddca9ef069ea1cd0501536cbcb3cfb1a4a700a837205237ce44393ed2cf2110592020a11c79ea27d2c3c09095c537e0000000000bb75823600010000000000000000000000000000000000000000000000000000000000000004c55a1c78b494ff3120000000000000000000000000000000000000000000546f6b656e427269646765010000001c8f9cf727175353b17a5f574270e370776123d90fd74956ae4277962b4fdee24c +Sui (21) Token Bridge,01000000030d00a42d36048a27a763413bfa1d261daeb86f7feed7a9850d9915d8c44acee97dd4327c0351648ac4d496050e6af6534badd2b86416a5675f373e5a5ebf876505700002349ec8ae9aeef147e87b2b06a475d6ec760c20680285ecdd6b7b400cbd67504232e0cae965d27dc9f0a83fbed40ec0a759bd7ad9a014a69c90364a1df88048dc000323d01a78c01887d981e22ffb0f91d02c1d7f015393513db3f215af64bd9cb74829eb08fc9895f8b1247933dd23c2b71ac1bf5bbefb8bea829ab2f6b94317eaa901040305b91b17227313395a861365c8b110414e961ccf25a2b645226e6a307e488f6dace0896c354425f5dfb6b0cb968ef1f752653cd85179b9344f215b555601dd01060dcfd80b54b6f43e502b9628a5a2b92b453fa96718397ecbe0e279495ff37c0835fff5ee7f3b749de287a0c3440105ddd705d06975d56384792373645b7787df000715985ff2cf28e3a8062d9ffc7ef69fbd7c56082f0938cc564586d0dbcacf14986579e7d7a8629dc8a9cfcbf0c97c46ae6492d05c5fba193400746a43b6f1123d000a5db8adfca6d43dd345e130fa0cae250ac7cfa364a29d47cf219ce2b50d6f930a0e1bc399b5b92cdec3d00fcf6f2c7f0732996344812dae85afcfa077c67d94b8010bf68fe7c2ed3aaa180b01ba28052fa63d72509e642bf45f8b5c14c582d8e6eb99514d41ab7fd3f6451470e02a054a3630e347020b6330a8ec23efdc3e4da4550b010d7f3ec58dbb8ae21a2fb71941ed80d646469f1992e7fdc32706c327bfbe01b98011e8b377fce487237f9238fe9af09991f5da11d85aba5a4a81e99df8d066aead010e18e7de979a55bd568b26754fdd7d9e7b03572d742e5657f944ab35b44398a40e07a0c2399e13a244277138375e7e980bf6b666f39bc2f86afd2605f0249a5a53000f77d089279a354b7faa1f3fdc084f6e0ef684d9bcce8d9fb11b5568c0d0b215f15d54cc4383e1b7112fadc238f750b885f5f81a21f84e00ec4487a8064386cc2e01106d9d3067e19413e985f76852eb0cdd071fef659540ddf3a9d5610d492a68a13c61cd109f64c977c1274f9782dcbddfa46ee94331e02f98ba8fb37e22300bd63e0111819a499e30feb82190736054d2993918aeb591e3098b4df77630e93512fec4c122f2cccfe88f2b735f42a06571944d800f3dfcb07de7956330515ddb3c9a41360000000000764b7752000100000000000000000000000000000000000000000000000000000000000000047654167e9520c1b820000000000000000000000000000000000000000000546f6b656e4272696467650100000015ccceeb29348f71bdd22ffef43a2a19c1f5b5e17c5cca5411529120182672ade5 +Sei (32) Token Bridge,01000000030d03e6be465d35dbbcc957b5803283ee89586e56d0ff29f0a530d4fb96fdc69028221623127de2d6115fe060f55840053f1f9c73b8d4f5bf8043cbe9a77ad5f428fc00046ae1f149be927d2dd8cd4b5d38c9aa0d6b4e2883dd52e742fea2d20e22af25e10581732281dcf59e97755219ae64e408ee6be20c68059da62dbd8e6f86690da0000539b965de3dcfd312cfe5a1b1b3999d299b83d263b8817278bc7109c061649c513582c8ab46f819cc98f243e3b0b8dd543b7f1263d443dbef34ea1e9c023159380006dde501b50c618965ab1766d896d9b919effc337136a72795cf0c47a5460286c4006685ab5da24c21f8558c1a392b62291e0172deafaf486918b01f6ffc73d420010839a1b899424789485a8e2f5cfe59bd731ca62a37228ee7bd09252aa11e6995f93c650360e66ef878cdafb6feca334b0a4d69dadf802a8fd0e228ed0af5467d1701093ac07f14460a31e577bf0910718ee0dc4a28743d24c61e58e1e4f275ae05bb16238043863aa160e522b3a32ac276584129e98a6a21490ea26e0e60c9a71d6f4f010adb8ed8e54ab1f9f7a7073a0135bbdde881d2baf9699076ca15d0fc0438dc4a681d27659205fc3075b414881b602731fd6d53a2eba8c92c78337e8ccc00b325ab010bcc9c74a50ff0243ae8c3189449ca86ce66ede52a495b53d7461e4001b75e762e55a9ce5256b4d145189027c32f85d56a0a891214afaa521607858b9d7a8e043b010cd70ded635f6a295b4697cfe25adb8c10223e2253d0637ef7a0bba951e34f62bf0216e186e406be8ab707fdbab7cc5673c0de3177cbdbc6ca9591b27b5fed37d0000da39343ac0b8551d7bf5aaf4a2fb4c606649783ab2fa803012174a9974da5b8ca7c0e1633e35ccf8d539831df9636ddc613e654e7b3e207265b3457a6e6d81cae000e5d70e2931e4f223d5b06f87706f308c933f6247ffc3b261e8a9f5c75faddfa7c66a452ed16337b0bac026dd5974a82ea71ba32975ecd1cd490eee3326330a2b90010784a772c9a9d3ae20d3114c74c6eecaa3ff467b8e7f3199d0bd0d45bcc719a26579d6fdcd69f02d044e45118c94c92224a2da9a18f9891d82818fc170c2838ac0012cd71e7f13c2122ddae87806b2c531cb8ed6c7cb0df5243413f751914ffc46780294d91d651d4f76efd5fdffdd3859053c4204bce0fe63b9c3d88e5438a5a790701000000009f745aeb00010000000000000000000000000000000000000000000000000000000000000004a69638d9f869868020000000000000000000000000000000000000000000546f6b656e427269646765010000002086c5fd957e2db8389553e1728f9c27964b22a8154091ccba54d75f4b10c61f5e \ No newline at end of file diff --git a/devnet/node.yaml b/devnet/node.yaml index b681f19675..10bc376b82 100644 --- a/devnet/node.yaml +++ b/devnet/node.yaml @@ -103,6 +103,8 @@ spec: - ws://eth-devnet:8545 - --neonRPC - ws://eth-devnet:8545 + - --baseRPC + - ws://eth-devnet:8545 # - --wormchainURL # - wormchain:9090 # - --wormchainKeyPath diff --git a/ethereum/.env.base.mainnet b/ethereum/.env.base.mainnet new file mode 100644 index 0000000000..9d75cba814 --- /dev/null +++ b/ethereum/.env.base.mainnet @@ -0,0 +1,16 @@ +# Base mainnet env +# Rename to .env to use with truffle migrations + +# Wormhole Core Migrations +INIT_SIGNERS=["0x58CC3AE5C097b213cE3c81979e1B9f9570746AA5"] +INIT_CHAIN_ID=30 +INIT_GOV_CHAIN_ID=0x1 +INIT_GOV_CONTRACT=0x0000000000000000000000000000000000000000000000000000000000000004 +INIT_EVM_CHAIN_ID=8453 + +# Bridge Migrations +BRIDGE_INIT_CHAIN_ID=30 +BRIDGE_INIT_GOV_CHAIN_ID=0x1 +BRIDGE_INIT_GOV_CONTRACT=0x0000000000000000000000000000000000000000000000000000000000000004 +BRIDGE_INIT_WETH=0x4200000000000000000000000000000000000006 +BRIDGE_INIT_FINALITY=1 diff --git a/ethereum/truffle-config.js b/ethereum/truffle-config.js index a9f7eac006..d4cbab6d3a 100644 --- a/ethereum/truffle-config.js +++ b/ethereum/truffle-config.js @@ -3,7 +3,8 @@ const HDWalletProvider = require("@truffle/hdwallet-provider"); const KLAYHDWalletProvider = require("truffle-hdwallet-provider-klaytn"); module.exports = { - contracts_directory: "contracts/{*.sol,bridge/{*.sol,interfaces/*.sol,token/*.sol,mock/*.sol,utils/*.sol},interfaces/IWormhole.sol,mock/*.sol,nft/{*.sol,interfaces/*.sol,token/*.sol,mock/*.sol}}", + contracts_directory: + "contracts/{*.sol,bridge/{*.sol,interfaces/*.sol,token/*.sol,mock/*.sol,utils/*.sol},interfaces/IWormhole.sol,mock/*.sol,nft/{*.sol,interfaces/*.sol,token/*.sol,mock/*.sol}}", networks: { development: { host: "127.0.0.1", @@ -347,6 +348,15 @@ module.exports = { }, network_id: 77, }, + base: { + provider: () => { + return new HDWalletProvider( + process.env.MNEMONIC, + "https://developer-access-mainnet.base.org" + ); + }, + network_id: 8453, + }, base_testnet: { provider: () => { return new HDWalletProvider( diff --git a/node/cmd/guardiand/node.go b/node/cmd/guardiand/node.go index 3f8f6baa1f..20ceb8d120 100644 --- a/node/cmd/guardiand/node.go +++ b/node/cmd/guardiand/node.go @@ -611,6 +611,10 @@ func runNode(cmd *cobra.Command, args []string) { logger.Fatal("Both --optimismContract and --optimismRPC must be set together or both unset") } + if (*baseRPC == "") != (*baseContract == "") { + logger.Fatal("Both --baseContract and --baseRPC must be set together or both unset") + } + if *testnetMode { if *neonRPC == "" { logger.Fatal("Please specify --neonRPC") @@ -618,12 +622,6 @@ func runNode(cmd *cobra.Command, args []string) { if *neonContract == "" { logger.Fatal("Please specify --neonContract") } - if *baseRPC == "" { - logger.Fatal("Please specify --baseRPC") - } - if *baseContract == "" { - logger.Fatal("Please specify --baseContract") - } if *sepoliaRPC == "" { logger.Fatal("Please specify --sepoliaRPC") } @@ -637,12 +635,6 @@ func runNode(cmd *cobra.Command, args []string) { if *neonContract != "" && !*unsafeDevMode { logger.Fatal("Please do not specify --neonContract") } - if *baseRPC != "" && !*unsafeDevMode { - logger.Fatal("Please do not specify --baseRPC") - } - if *baseContract != "" && !*unsafeDevMode { - logger.Fatal("Please do not specify --baseContract") - } if *sepoliaRPC != "" && !*unsafeDevMode { logger.Fatal("Please do not specify --sepoliaRPC") } @@ -1178,6 +1170,17 @@ func runNode(cmd *cobra.Command, args []string) { watcherConfigs = append(watcherConfigs, wc) } + if shouldStart(baseRPC) { + wc := &evm.WatcherConfig{ + NetworkID: "base", + ChainID: vaa.ChainIDBase, + Rpc: *baseRPC, + Contract: *baseContract, + } + + watcherConfigs = append(watcherConfigs, wc) + } + if shouldStart(terraWS) { wc := &cosmwasm.WatcherConfig{ NetworkID: "terra", @@ -1328,17 +1331,6 @@ func runNode(cmd *cobra.Command, args []string) { watcherConfigs = append(watcherConfigs, wc) } - if shouldStart(baseRPC) { - wc := &evm.WatcherConfig{ - NetworkID: "base", - ChainID: vaa.ChainIDBase, - Rpc: *baseRPC, - Contract: *baseContract, - } - - watcherConfigs = append(watcherConfigs, wc) - } - if shouldStart(sepoliaRPC) { wc := &evm.WatcherConfig{ NetworkID: "sepolia", diff --git a/node/hack/repair_eth/repair_eth.go b/node/hack/repair_eth/repair_eth.go index 56df86fe74..91d3b8b125 100644 --- a/node/hack/repair_eth/repair_eth.go +++ b/node/hack/repair_eth/repair_eth.go @@ -45,6 +45,7 @@ var etherscanAPIMap = map[vaa.ChainID]string{ vaa.ChainIDMoonbeam: "https://api-moonbeam.moonscan.io", vaa.ChainIDArbitrum: "https://api.arbiscan.io", vaa.ChainIDOptimism: "https://api-optimistic.etherscan.io", + vaa.ChainIDBase: "https://api.basescan.org", } var coreContractMap = map[vaa.ChainID]string{ @@ -62,6 +63,7 @@ var coreContractMap = map[vaa.ChainID]string{ vaa.ChainIDMoonbeam: strings.ToLower("0xC8e2b0cD52Cf01b0Ce87d389Daa3d414d4cE29f3"), vaa.ChainIDArbitrum: strings.ToLower("0xa5f208e072434bC67592E4C49C1B991BA79BCA46"), vaa.ChainIDOptimism: strings.ToLower("0xEe91C335eab126dF5fDB3797EA9d6aD93aeC9722"), + vaa.ChainIDBase: strings.ToLower("0xbebdb6C8ddC678FfA9f8748f85C815C556Dd8ac6"), } var ( diff --git a/node/pkg/governor/mainnet_chains.go b/node/pkg/governor/mainnet_chains.go index 18bc637c6d..3b2b385d4b 100644 --- a/node/pkg/governor/mainnet_chains.go +++ b/node/pkg/governor/mainnet_chains.go @@ -30,6 +30,7 @@ func chainList() []chainConfigEntry { chainConfigEntry{emitterChainID: vaa.ChainIDMoonbeam, dailyLimit: 5_000_000, bigTransactionSize: 500_000}, chainConfigEntry{emitterChainID: vaa.ChainIDArbitrum, dailyLimit: 200_000, bigTransactionSize: 20_000}, chainConfigEntry{emitterChainID: vaa.ChainIDOptimism, dailyLimit: 200_000, bigTransactionSize: 20_000}, + chainConfigEntry{emitterChainID: vaa.ChainIDBase, dailyLimit: 200_000, bigTransactionSize: 20_000}, chainConfigEntry{emitterChainID: vaa.ChainIDAptos, dailyLimit: 5_000_000, bigTransactionSize: 500_000}, chainConfigEntry{emitterChainID: vaa.ChainIDXpla, dailyLimit: 200_000, bigTransactionSize: 20_000}, chainConfigEntry{emitterChainID: vaa.ChainIDSui, dailyLimit: 5_000_000, bigTransactionSize: 500_000}, diff --git a/node/pkg/governor/mainnet_tokens_test.go b/node/pkg/governor/mainnet_tokens_test.go index 4c09ce7ee9..89f54b5803 100644 --- a/node/pkg/governor/mainnet_tokens_test.go +++ b/node/pkg/governor/mainnet_tokens_test.go @@ -14,7 +14,7 @@ func TestTokenListSize(t *testing.T) { /* Assuming that governed tokens will need to be updated every time we regenerate it */ - assert.Equal(t, 804, len(tokenConfigEntries)) + assert.Equal(t, 805, len(tokenConfigEntries)) } func TestTokenListAddressSize(t *testing.T) { diff --git a/node/pkg/governor/manual_tokens.go b/node/pkg/governor/manual_tokens.go index 34df415575..d6d58a5c80 100644 --- a/node/pkg/governor/manual_tokens.go +++ b/node/pkg/governor/manual_tokens.go @@ -15,6 +15,7 @@ func manualTokenList() []tokenConfigEntry { {chain: 19, addr: "017038850bf3af746c36803cce35009268f00d22ae2b55ffb59ac5f2a6add40b", symbol: "INJ", coinGeckoId: "injective-protocol", decimals: 8, price: 1.64}, {chain: 21, addr: "9258181f5ceac8dbffb7030890243caed69a9599d2886d957a9cb7656af3bdb3", symbol: "Sui", coinGeckoId: "sui", decimals: 9, price: 0.1}, {chain: 2, addr: "00000000000000000000000018084fba666a33d37592fa2633fd49a74dd93a88", symbol: "tBTC", coinGeckoId: "tbtc", decimals: 18, price: 26553}, + {chain: 30, addr: "0000000000000000000000004200000000000000000000000000000000000006", symbol: "WETH", coinGeckoId: "weth", decimals: 18, price: 1994.41}, {chain: 32, addr: "00881043998ff2b738519d444d2dd0da3da4545de08290c1076746538d5333df", symbol: "Sei", coinGeckoId: "sei-network", decimals: 6, price: 0.0}, } } diff --git a/sdk/js/src/utils/consts.ts b/sdk/js/src/utils/consts.ts index 76e0debd44..b7d5e8c80e 100644 --- a/sdk/js/src/utils/consts.ts +++ b/sdk/js/src/utils/consts.ts @@ -253,9 +253,9 @@ const MAINNET = { nft_bridge: undefined, }, base: { - core: undefined, - token_bridge: undefined, - nft_bridge: undefined, + core: "0xbebdb6C8ddC678FfA9f8748f85C815C556Dd8ac6", + token_bridge: "0x8d2de8d2f73F1F4cAB472AC9A881C9b123C79627", + nft_bridge: "0xDA3adC6621B2677BEf9aD26598e6939CF0D92f88", }, sei: { core: "sei1gjrrme22cyha4ht2xapn3f08zzw6z3d4uxx6fyy9zd5dyr3yxgzqqncdqn", diff --git a/sdk/mainnet_consts.go b/sdk/mainnet_consts.go index a7c97244a2..18def13d88 100644 --- a/sdk/mainnet_consts.go +++ b/sdk/mainnet_consts.go @@ -116,6 +116,7 @@ var knownTokenbridgeEmitters = map[vaa.ChainID]string{ vaa.ChainIDMoonbeam: "000000000000000000000000B1731c586ca89a23809861c6103F0b96B3F57D92", vaa.ChainIDArbitrum: "0000000000000000000000000b2402144Bb366A632D14B83F244D2e0e21bD39c", vaa.ChainIDOptimism: "0000000000000000000000001D68124e65faFC907325e3EDbF8c4d84499DAa8b", + vaa.ChainIDBase: "0000000000000000000000008d2de8d2f73F1F4cAB472AC9A881C9b123C79627", vaa.ChainIDXpla: "8f9cf727175353b17a5f574270e370776123d90fd74956ae4277962b4fdee24c", vaa.ChainIDInjective: "00000000000000000000000045dbea4617971d93188eda21530bc6503d153313", vaa.ChainIDSui: "ccceeb29348f71bdd22ffef43a2a19c1f5b5e17c5cca5411529120182672ade5", @@ -140,6 +141,7 @@ var knownNFTBridgeEmitters = map[vaa.ChainID]string{ vaa.ChainIDMoonbeam: "000000000000000000000000453cfBe096C0f8D763E8C5F24B441097d577bdE2", vaa.ChainIDArbitrum: "0000000000000000000000003dD14D553cFD986EAC8e3bddF629d82073e188c8", vaa.ChainIDOptimism: "000000000000000000000000fE8cD454b4A1CA468B57D79c0cc77Ef5B6f64585", + vaa.ChainIDBase: "000000000000000000000000DA3adC6621B2677BEf9aD26598e6939CF0D92f88", vaa.ChainIDAptos: "0000000000000000000000000000000000000000000000000000000000000005", } From 14a1251c06b3d837dcbd2b7bed5b1abae6eb7d02 Mon Sep 17 00:00:00 2001 From: chase-45 <88348425+chase-45@users.noreply.github.com> Date: Tue, 18 Jul 2023 12:16:51 -0400 Subject: [PATCH 02/23] ethereum: pin ethereum docker forge evm version to london (#3120) --- ethereum/foundry.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/ethereum/foundry.toml b/ethereum/foundry.toml index 911bfc55ca..32faa6f4b1 100644 --- a/ethereum/foundry.toml +++ b/ethereum/foundry.toml @@ -7,6 +7,7 @@ src = "contracts" # We put the tests into the forge-test directory (instead of test) so that # truffle doesn't try to build them test = "forge-test" +evm_version = "london" out = 'build-forge' libs = ['lib', 'node_modules'] From 9c668072ef386842490145f224fb0cfe2dc1f0ac Mon Sep 17 00:00:00 2001 From: tbjump Date: Tue, 27 Jun 2023 13:36:52 +0000 Subject: [PATCH 03/23] node/p2p: Warn about channel overflows in GoTest mode --- node/pkg/node/options.go | 4 ++++ node/pkg/p2p/p2p.go | 46 ++++++++++++++++++++++++++-------------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/node/pkg/node/options.go b/node/pkg/node/options.go index 0ec4a26bda..185919bfdd 100644 --- a/node/pkg/node/options.go +++ b/node/pkg/node/options.go @@ -46,6 +46,10 @@ func GuardianOptionP2P(p2pKey libp2p_crypto.PrivKey, networkId string, bootstrap components := p2p.DefaultComponents() components.Port = port + if g.env == common.GoTest { + components.WarnChannelOverflow = true + } + g.runnables["p2p"] = p2p.Run( g.obsvC, g.obsvReqC.writeC, diff --git a/node/pkg/p2p/p2p.go b/node/pkg/p2p/p2p.go index a2816a059e..02ceb78a23 100644 --- a/node/pkg/p2p/p2p.go +++ b/node/pkg/p2p/p2p.go @@ -3,6 +3,7 @@ package p2p import ( "context" "crypto/ecdsa" + "encoding/hex" "errors" "fmt" "strings" @@ -10,10 +11,10 @@ import ( "time" "github.com/certusone/wormhole/node/pkg/accountant" - node_common "github.com/certusone/wormhole/node/pkg/common" + "github.com/certusone/wormhole/node/pkg/common" "github.com/certusone/wormhole/node/pkg/governor" "github.com/certusone/wormhole/node/pkg/version" - "github.com/ethereum/go-ethereum/common" + eth_common "github.com/ethereum/go-ethereum/common" ethcrypto "github.com/ethereum/go-ethereum/crypto" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" @@ -71,11 +72,11 @@ var signedObservationRequestPrefix = []byte("signed_observation_request|") // heartbeatMaxTimeDifference specifies the maximum time difference between the local clock and the timestamp in incoming heartbeat messages. Heartbeats that are this old or this much into the future will be dropped. This value should encompass clock skew and network delay. var heartbeatMaxTimeDifference = time.Minute * 15 -func heartbeatDigest(b []byte) common.Hash { +func heartbeatDigest(b []byte) eth_common.Hash { return ethcrypto.Keccak256Hash(append(heartbeatMessagePrefix, b...)) } -func signedObservationRequestDigest(b []byte) common.Hash { +func signedObservationRequestDigest(b []byte) eth_common.Hash { return ethcrypto.Keccak256Hash(append(signedObservationRequestPrefix, b...)) } @@ -88,10 +89,12 @@ type Components struct { // ConnMgr is the ConnectionManager that the Guardian is going to use ConnMgr *connmgr.BasicConnMgr // ProtectedHostByGuardianKey is used to ensure that only one p2p peer can be protected by any given known guardian key - ProtectedHostByGuardianKey map[common.Address]peer.ID + ProtectedHostByGuardianKey map[eth_common.Address]peer.ID // ProtectedHostByGuardianKeyLock is only useful to prevent a race condition in test as ProtectedHostByGuardianKey // is only accessed by a single routine at any given time in a running Guardian. ProtectedHostByGuardianKeyLock sync.Mutex + // WarnChannelOverflow: If true, errors due to overflowing channels will produce logger.Warn + WarnChannelOverflow bool } func (f *Components) ListeningAddresses() []string { @@ -118,7 +121,7 @@ func DefaultComponents() *Components { }, Port: DefaultPort, ConnMgr: mgr, - ProtectedHostByGuardianKey: make(map[common.Address]peer.ID), + ProtectedHostByGuardianKey: make(map[eth_common.Address]peer.ID), } } @@ -184,7 +187,7 @@ func Run( signedInC chan<- *gossipv1.SignedVAAWithQuorum, priv crypto.PrivKey, gk *ecdsa.PrivateKey, - gst *node_common.GuardianSetState, + gst *common.GuardianSetState, networkID string, bootstrapPeers string, nodeName string, @@ -539,7 +542,7 @@ func Run( zap.Binary("raw", envelope.Data), zap.String("from", envelope.GetFrom().String())) } else { - guardianAddr := common.BytesToAddress(s.GuardianAddr) + guardianAddr := eth_common.BytesToAddress(s.GuardianAddr) prevPeerId, ok := components.ProtectedHostByGuardianKey[guardianAddr] if ok { if prevPeerId != peerId { @@ -569,6 +572,9 @@ func Run( case obsvC <- m.SignedObservation: p2pMessagesReceived.WithLabelValues("observation").Inc() default: + if components.WarnChannelOverflow { + logger.Warn("Ignoring SignedObservation because obsvC full", zap.String("hash", hex.EncodeToString(m.SignedObservation.Hash))) + } p2pReceiveChannelOverflow.WithLabelValues("observation").Inc() } case *gossipv1.GossipMessage_SignedVaaWithQuorum: @@ -576,6 +582,14 @@ func Run( case signedInC <- m.SignedVaaWithQuorum: p2pMessagesReceived.WithLabelValues("signed_vaa_with_quorum").Inc() default: + if components.WarnChannelOverflow { + // TODO do not log this in production + var hexStr string + if vaa, err := vaa.Unmarshal(m.SignedVaaWithQuorum.Vaa); err == nil { + hexStr = vaa.HexDigest() + } + logger.Warn("Ignoring SignedVaaWithQuorum because signedInC full", zap.String("hash", hexStr)) + } p2pReceiveChannelOverflow.WithLabelValues("signed_vaa_with_quorum").Inc() } case *gossipv1.GossipMessage_SignedObservationRequest: @@ -649,10 +663,10 @@ func createSignedHeartbeat(gk *ecdsa.PrivateKey, heartbeat *gossipv1.Heartbeat) } } -func processSignedHeartbeat(from peer.ID, s *gossipv1.SignedHeartbeat, gs *node_common.GuardianSet, gst *node_common.GuardianSetState, disableVerify bool) (*gossipv1.Heartbeat, error) { - envelopeAddr := common.BytesToAddress(s.GuardianAddr) +func processSignedHeartbeat(from peer.ID, s *gossipv1.SignedHeartbeat, gs *common.GuardianSet, gst *common.GuardianSetState, disableVerify bool) (*gossipv1.Heartbeat, error) { + envelopeAddr := eth_common.BytesToAddress(s.GuardianAddr) idx, ok := gs.KeyIndex(envelopeAddr) - var pk common.Address + var pk eth_common.Address if !ok { if !disableVerify { return nil, fmt.Errorf("invalid message: %s not in guardian set", envelopeAddr) @@ -673,7 +687,7 @@ func processSignedHeartbeat(from peer.ID, s *gossipv1.SignedHeartbeat, gs *node_ return nil, errors.New("failed to recover public key") } - signerAddr := common.BytesToAddress(ethcrypto.Keccak256(pubKey[1:])[12:]) + signerAddr := eth_common.BytesToAddress(ethcrypto.Keccak256(pubKey[1:])[12:]) if pk != signerAddr && !disableVerify { return nil, fmt.Errorf("invalid signer: %v", signerAddr) } @@ -702,10 +716,10 @@ func processSignedHeartbeat(from peer.ID, s *gossipv1.SignedHeartbeat, gs *node_ return &h, nil } -func processSignedObservationRequest(s *gossipv1.SignedObservationRequest, gs *node_common.GuardianSet) (*gossipv1.ObservationRequest, error) { - envelopeAddr := common.BytesToAddress(s.GuardianAddr) +func processSignedObservationRequest(s *gossipv1.SignedObservationRequest, gs *common.GuardianSet) (*gossipv1.ObservationRequest, error) { + envelopeAddr := eth_common.BytesToAddress(s.GuardianAddr) idx, ok := gs.KeyIndex(envelopeAddr) - var pk common.Address + var pk eth_common.Address if !ok { return nil, fmt.Errorf("invalid message: %s not in guardian set", envelopeAddr) } else { @@ -724,7 +738,7 @@ func processSignedObservationRequest(s *gossipv1.SignedObservationRequest, gs *n return nil, errors.New("failed to recover public key") } - signerAddr := common.BytesToAddress(ethcrypto.Keccak256(pubKey[1:])[12:]) + signerAddr := eth_common.BytesToAddress(ethcrypto.Keccak256(pubKey[1:])[12:]) if pk != signerAddr { return nil, fmt.Errorf("invalid signer: %v", signerAddr) } From 2429f51ee3212da21d5990966bf583593f41e404 Mon Sep 17 00:00:00 2001 From: tbjump Date: Tue, 27 Jun 2023 13:39:25 +0000 Subject: [PATCH 04/23] node: Add benchmarks --- node/pkg/node/node_test.go | 399 ++++++++++++++++++++++++++++++------- 1 file changed, 329 insertions(+), 70 deletions(-) diff --git a/node/pkg/node/node_test.go b/node/pkg/node/node_test.go index ed53176245..5290d5ed61 100644 --- a/node/pkg/node/node_test.go +++ b/node/pkg/node/node_test.go @@ -7,6 +7,7 @@ import ( "crypto/ecdsa" "crypto/rand" "encoding/binary" + "errors" "fmt" "io" "math/big" @@ -19,6 +20,8 @@ import ( "testing" "time" + "sync/atomic" + "github.com/certusone/wormhole/node/pkg/common" "github.com/certusone/wormhole/node/pkg/db" "github.com/certusone/wormhole/node/pkg/devnet" @@ -47,9 +50,9 @@ import ( ) const LOCAL_RPC_PORTRANGE_START = 10000 -const LOCAL_P2P_PORTRANGE_START = 10100 -const LOCAL_STATUS_PORTRANGE_START = 10200 -const LOCAL_PUBLICWEB_PORTRANGE_START = 10300 +const LOCAL_P2P_PORTRANGE_START = 11000 +const LOCAL_STATUS_PORTRANGE_START = 12000 +const LOCAL_PUBLICWEB_PORTRANGE_START = 13000 var PROMETHEUS_METRIC_VALID_HEARTBEAT_RECEIVED = "wormhole_p2p_broadcast_messages_received_total{type=\"valid_heartbeat\"}" @@ -57,7 +60,13 @@ const WAIT_FOR_LOGS = true const WAIT_FOR_METRICS = false // The level at which logs will be written to console; During testing, logs are produced and buffered at Debug level, because some tests need to look for certain entries. -const CONSOLE_LOG_LEVEL = zap.InfoLevel +var CONSOLE_LOG_LEVEL = zap.InfoLevel + +var TEST_ID_CTR atomic.Uint32 + +func getTestId() uint { + return uint(TEST_ID_CTR.Add(1)) +} type mockGuardian struct { p2pKey libp2p_crypto.PrivKey @@ -98,28 +107,32 @@ func mockGuardianSetToGuardianAddrList(gs []*mockGuardian) []eth_common.Address return result } -func mockPublicSocket(mockGuardianIndex uint) string { - return fmt.Sprintf("/tmp/test_guardian_%d_public.socket", mockGuardianIndex) +func mockPublicSocket(testId uint, mockGuardianIndex uint) string { + return fmt.Sprintf("/tmp/test_guardian_%d_public.socket", mockGuardianIndex+testId*20) } -func mockAdminStocket(mockGuardianIndex uint) string { - return fmt.Sprintf("/tmp/test_guardian_%d_admin.socket", mockGuardianIndex) +func mockAdminStocket(testId uint, mockGuardianIndex uint) string { + return fmt.Sprintf("/tmp/test_guardian_%d_admin.socket", mockGuardianIndex+testId*20) } -func mockPublicRpc(mockGuardianIndex uint) string { - return fmt.Sprintf("127.0.0.1:%d", mockGuardianIndex+LOCAL_RPC_PORTRANGE_START) +func mockPublicRpc(testId uint, mockGuardianIndex uint) string { + return fmt.Sprintf("127.0.0.1:%d", mockGuardianIndex+LOCAL_RPC_PORTRANGE_START+testId*20) } -func mockPublicWeb(mockGuardianIndex uint) string { - return fmt.Sprintf("127.0.0.1:%d", mockGuardianIndex+LOCAL_PUBLICWEB_PORTRANGE_START) +func mockPublicWeb(testId uint, mockGuardianIndex uint) string { + return fmt.Sprintf("127.0.0.1:%d", mockGuardianIndex+LOCAL_PUBLICWEB_PORTRANGE_START+testId*20) } -func mockStatusPort(mockGuardianIndex uint) uint { - return mockGuardianIndex + LOCAL_STATUS_PORTRANGE_START +func mockStatusPort(testId uint, mockGuardianIndex uint) uint { + return mockGuardianIndex + LOCAL_STATUS_PORTRANGE_START + testId*20 +} + +func mockP2PPort(testId uint, mockGuardianIndex uint) uint { + return mockGuardianIndex + LOCAL_P2P_PORTRANGE_START + testId*20 } // mockGuardianRunnable returns a runnable that first sets up a mock guardian an then runs it. -func mockGuardianRunnable(gs []*mockGuardian, mockGuardianIndex uint, obsDb mock.ObservationDb) supervisor.Runnable { +func mockGuardianRunnable(testId uint, gs []*mockGuardian, mockGuardianIndex uint, obsDb mock.ObservationDb) supervisor.Runnable { return func(ctx context.Context) error { // Create a sub-context with cancel function that we can pass to G.run. ctx, ctxCancel := context.WithCancel(ctx) @@ -151,15 +164,15 @@ func mockGuardianRunnable(gs []*mockGuardian, mockGuardianIndex uint, obsDb mock if err != nil { return err } - bootstrapPeers := fmt.Sprintf("/ip4/127.0.0.1/udp/%d/quic/p2p/%s", LOCAL_P2P_PORTRANGE_START, zeroPeerId.String()) - p2pPort := uint(LOCAL_P2P_PORTRANGE_START + mockGuardianIndex) + bootstrapPeers := fmt.Sprintf("/ip4/127.0.0.1/udp/%d/quic/p2p/%s", mockP2PPort(testId, 0), zeroPeerId.String()) + p2pPort := mockP2PPort(testId, mockGuardianIndex) // configure publicRpc - publicSocketPath := mockPublicSocket(mockGuardianIndex) - publicRpc := mockPublicRpc(mockGuardianIndex) + publicSocketPath := mockPublicSocket(testId, mockGuardianIndex) + publicRpc := mockPublicRpc(testId, mockGuardianIndex) // configure adminservice - adminSocketPath := mockAdminStocket(mockGuardianIndex) + adminSocketPath := mockAdminStocket(testId, mockGuardianIndex) rpcMap := make(map[string]string) // assemble all the options @@ -171,9 +184,9 @@ func mockGuardianRunnable(gs []*mockGuardian, mockGuardianIndex uint, obsDb mock GuardianOptionP2P(gs[mockGuardianIndex].p2pKey, networkID, bootstrapPeers, nodeName, false, p2pPort, func() string { return "" }), GuardianOptionPublicRpcSocket(publicSocketPath, common.GrpcLogDetailFull), GuardianOptionPublicrpcTcpService(publicRpc, common.GrpcLogDetailFull), - GuardianOptionPublicWeb(mockPublicWeb(mockGuardianIndex), publicSocketPath, "", false, ""), + GuardianOptionPublicWeb(mockPublicWeb(testId, mockGuardianIndex), publicSocketPath, "", false, ""), GuardianOptionAdminService(adminSocketPath, nil, nil, rpcMap), - GuardianOptionStatusServer(fmt.Sprintf("[::]:%d", mockStatusPort(mockGuardianIndex))), + GuardianOptionStatusServer(fmt.Sprintf("[::]:%d", mockStatusPort(testId, mockGuardianIndex))), GuardianOptionProcessor(), } @@ -187,6 +200,9 @@ func mockGuardianRunnable(gs []*mockGuardian, mockGuardianIndex uint, obsDb mock } <-ctx.Done() + time.Sleep(time.Second * 1) // Wait 1s for all sorts of things to complete. + db.Close() // close BadgerDb + return nil } } @@ -224,7 +240,7 @@ func waitForHeartbeatsInLogs(t testing.TB, zapObserver *observer.ObservedLogs, g } } } - time.Sleep(time.Microsecond * 100) + time.Sleep(time.Millisecond) } } @@ -232,14 +248,15 @@ func waitForHeartbeatsInLogs(t testing.TB, zapObserver *observer.ObservedLogs, g // WARNING: Currently, there is only a global registry for all prometheus metrics, leading to all guardian nodes writing to the same one. // // As long as this is the case, you probably don't want to use this function. -func waitForPromMetricGte(t testing.TB, ctx context.Context, gs []*mockGuardian, metric string, min int) { +func waitForPromMetricGte(t testing.TB, testId uint, ctx context.Context, gs []*mockGuardian, metric string, min int) { metricBytes := []byte(metric) requests := make([]*http.Request, len(gs)) + readyFlags := make([]bool, len(gs)) //logger := supervisor.Logger(ctx) // create the prom api clients for i := range gs { - url := fmt.Sprintf("http://localhost:%d/metrics", mockStatusPort(uint(i))) + url := fmt.Sprintf("http://localhost:%d/metrics", mockStatusPort(testId, uint(i))) req, err := http.NewRequestWithContext(ctx, "GET", url, nil) assert.NoError(t, err) requests[i] = req @@ -251,8 +268,8 @@ func waitForPromMetricGte(t testing.TB, ctx context.Context, gs []*mockGuardian, // query them for readyCounter := 0; readyCounter < len(gs); { - for i, g := range gs { - if g.ready { + for i := range gs { + if readyFlags[i] { continue } @@ -279,12 +296,39 @@ func waitForPromMetricGte(t testing.TB, ctx context.Context, gs []*mockGuardian, }() if ready { - g.ready = true + readyFlags[i] = true readyCounter++ } } - time.Sleep(time.Second * 5) + time.Sleep(time.Second * 5) // TODO + } +} + +// waitForVaa polls the publicRpc service every 5ms until there is a response. +func waitForVaa(ctx context.Context, c publicrpcv1.PublicRPCServiceClient, msgId *publicrpcv1.MessageID, mustNotReachQuorum bool) (*publicrpcv1.GetSignedVAAResponse, error) { + var r *publicrpcv1.GetSignedVAAResponse + var err error + + //logger := supervisor.Logger(ctx) + for { + select { + case <-ctx.Done(): + return nil, errors.New("context canceled") + default: + queryCtx, queryCancel := context.WithTimeout(ctx, time.Second) + r, err = c.GetSignedVAA(queryCtx, &publicrpcv1.GetSignedVAARequest{MessageId: msgId}) + queryCancel() + } + if err == nil && r != nil { + // success + return r, err + } + if mustNotReachQuorum { + // no need to re-try because we're expecting an error. + return r, err + } + time.Sleep(time.Millisecond * 10) } } @@ -309,6 +353,8 @@ func randomTime() time.Time { } var someMsgSequenceCounter uint64 = 0 +var someMsgEmitter vaa.Address = [32]byte{1, 2, 3} +var someMsgEmitterChain vaa.ChainID = vaa.ChainIDSolana func someMessage() *common.MessagePublication { someMsgSequenceCounter++ @@ -318,8 +364,8 @@ func someMessage() *common.MessagePublication { Nonce: math_rand.Uint32(), //nolint Sequence: someMsgSequenceCounter, ConsistencyLevel: 1, - EmitterChain: vaa.ChainIDSolana, - EmitterAddress: [32]byte{1, 2, 3}, + EmitterChain: someMsgEmitterChain, + EmitterAddress: someMsgEmitter, Payload: []byte{}, Unreliable: false, } @@ -452,7 +498,7 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -// TestBasicConsensus tests that a set of guardians can form consensus on certain messages and reject certain other messages +// TestConsensus tests that a set of guardians can form consensus on certain messages and reject certain other messages func TestConsensus(t *testing.T) { const numGuardians = 4 // Quorum will be 3 out of 4 guardians. @@ -522,6 +568,7 @@ func testConsensus(t *testing.T, testCases []testCase, numGuardians int) { const guardianSetIndex = 5 // index of the active guardian set (can be anything, just needs to be set to something) const vaaCheckGuardianIndex uint = 0 // we will query this guardian's publicrpc for VAAs const adminRpcGuardianIndex uint = 0 // we will query this guardian's adminRpc + testId := getTestId() // Test's main lifecycle context. rootCtx, rootCtxCancel := context.WithTimeout(context.Background(), testTimeout) @@ -539,7 +586,7 @@ func testConsensus(t *testing.T, testCases []testCase, numGuardians int) { // run the guardians for i := 0; i < numGuardians; i++ { - gRun := mockGuardianRunnable(gs, uint(i), obsDb) + gRun := mockGuardianRunnable(testId, gs, uint(i), obsDb) err := supervisor.Run(ctx, fmt.Sprintf("g-%d", i), gRun) if i == 0 && numGuardians > 1 { time.Sleep(time.Second) // give the bootstrap guardian some time to start up @@ -561,7 +608,7 @@ func testConsensus(t *testing.T, testCases []testCase, numGuardians int) { // wait for the status server to come online and check that it works for i := range gs { - err := testStatusServer(ctx, logger, fmt.Sprintf("http://127.0.0.1:%d/metrics", mockStatusPort(uint(i)))) + err := testStatusServer(ctx, logger, fmt.Sprintf("http://127.0.0.1:%d/metrics", mockStatusPort(testId, uint(i)))) assert.NoError(t, err) } @@ -570,7 +617,7 @@ func testConsensus(t *testing.T, testCases []testCase, numGuardians int) { assert.True(t, WAIT_FOR_LOGS || WAIT_FOR_METRICS) assert.False(t, WAIT_FOR_LOGS && WAIT_FOR_METRICS) // can't do both, because they both write to gs[].ready if WAIT_FOR_METRICS { - waitForPromMetricGte(t, ctx, gs, PROMETHEUS_METRIC_VALID_HEARTBEAT_RECEIVED, 1) + waitForPromMetricGte(t, testId, ctx, gs, PROMETHEUS_METRIC_VALID_HEARTBEAT_RECEIVED, 1) } if WAIT_FOR_LOGS { waitForHeartbeatsInLogs(t, zapObserver, gs) @@ -596,14 +643,14 @@ func testConsensus(t *testing.T, testCases []testCase, numGuardians int) { } // Wait for adminrpc to come online - for zapObserver.FilterMessage("admin server listening on").FilterField(zap.String("path", mockAdminStocket(adminRpcGuardianIndex))).Len() == 0 { + for zapObserver.FilterMessage("admin server listening on").FilterField(zap.String("path", mockAdminStocket(testId, adminRpcGuardianIndex))).Len() == 0 { logger.Info("admin server seems to be offline (according to logs). Waiting 100ms...") time.Sleep(time.Microsecond * 100) } // Send manual re-observation requests func() { // put this in own function to use defer - s := fmt.Sprintf("unix:///%s", mockAdminStocket(vaaCheckGuardianIndex)) + s := fmt.Sprintf("unix:///%s", mockAdminStocket(testId, vaaCheckGuardianIndex)) conn, err := grpc.DialContext(ctx, s, grpc.WithTransportCredentials(insecure.NewCredentials())) require.NoError(t, err) defer conn.Close() @@ -628,14 +675,14 @@ func testConsensus(t *testing.T, testCases []testCase, numGuardians int) { }() // Wait for publicrpc to come online - for zapObserver.FilterMessage("publicrpc server listening").FilterField(zap.String("addr", mockPublicRpc(vaaCheckGuardianIndex))).Len() == 0 { + for zapObserver.FilterMessage("publicrpc server listening").FilterField(zap.String("addr", mockPublicRpc(testId, vaaCheckGuardianIndex))).Len() == 0 { logger.Info("publicrpc seems to be offline (according to logs). Waiting 100ms...") time.Sleep(time.Microsecond * 100) } // check that the VAAs were generated logger.Info("Connecting to publicrpc...") - conn, err := grpc.DialContext(ctx, mockPublicRpc(vaaCheckGuardianIndex), grpc.WithTransportCredentials(insecure.NewCredentials())) + conn, err := grpc.DialContext(ctx, mockPublicRpc(testId, vaaCheckGuardianIndex), grpc.WithTransportCredentials(insecure.NewCredentials())) require.NoError(t, err) defer conn.Close() @@ -650,38 +697,12 @@ func testConsensus(t *testing.T, testCases []testCase, numGuardians int) { logger.Info("Checking result of testcase", zap.Int("test_case", i)) // poll the API until we get a response without error - var r *publicrpcv1.GetSignedVAAResponse - var err error - for { - select { - case <-ctx.Done(): - break - default: - // timeout for grpc query - logger.Info("attempting to query for VAA", zap.Int("test_case", i)) - queryCtx, queryCancel := context.WithTimeout(ctx, time.Second) - r, err = c.GetSignedVAA(queryCtx, &publicrpcv1.GetSignedVAARequest{ - MessageId: &publicrpcv1.MessageID{ - EmitterChain: publicrpcv1.ChainID(msg.EmitterChain), - EmitterAddress: msg.EmitterAddress.String(), - Sequence: msg.Sequence, - }, - }) - queryCancel() - if err != nil { - logger.Info("error querying for VAA. Trying agin in 100ms.", zap.Int("test_case", i), zap.Error(err)) - } - } - if err == nil && r != nil { - logger.Info("Received VAA from publicrpc", zap.Int("test_case", i), zap.Binary("vaa_bytes", r.VaaBytes)) - break - } - if testCase.mustNotReachQuorum { - // no need to re-try because we're expecting an error. (and later we'll assert that's indeed an error) - break - } - time.Sleep(time.Millisecond * 100) + msgId := &publicrpcv1.MessageID{ + EmitterChain: publicrpcv1.ChainID(msg.EmitterChain), + EmitterAddress: msg.EmitterAddress.String(), + Sequence: msg.Sequence, } + r, err := waitForVaa(ctx, c, msgId, testCase.mustNotReachQuorum) assert.NotEqual(t, testCase.mustNotReachQuorum, testCase.mustReachQuorum) // either or if testCase.mustNotReachQuorum { @@ -881,3 +902,241 @@ func (c fatalHook) OnWrite(ce *zapcore.CheckedEntry, fields []zapcore.Field) { c <- sb.String() panic(ce.Message) } + +func signingMsgs(n int) [][]byte { + msgs := make([][]byte, n) + for i := 0; i < len(msgs); i++ { + msgs[i] = ethcrypto.Keccak256Hash([]byte{byte(i)}).Bytes() + } + return msgs +} + +func signMsgsP2p(pk libp2p_crypto.PrivKey, msgs [][]byte) [][]byte { + n := len(msgs) + signatures := make([][]byte, n) + // Ed25519.Sign + for i := 0; i < n; i++ { + sig, err := pk.Sign(msgs[i]) + if err != nil { + panic(err) + } + signatures[i] = sig + } + return signatures +} + +func signMsgsEth(pk *ecdsa.PrivateKey, msgs [][]byte) [][]byte { + n := len(msgs) + signatures := make([][]byte, n) + // Ed25519.Sign + for i := 0; i < n; i++ { + sig, err := ethcrypto.Sign(msgs[i], pk) + if err != nil { + panic(err) + } + signatures[i] = sig + } + return signatures +} + +func BenchmarkCrypto(b *testing.B) { + b.Run("libp2p (Ed25519)", func(b *testing.B) { + + p2pKey := devnet.DeterministicP2PPrivKeyByIndex(1) + + b.Run("sign", func(b *testing.B) { + msgs := signingMsgs(b.N) + b.ResetTimer() + signMsgsP2p(p2pKey, msgs) + }) + + b.Run("verify", func(b *testing.B) { + msgs := signingMsgs(b.N) + signatures := signMsgsP2p(p2pKey, msgs) + b.ResetTimer() + + // Ed25519.Verify + for i := 0; i < b.N; i++ { + ok, err := p2pKey.GetPublic().Verify(msgs[i], signatures[i]) + assert.NoError(b, err) + assert.True(b, ok) + } + }) + }) + + b.Run("ethcrypto (secp256k1)", func(b *testing.B) { + + gk := devnet.InsecureDeterministicEcdsaKeyByIndex(ethcrypto.S256(), 0) + //gkc := ethcrypto.CompressPubkey(&gk.PublicKey) + + b.Run("sign", func(b *testing.B) { + msgs := signingMsgs(b.N) + b.ResetTimer() + signMsgsEth(gk, msgs) + }) + + b.Run("verify", func(b *testing.B) { + msgs := signingMsgs(b.N) + signatures := signMsgsEth(gk, msgs) + b.ResetTimer() + + // Ed25519.Verify + for i := 0; i < b.N; i++ { + _, err := ethcrypto.Ecrecover(msgs[i], signatures[i]) + assert.NoError(b, err) + //assert.Equal(b, signer, gkc) + } + }) + }) +} + +// How to run: go test -v -ldflags '-extldflags "-Wl,--allow-multiple-definition" ' -bench ^BenchmarkConsensus -benchtime=1x -count 1 -run ^$ > bench.log; tail bench.log +func BenchmarkConsensus(b *testing.B) { + require.Equal(b, b.N, 1) + //CONSOLE_LOG_LEVEL = zap.DebugLevel + //CONSOLE_LOG_LEVEL = zap.InfoLevel + CONSOLE_LOG_LEVEL = zap.WarnLevel + benchmarkConsensus(b, "1", 19, 1000, 2) // ~28s + //benchmarkConsensus(b, "1", 19, 100, 2) // ~2s + //benchmarkConsensus(b, "1", 19, 100, 3) // sometimes fails, i.e. too much parallelism + //benchmarkConsensus(b, "1", 19, 100, 10) // sometimes fails, i.e. too much parallelism + //benchmarkConsensus(b, "1", 19, 100, 1) // 3s + //benchmarkConsensus(b, "1", 19, 20, 1) // 0.6s + //benchmarkConsensus(b, "1", 19, 5, 1) // 0.2s +} + +func benchmarkConsensus(t *testing.B, name string, numGuardians int, numMessages int, maxPendingObs int) { + t.Run(name, func(t *testing.B) { + require.Equal(t, t.N, 1) + testId := getTestId() + msgSeqStart := someMsgSequenceCounter + + const testTimeout = time.Minute * 2 + const guardianSetIndex = 5 // index of the active guardian set (can be anything, just needs to be set to something) + + // Test's main lifecycle context. + rootCtx, rootCtxCancel := context.WithTimeout(context.Background(), testTimeout) + defer rootCtxCancel() + + zapLogger, zapObserver := setupLogsCapture() + + supervisor.New(rootCtx, zapLogger, func(ctx context.Context) error { + logger := supervisor.Logger(ctx) + + // create the Guardian Set + gs := newMockGuardianSet(numGuardians) + + var obsDb mock.ObservationDb = nil // TODO + + // run the guardians + for i := 0; i < numGuardians; i++ { + gRun := mockGuardianRunnable(testId, gs, uint(i), obsDb) + err := supervisor.Run(ctx, fmt.Sprintf("g-%d", i), gRun) + if i == 0 && numGuardians > 1 { + time.Sleep(time.Second) // give the bootstrap guardian some time to start up + } + assert.NoError(t, err) + } + logger.Info("All Guardians initiated.") + supervisor.Signal(ctx, supervisor.SignalHealthy) + + // Inform them of the Guardian Set + commonGuardianSet := common.GuardianSet{ + Keys: mockGuardianSetToGuardianAddrList(gs), + Index: guardianSetIndex, + } + for i, g := range gs { + logger.Info("Sending guardian set update", zap.Int("guardian_index", i)) + g.MockSetC <- &commonGuardianSet + } + + // wait for the status server to come online and check that it works + for i := range gs { + err := testStatusServer(ctx, logger, fmt.Sprintf("http://127.0.0.1:%d/metrics", mockStatusPort(testId, uint(i)))) + assert.NoError(t, err) + } + + // Wait for them to connect each other and receive at least one heartbeat. + // This is necessary because if they have not joined the p2p network yet, gossip messages may get dropped silently. + assert.True(t, WAIT_FOR_LOGS || WAIT_FOR_METRICS) + if WAIT_FOR_METRICS { + waitForPromMetricGte(t, testId, ctx, gs, PROMETHEUS_METRIC_VALID_HEARTBEAT_RECEIVED, 1) + } + if WAIT_FOR_LOGS { + waitForHeartbeatsInLogs(t, zapObserver, gs) + } + logger.Info("All Guardians have received at least one heartbeat.") + + // Wait for publicrpc to come online. + for zapObserver.FilterMessage("publicrpc server listening").FilterField(zap.String("addr", mockPublicRpc(testId, 0))).Len() == 0 { + logger.Info("publicrpc seems to be offline (according to logs). Waiting 100ms...") + time.Sleep(time.Microsecond * 100) + } + // now that it's online, connect to publicrpc of guardian-0 + conn, err := grpc.DialContext(ctx, mockPublicRpc(testId, 0), grpc.WithTransportCredentials(insecure.NewCredentials())) + require.NoError(t, err) + defer conn.Close() + c := publicrpcv1.NewPublicRPCServiceClient(conn) + + logger.Info("-----------Beginning benchmark-----------") + t.ResetTimer() + + // nextObsReadyC ensures that there are not more than `maxPendingObs` observations pending at any given point in time. + nextObsReadyC := make(chan struct{}, maxPendingObs) + for j := 0; j < maxPendingObs; j++ { + nextObsReadyC <- struct{}{} + } + + go func() { + // feed observations to nodes + for i := 0; i < numMessages; i++ { + select { + case <-ctx.Done(): + return + case <-nextObsReadyC: + msg := someMessage() + for _, g := range gs { + msgCopy := *msg + g.MockObservationC <- &msgCopy + } + } + } + }() + + // check that the VAAs were generated + for i := 0; i < numMessages; i++ { + msgId := &publicrpcv1.MessageID{ + EmitterChain: publicrpcv1.ChainID(someMsgEmitterChain), + EmitterAddress: someMsgEmitter.String(), + Sequence: msgSeqStart + uint64(i+1), + } + // a VAA should not take longer than 10s to be produced, no matter what. + waitCtx, cancelFunc := context.WithTimeout(ctx, time.Second*10) + _, err := waitForVaa(waitCtx, c, msgId, false) + cancelFunc() + assert.NoError(t, err) + if err != nil { + // early cancel the benchmark + rootCtxCancel() + } + nextObsReadyC <- struct{}{} + } + + // We're done! + logger.Info("Tests completed.") + t.StopTimer() + supervisor.Signal(ctx, supervisor.SignalDone) + rootCtxCancel() + return nil + }, + supervisor.WithPropagatePanic) + + <-rootCtx.Done() + assert.NotEqual(t, rootCtx.Err(), context.DeadlineExceeded) + zapLogger.Info("Test root context cancelled, exiting...") + + // wait for everything to shut down gracefully TODO since switching to portIds by `testId`, this is no longer necessary + //time.Sleep(time.Second * 11) // 11s is needed to gracefully shutdown libp2p + time.Sleep(time.Second * 1) // 1s is needed to gracefully shutdown BadgerDB + }) +} From 36fc45d87b6c71c03d34186898acb35a548ecfa2 Mon Sep 17 00:00:00 2001 From: tbjump Date: Tue, 27 Jun 2023 16:49:13 +0000 Subject: [PATCH 05/23] gitignore: ignore *.prof --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index e22024f31b..a6056a3344 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ bigtable-writer.json /ethereum/cache/ sui.log.* sui/examples/wrapped_coin +*.prof \ No newline at end of file From fe09eb509932afe62f276a78dcee9774b257c0d9 Mon Sep 17 00:00:00 2001 From: tbjump Date: Wed, 5 Jul 2023 22:53:50 +0000 Subject: [PATCH 06/23] node/db: use provided logger for badgerDb --- node/pkg/db/open.go | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/node/pkg/db/open.go b/node/pkg/db/open.go index 90890627da..f7adfd997a 100644 --- a/node/pkg/db/open.go +++ b/node/pkg/db/open.go @@ -1,6 +1,7 @@ package db import ( + "fmt" "os" "path" @@ -8,8 +9,28 @@ import ( "go.uber.org/zap" ) +type badgerZapLogger struct { + *zap.Logger +} + +func (l badgerZapLogger) Errorf(f string, v ...interface{}) { + l.Error(fmt.Sprintf(f, v...)) +} + +func (l badgerZapLogger) Warningf(f string, v ...interface{}) { + l.Warn(fmt.Sprintf(f, v...)) +} + +func (l badgerZapLogger) Infof(f string, v ...interface{}) { + l.Info(fmt.Sprintf(f, v...)) +} + +func (l badgerZapLogger) Debugf(f string, v ...interface{}) { + l.Debug(fmt.Sprintf(f, v...)) +} + func OpenDb(logger *zap.Logger, dataDir *string) *Database { - var options badger.Options + options := badger.DefaultOptions(dbPath) if dataDir != nil { dbPath := path.Join(*dataDir, "db") @@ -22,6 +43,8 @@ func OpenDb(logger *zap.Logger, dataDir *string) *Database { options = badger.DefaultOptions("").WithInMemory(true) } + options = options.WithLogger(badgerZapLogger{logger}) + db, err := badger.Open(options) if err != nil { logger.Fatal("failed to open database", zap.Error(err)) From ddeb78ceb7495061c66b64038effaad435dea114 Mon Sep 17 00:00:00 2001 From: tbjump Date: Wed, 5 Jul 2023 23:03:58 +0000 Subject: [PATCH 07/23] node: increase inboundObservationBufferSize to 5000 --- node/pkg/node/node.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/pkg/node/node.go b/node/pkg/node/node.go index 6e994a0c6a..4bb0fed17b 100644 --- a/node/pkg/node/node.go +++ b/node/pkg/node/node.go @@ -19,7 +19,7 @@ import ( ) const ( - inboundObservationBufferSize = 50 + inboundObservationBufferSize = 5000 inboundSignedVaaBufferSize = 50 observationRequestOutboundBufferSize = 50 observationRequestInboundBufferSize = 50 From d73bce429ac31df892c0a4879b011890a92ef246 Mon Sep 17 00:00:00 2001 From: tbjump Date: Wed, 5 Jul 2023 23:17:23 +0000 Subject: [PATCH 08/23] node/node_test: count amount of generated logs --- node/pkg/db/open.go | 2 +- node/pkg/node/logcounter.go | 42 +++++++++++++++++++++++++++++++++++++ node/pkg/node/node_test.go | 27 ++++++++++++++++-------- 3 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 node/pkg/node/logcounter.go diff --git a/node/pkg/db/open.go b/node/pkg/db/open.go index f7adfd997a..513258db27 100644 --- a/node/pkg/db/open.go +++ b/node/pkg/db/open.go @@ -30,7 +30,7 @@ func (l badgerZapLogger) Debugf(f string, v ...interface{}) { } func OpenDb(logger *zap.Logger, dataDir *string) *Database { - options := badger.DefaultOptions(dbPath) + var options badger.Options if dataDir != nil { dbPath := path.Join(*dataDir, "db") diff --git a/node/pkg/node/logcounter.go b/node/pkg/node/logcounter.go new file mode 100644 index 0000000000..b3759a3db1 --- /dev/null +++ b/node/pkg/node/logcounter.go @@ -0,0 +1,42 @@ +package node + +import ( + "sync/atomic" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +type LogSizeCounter struct { + level zapcore.Level + ctr atomic.Uint64 +} + +func NewLogSizeCounter(lvl zapcore.Level) *LogSizeCounter { + return &LogSizeCounter{ + level: lvl, + } +} + +func (lc *LogSizeCounter) Reset() uint64 { + n := lc.ctr.Load() + lc.ctr.Store(0) + return n +} + +func (lc *LogSizeCounter) Sync() error { return nil } + +func (lc *LogSizeCounter) Write(p []byte) (n int, err error) { + n = len(p) + lc.ctr.Add(uint64(n)) + return n, nil +} + +func (lc *LogSizeCounter) Core() zapcore.Core { + var output zapcore.WriteSyncer = lc + encoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()) + priority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { + return lvl >= lc.level + }) + return zapcore.NewCore(encoder, output, priority) +} diff --git a/node/pkg/node/node_test.go b/node/pkg/node/node_test.go index 5290d5ed61..1798a671eb 100644 --- a/node/pkg/node/node_test.go +++ b/node/pkg/node/node_test.go @@ -175,6 +175,9 @@ func mockGuardianRunnable(testId uint, gs []*mockGuardian, mockGuardianIndex uin adminSocketPath := mockAdminStocket(testId, mockGuardianIndex) rpcMap := make(map[string]string) + // We set this to None because we don't want to count these logs when counting the amount of logs generated per message + publicRpcLogDetail := common.GrpcLogDetailNone + // assemble all the options guardianOptions := []*GuardianOption{ GuardianOptionDatabase(db), @@ -182,8 +185,8 @@ func mockGuardianRunnable(testId uint, gs []*mockGuardian, mockGuardianIndex uin GuardianOptionNoAccountant(), // disable accountant GuardianOptionGovernor(true), GuardianOptionP2P(gs[mockGuardianIndex].p2pKey, networkID, bootstrapPeers, nodeName, false, p2pPort, func() string { return "" }), - GuardianOptionPublicRpcSocket(publicSocketPath, common.GrpcLogDetailFull), - GuardianOptionPublicrpcTcpService(publicRpc, common.GrpcLogDetailFull), + GuardianOptionPublicRpcSocket(publicSocketPath, publicRpcLogDetail), + GuardianOptionPublicrpcTcpService(publicRpc, publicRpcLogDetail), GuardianOptionPublicWeb(mockPublicWeb(testId, mockGuardianIndex), publicSocketPath, "", false, ""), GuardianOptionAdminService(adminSocketPath, nil, nil, rpcMap), GuardianOptionStatusServer(fmt.Sprintf("[::]:%d", mockStatusPort(testId, mockGuardianIndex))), @@ -208,11 +211,12 @@ func mockGuardianRunnable(testId uint, gs []*mockGuardian, mockGuardianIndex uin } // setupLogsCapture is a helper function for making a zap logger/observer combination for testing that certain logs have been made -func setupLogsCapture(options ...zap.Option) (*zap.Logger, *observer.ObservedLogs) { +func setupLogsCapture(options ...zap.Option) (*zap.Logger, *observer.ObservedLogs, *LogSizeCounter) { observedCore, observedLogs := observer.New(zap.DebugLevel) consoleLogger, _ := zap.NewDevelopment(zap.IncreaseLevel(CONSOLE_LOG_LEVEL)) - parentLogger := zap.New(zapcore.NewTee(observedCore, consoleLogger.Core()), options...) - return parentLogger, observedLogs + lc := NewLogSizeCounter(zap.InfoLevel) + parentLogger := zap.New(zapcore.NewTee(observedCore, consoleLogger.Core(), lc.Core()), options...) + return parentLogger, observedLogs, lc } func waitForHeartbeatsInLogs(t testing.TB, zapObserver *observer.ObservedLogs, gs []*mockGuardian) { @@ -574,7 +578,7 @@ func testConsensus(t *testing.T, testCases []testCase, numGuardians int) { rootCtx, rootCtxCancel := context.WithTimeout(context.Background(), testTimeout) defer rootCtxCancel() - zapLogger, zapObserver := setupLogsCapture() + zapLogger, zapObserver, _ := setupLogsCapture() supervisor.New(rootCtx, zapLogger, func(ctx context.Context) error { logger := supervisor.Logger(ctx) @@ -839,7 +843,7 @@ func testGuardianConfigurations(t *testing.T, testCases []testCaseGuardianConfig // The panic will be subsequently caught by the supervisor fatalHook := make(fatalHook) defer close(fatalHook) - zapLogger, zapObserver := setupLogsCapture(zap.WithFatalHook(fatalHook)) + zapLogger, zapObserver, _ := setupLogsCapture(zap.WithFatalHook(fatalHook)) supervisor.New(rootCtx, zapLogger, func(ctx context.Context) error { // Create a sub-context with cancel function that we can pass to G.run. @@ -996,7 +1000,8 @@ func BenchmarkConsensus(b *testing.B) { //CONSOLE_LOG_LEVEL = zap.DebugLevel //CONSOLE_LOG_LEVEL = zap.InfoLevel CONSOLE_LOG_LEVEL = zap.WarnLevel - benchmarkConsensus(b, "1", 19, 1000, 2) // ~28s + //benchmarkConsensus(b, "1", 19, 1000, 2) // ~28s + benchmarkConsensus(b, "1", 19, 100, 1) // ~3s //benchmarkConsensus(b, "1", 19, 100, 2) // ~2s //benchmarkConsensus(b, "1", 19, 100, 3) // sometimes fails, i.e. too much parallelism //benchmarkConsensus(b, "1", 19, 100, 10) // sometimes fails, i.e. too much parallelism @@ -1018,7 +1023,7 @@ func benchmarkConsensus(t *testing.B, name string, numGuardians int, numMessages rootCtx, rootCtxCancel := context.WithTimeout(context.Background(), testTimeout) defer rootCtxCancel() - zapLogger, zapObserver := setupLogsCapture() + zapLogger, zapObserver, setupLogsCapture := setupLogsCapture() supervisor.New(rootCtx, zapLogger, func(ctx context.Context) error { logger := supervisor.Logger(ctx) @@ -1079,6 +1084,7 @@ func benchmarkConsensus(t *testing.B, name string, numGuardians int, numMessages c := publicrpcv1.NewPublicRPCServiceClient(conn) logger.Info("-----------Beginning benchmark-----------") + setupLogsCapture.Reset() t.ResetTimer() // nextObsReadyC ensures that there are not more than `maxPendingObs` observations pending at any given point in time. @@ -1125,6 +1131,9 @@ func benchmarkConsensus(t *testing.B, name string, numGuardians int, numMessages // We're done! logger.Info("Tests completed.") t.StopTimer() + logsize := setupLogsCapture.Reset() + logsize = logsize / uint64(numMessages) / uint64(numGuardians) // normalize + logger.Warn("benchmarkConsensus: logsize report", zap.Uint64("logbytes_per_msg", logsize)) supervisor.Signal(ctx, supervisor.SignalDone) rootCtxCancel() return nil From 7a0c03c846a96a455159db4dde074e8d8abb2a5f Mon Sep 17 00:00:00 2001 From: tbjump Date: Tue, 11 Jul 2023 23:10:54 +0000 Subject: [PATCH 09/23] node/node_test: Capture logs at INFO level instead of DEBUG --- node/pkg/node/node_test.go | 6 +++--- node/pkg/node/options.go | 1 + node/pkg/p2p/p2p.go | 10 +++++++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/node/pkg/node/node_test.go b/node/pkg/node/node_test.go index 1798a671eb..0fc5af9b91 100644 --- a/node/pkg/node/node_test.go +++ b/node/pkg/node/node_test.go @@ -59,7 +59,7 @@ var PROMETHEUS_METRIC_VALID_HEARTBEAT_RECEIVED = "wormhole_p2p_broadcast_message const WAIT_FOR_LOGS = true const WAIT_FOR_METRICS = false -// The level at which logs will be written to console; During testing, logs are produced and buffered at Debug level, because some tests need to look for certain entries. +// The level at which logs will be written to console; During testing, logs are produced and buffered at Info level, because some tests need to look for certain entries. var CONSOLE_LOG_LEVEL = zap.InfoLevel var TEST_ID_CTR atomic.Uint32 @@ -212,7 +212,7 @@ func mockGuardianRunnable(testId uint, gs []*mockGuardian, mockGuardianIndex uin // setupLogsCapture is a helper function for making a zap logger/observer combination for testing that certain logs have been made func setupLogsCapture(options ...zap.Option) (*zap.Logger, *observer.ObservedLogs, *LogSizeCounter) { - observedCore, observedLogs := observer.New(zap.DebugLevel) + observedCore, observedLogs := observer.New(zap.InfoLevel) consoleLogger, _ := zap.NewDevelopment(zap.IncreaseLevel(CONSOLE_LOG_LEVEL)) lc := NewLogSizeCounter(zap.InfoLevel) parentLogger := zap.New(zapcore.NewTee(observedCore, consoleLogger.Core(), lc.Core()), options...) @@ -221,7 +221,7 @@ func setupLogsCapture(options ...zap.Option) (*zap.Logger, *observer.ObservedLog func waitForHeartbeatsInLogs(t testing.TB, zapObserver *observer.ObservedLogs, gs []*mockGuardian) { // example log entry that we're looking for: - // DEBUG root.g-2.g.p2p p2p/p2p.go:465 valid signed heartbeat received {"value": "node_name:\"g-0\" timestamp:1685677055425243683 version:\"development\" guardian_addr:\"0xeF2a03eAec928DD0EEAf35aD31e34d2b53152c07\" boot_timestamp:1685677040424855922 p2p_node_id:\"\\x00$\\x08\\x01\\x12 \\x97\\xf3\\xbd\\x87\\x13\\x15(\\x1e\\x8b\\x83\\xedǩ\\xfd\\x05A\\x06aTD\\x90p\\xcc\\xdb<\\xddB\\xcfi\\xccވ\"", "from": "12D3KooWL3XJ9EMCyZvmmGXL2LMiVBtrVa2BuESsJiXkSj7333Jw"} + // INFO root.g-2.g.p2p p2p/p2p.go:465 valid signed heartbeat received {"value": "node_name:\"g-0\" timestamp:1685677055425243683 version:\"development\" guardian_addr:\"0xeF2a03eAec928DD0EEAf35aD31e34d2b53152c07\" boot_timestamp:1685677040424855922 p2p_node_id:\"\\x00$\\x08\\x01\\x12 \\x97\\xf3\\xbd\\x87\\x13\\x15(\\x1e\\x8b\\x83\\xedǩ\\xfd\\x05A\\x06aTD\\x90p\\xcc\\xdb<\\xddB\\xcfi\\xccވ\"", "from": "12D3KooWL3XJ9EMCyZvmmGXL2LMiVBtrVa2BuESsJiXkSj7333Jw"} re := regexp.MustCompile("g-[0-9]+") for readyCounter := 0; readyCounter < len(gs); { diff --git a/node/pkg/node/options.go b/node/pkg/node/options.go index 185919bfdd..9de54f705d 100644 --- a/node/pkg/node/options.go +++ b/node/pkg/node/options.go @@ -48,6 +48,7 @@ func GuardianOptionP2P(p2pKey libp2p_crypto.PrivKey, networkId string, bootstrap if g.env == common.GoTest { components.WarnChannelOverflow = true + components.SignedHeartbeatLogLevel = zapcore.InfoLevel } g.runnables["p2p"] = p2p.Run( diff --git a/node/pkg/p2p/p2p.go b/node/pkg/p2p/p2p.go index 02ceb78a23..6e5dc1e037 100644 --- a/node/pkg/p2p/p2p.go +++ b/node/pkg/p2p/p2p.go @@ -34,6 +34,7 @@ import ( libp2ptls "github.com/libp2p/go-libp2p/p2p/security/tls" libp2pquic "github.com/libp2p/go-libp2p/p2p/transport/quic" "go.uber.org/zap" + "go.uber.org/zap/zapcore" "google.golang.org/protobuf/proto" gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1" @@ -95,6 +96,8 @@ type Components struct { ProtectedHostByGuardianKeyLock sync.Mutex // WarnChannelOverflow: If true, errors due to overflowing channels will produce logger.Warn WarnChannelOverflow bool + // SignedHeartbeatLogLevel is the log level at which SignedHeartbeatReceived events will be logged. + SignedHeartbeatLogLevel zapcore.Level } func (f *Components) ListeningAddresses() []string { @@ -122,6 +125,7 @@ func DefaultComponents() *Components { Port: DefaultPort, ConnMgr: mgr, ProtectedHostByGuardianKey: make(map[eth_common.Address]peer.ID), + SignedHeartbeatLogLevel: zapcore.DebugLevel, } } @@ -511,14 +515,14 @@ func Run( gs := gst.Get() if gs == nil { // No valid guardian set yet - dropping heartbeat - logger.Debug("skipping heartbeat - no guardian set", + logger.Log(components.SignedHeartbeatLogLevel, "skipping heartbeat - no guardian set", zap.Any("value", s), zap.String("from", envelope.GetFrom().String())) break } if heartbeat, err := processSignedHeartbeat(envelope.GetFrom(), s, gs, gst, disableHeartbeatVerify); err != nil { p2pMessagesReceived.WithLabelValues("invalid_heartbeat").Inc() - logger.Debug("invalid signed heartbeat received", + logger.Log(components.SignedHeartbeatLogLevel, "invalid signed heartbeat received", zap.Error(err), zap.Any("payload", msg.Message), zap.Any("value", s), @@ -526,7 +530,7 @@ func Run( zap.String("from", envelope.GetFrom().String())) } else { p2pMessagesReceived.WithLabelValues("valid_heartbeat").Inc() - logger.Debug("valid signed heartbeat received", + logger.Log(components.SignedHeartbeatLogLevel, "valid signed heartbeat received", zap.Any("value", heartbeat), zap.String("from", envelope.GetFrom().String())) From 336cc3f9bc933434f07587d4afd89d61a6fda1ab Mon Sep 17 00:00:00 2001 From: tbjump Date: Tue, 11 Jul 2023 23:18:51 +0000 Subject: [PATCH 10/23] node/node_test: Update BenchmarkConsensus numbers --- node/pkg/node/node_test.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/node/pkg/node/node_test.go b/node/pkg/node/node_test.go index 0fc5af9b91..7f671994de 100644 --- a/node/pkg/node/node_test.go +++ b/node/pkg/node/node_test.go @@ -994,20 +994,17 @@ func BenchmarkCrypto(b *testing.B) { }) } -// How to run: go test -v -ldflags '-extldflags "-Wl,--allow-multiple-definition" ' -bench ^BenchmarkConsensus -benchtime=1x -count 1 -run ^$ > bench.log; tail bench.log +// How to run: +// +// go test -v -ldflags '-extldflags "-Wl,--allow-multiple-definition" ' -bench ^BenchmarkConsensus -benchtime=1x -count 1 -run ^$ > bench.log; tail bench.log func BenchmarkConsensus(b *testing.B) { require.Equal(b, b.N, 1) //CONSOLE_LOG_LEVEL = zap.DebugLevel //CONSOLE_LOG_LEVEL = zap.InfoLevel CONSOLE_LOG_LEVEL = zap.WarnLevel - //benchmarkConsensus(b, "1", 19, 1000, 2) // ~28s - benchmarkConsensus(b, "1", 19, 100, 1) // ~3s - //benchmarkConsensus(b, "1", 19, 100, 2) // ~2s - //benchmarkConsensus(b, "1", 19, 100, 3) // sometimes fails, i.e. too much parallelism - //benchmarkConsensus(b, "1", 19, 100, 10) // sometimes fails, i.e. too much parallelism - //benchmarkConsensus(b, "1", 19, 100, 1) // 3s - //benchmarkConsensus(b, "1", 19, 20, 1) // 0.6s - //benchmarkConsensus(b, "1", 19, 5, 1) // 0.2s + benchmarkConsensus(b, "1", 19, 1000, 10) // ~10s + //benchmarkConsensus(b, "1", 19, 1000, 5) // ~10s + //benchmarkConsensus(b, "1", 19, 1000, 1) // ~13s } func benchmarkConsensus(t *testing.B, name string, numGuardians int, numMessages int, maxPendingObs int) { From b60472cb526a8da390015e67d7c478e28c84c3d8 Mon Sep 17 00:00:00 2001 From: tbjump Date: Wed, 5 Jul 2023 22:53:50 +0000 Subject: [PATCH 11/23] node/db: use provided logger for badgerDb --- node/pkg/db/open.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/pkg/db/open.go b/node/pkg/db/open.go index 513258db27..f7adfd997a 100644 --- a/node/pkg/db/open.go +++ b/node/pkg/db/open.go @@ -30,7 +30,7 @@ func (l badgerZapLogger) Debugf(f string, v ...interface{}) { } func OpenDb(logger *zap.Logger, dataDir *string) *Database { - var options badger.Options + options := badger.DefaultOptions(dbPath) if dataDir != nil { dbPath := path.Join(*dataDir, "db") From 42866295f812a8a8d07292d61f7b4b34f0fe629c Mon Sep 17 00:00:00 2001 From: tbjump Date: Wed, 5 Jul 2023 23:17:23 +0000 Subject: [PATCH 12/23] node/node_test: count amount of generated logs --- node/pkg/db/open.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/pkg/db/open.go b/node/pkg/db/open.go index f7adfd997a..513258db27 100644 --- a/node/pkg/db/open.go +++ b/node/pkg/db/open.go @@ -30,7 +30,7 @@ func (l badgerZapLogger) Debugf(f string, v ...interface{}) { } func OpenDb(logger *zap.Logger, dataDir *string) *Database { - options := badger.DefaultOptions(dbPath) + var options badger.Options if dataDir != nil { dbPath := path.Join(*dataDir, "db") From a62edfbdc0169e95a1132930741ae2b57153edb1 Mon Sep 17 00:00:00 2001 From: tbjump Date: Tue, 11 Jul 2023 21:03:04 +0000 Subject: [PATCH 13/23] node: output logs only for failed tests --- node/pkg/accountant/accountant_test.go | 3 ++- node/pkg/node/node_test.go | 14 +++++++------ .../pkg/watchers/near/nearapi/nearapi_test.go | 4 ++-- node/pkg/watchers/near/watcher_test.go | 20 +++++++++++-------- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/node/pkg/accountant/accountant_test.go b/node/pkg/accountant/accountant_test.go index e71dd3d132..efa1dd3cb0 100644 --- a/node/pkg/accountant/accountant_test.go +++ b/node/pkg/accountant/accountant_test.go @@ -24,6 +24,7 @@ import ( gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1" "github.com/wormhole-foundation/wormhole/sdk/vaa" "go.uber.org/zap" + "go.uber.org/zap/zaptest" ) const ( @@ -323,7 +324,7 @@ func TestInterestingTransferShouldBeBlockedWhenEnforcingAccountant(t *testing.T) func TestForDeadlock(t *testing.T) { ctx := context.Background() - logger, _ := zap.NewDevelopment() + logger := zaptest.NewLogger(t) obsvReqWriteC := make(chan *gossipv1.ObservationRequest, 10) acctChan := make(chan *common.MessagePublication, MsgChannelCapacity) wormchainConn := MockAccountantWormchainConn{} diff --git a/node/pkg/node/node_test.go b/node/pkg/node/node_test.go index 7f671994de..62724fdb92 100644 --- a/node/pkg/node/node_test.go +++ b/node/pkg/node/node_test.go @@ -41,6 +41,7 @@ import ( "github.com/wormhole-foundation/wormhole/sdk/vaa" "go.uber.org/zap" "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest" "go.uber.org/zap/zaptest/observer" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" @@ -211,9 +212,9 @@ func mockGuardianRunnable(testId uint, gs []*mockGuardian, mockGuardianIndex uin } // setupLogsCapture is a helper function for making a zap logger/observer combination for testing that certain logs have been made -func setupLogsCapture(options ...zap.Option) (*zap.Logger, *observer.ObservedLogs, *LogSizeCounter) { +func setupLogsCapture(t testing.TB, options ...zap.Option) (*zap.Logger, *observer.ObservedLogs, *LogSizeCounter) { observedCore, observedLogs := observer.New(zap.InfoLevel) - consoleLogger, _ := zap.NewDevelopment(zap.IncreaseLevel(CONSOLE_LOG_LEVEL)) + consoleLogger := zaptest.NewLogger(t, zaptest.Level(CONSOLE_LOG_LEVEL)) lc := NewLogSizeCounter(zap.InfoLevel) parentLogger := zap.New(zapcore.NewTee(observedCore, consoleLogger.Core(), lc.Core()), options...) return parentLogger, observedLogs, lc @@ -578,7 +579,7 @@ func testConsensus(t *testing.T, testCases []testCase, numGuardians int) { rootCtx, rootCtxCancel := context.WithTimeout(context.Background(), testTimeout) defer rootCtxCancel() - zapLogger, zapObserver, _ := setupLogsCapture() + zapLogger, zapObserver, _ := setupLogsCapture(t) supervisor.New(rootCtx, zapLogger, func(ctx context.Context) error { logger := supervisor.Logger(ctx) @@ -745,7 +746,8 @@ func testConsensus(t *testing.T, testCases []testCase, numGuardians int) { <-rootCtx.Done() assert.NotEqual(t, rootCtx.Err(), context.DeadlineExceeded) - zapLogger.Info("Test root context cancelled, exiting...") + zapLogger.Info("Test root context cancelled, waiting 10ms for everything to shut down properly...") + time.Sleep(time.Millisecond * 10) } type testCaseGuardianConfig struct { @@ -843,7 +845,7 @@ func testGuardianConfigurations(t *testing.T, testCases []testCaseGuardianConfig // The panic will be subsequently caught by the supervisor fatalHook := make(fatalHook) defer close(fatalHook) - zapLogger, zapObserver, _ := setupLogsCapture(zap.WithFatalHook(fatalHook)) + zapLogger, zapObserver, _ := setupLogsCapture(t, zap.WithFatalHook(fatalHook)) supervisor.New(rootCtx, zapLogger, func(ctx context.Context) error { // Create a sub-context with cancel function that we can pass to G.run. @@ -1020,7 +1022,7 @@ func benchmarkConsensus(t *testing.B, name string, numGuardians int, numMessages rootCtx, rootCtxCancel := context.WithTimeout(context.Background(), testTimeout) defer rootCtxCancel() - zapLogger, zapObserver, setupLogsCapture := setupLogsCapture() + zapLogger, zapObserver, setupLogsCapture := setupLogsCapture(t) supervisor.New(rootCtx, zapLogger, func(ctx context.Context) error { logger := supervisor.Logger(ctx) diff --git a/node/pkg/watchers/near/nearapi/nearapi_test.go b/node/pkg/watchers/near/nearapi/nearapi_test.go index f6d846f19a..227d110ec5 100644 --- a/node/pkg/watchers/near/nearapi/nearapi_test.go +++ b/node/pkg/watchers/near/nearapi/nearapi_test.go @@ -8,7 +8,7 @@ import ( "time" mockserver "github.com/certusone/wormhole/node/pkg/watchers/near/nearapi/mock" - "go.uber.org/zap" + "go.uber.org/zap/zaptest" "github.com/certusone/wormhole/node/pkg/watchers/near/nearapi" "github.com/stretchr/testify/assert" @@ -52,7 +52,7 @@ func TestNearApi(t *testing.T) { ctx, cancelFunc := context.WithTimeout(parentCtx, time.Second*5) defer cancelFunc() - logger, _ := zap.NewDevelopment() + logger := zaptest.NewLogger(t) mockServer := mockserver.NewForwardingCachingServer(logger, "https://rpc.mainnet.near.org", "mock/apitest/", nil) mockHttpServer := httptest.NewServer(mockServer) diff --git a/node/pkg/watchers/near/watcher_test.go b/node/pkg/watchers/near/watcher_test.go index ab9fc4ee2c..3865a35ed1 100644 --- a/node/pkg/watchers/near/watcher_test.go +++ b/node/pkg/watchers/near/watcher_test.go @@ -18,6 +18,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/wormhole-foundation/wormhole/sdk/vaa" "go.uber.org/zap" + "go.uber.org/zap/zaptest" ) const ( @@ -89,7 +90,7 @@ Stages of the test: 4) Check that all re-observed messages are correct */ func (testCase *testCase) run(ctx context.Context) error { - logger := supervisor.Logger(ctx) + logger := zaptest.NewLogger(testCase.t).Named("near.test") // Run the mock server mockServer := mockserver.NewForwardingCachingServer(logger, testCase.upstreamHost, testCase.cacheDir, testCase.latestFinalBlocks) @@ -205,11 +206,14 @@ func (testCase *testCase) setupAndRun(logger *zap.Logger) { rootCtxCancel() assert.NotEqual(testCase.t, err, context.DeadlineExceeded) // throw an error if timeout assert.NoError(testCase.t, err) + + // wait 10ms for things to shut down gracefully + time.Sleep(time.Millisecond * 10) } // TestWatcherSimple() tests the most simple case: "final" API only retruns one block which contains a Wormhole transaction. No re-observation requests. func TestWatcherSimple(t *testing.T) { - logger, _ := zap.NewDevelopment() + logger := zaptest.NewLogger(t) pl, _ := hex.DecodeString("0100000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000000000f0108bc32f7de18a5f6e1e7d6ee7aff9f5fc858d0d87ac0da94dd8d2a5d267d6b00160000000000000000000000000000000000000000000000000000000000000000") txHashBytes, _ := hex.DecodeString("88029cf0e7432cec04c266a3e72903ee6650b4624c7f9c8e22b04d78e18e87f8") @@ -244,7 +248,7 @@ func TestWatcherSimple(t *testing.T) { // TestWatcherSimple2() tests the case where the "final" API returns a sequence of real blocks which contain a single Wormhole transaction. No re-observation requests. func TestWatcherSimple2(t *testing.T) { - logger, _ := zap.NewDevelopment() + logger := zaptest.NewLogger(t) pl, _ := hex.DecodeString("0100000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000000000f0108bc32f7de18a5f6e1e7d6ee7aff9f5fc858d0d87ac0da94dd8d2a5d267d6b00160000000000000000000000000000000000000000000000000000000000000000") txHashBytes, _ := hex.DecodeString("88029cf0e7432cec04c266a3e72903ee6650b4624c7f9c8e22b04d78e18e87f8") @@ -287,7 +291,7 @@ func TestWatcherSimple2(t *testing.T) { // TestWatcherReobservation() tests the simple re-observation case: The "final" endpoint returns // the same unrelated block and there is a re-observation request for past data. func TestWatcherReobservation(t *testing.T) { - logger, _ := zap.NewDevelopment() + logger := zaptest.NewLogger(t) pl, _ := hex.DecodeString("0100000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000000000f0108bc32f7de18a5f6e1e7d6ee7aff9f5fc858d0d87ac0da94dd8d2a5d267d6b00160000000000000000000000000000000000000000000000000000000000000000") txHashBytes, _ := hex.DecodeString("88029cf0e7432cec04c266a3e72903ee6650b4624c7f9c8e22b04d78e18e87f8") @@ -329,7 +333,7 @@ func TestWatcherReobservation(t *testing.T) { // TestWatcherDelayedFinal() tests the case where a block cannot be finalized by a parent having it as // last_final_block and instead needs to be finalized by having it observed as finalized during polling func TestWatcherDelayedFinal(t *testing.T) { - logger, _ := zap.NewDevelopment() + logger := zaptest.NewLogger(t) pl, _ := hex.DecodeString("0100000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000000000f0108bc32f7de18a5f6e1e7d6ee7aff9f5fc858d0d87ac0da94dd8d2a5d267d6b00160000000000000000000000000000000000000000000000000000000000000000") txHashBytes, _ := hex.DecodeString("88029cf0e7432cec04c266a3e72903ee6650b4624c7f9c8e22b04d78e18e87f8") @@ -374,7 +378,7 @@ func TestWatcherDelayedFinal(t *testing.T) { // last_final_block and instead needs to be finalized by having it observed as finalized during polling // additionally, there is a large gap between polls func TestWatcherDelayedFinalAndGaps(t *testing.T) { - logger, _ := zap.NewDevelopment() + logger := zaptest.NewLogger(t) pl, _ := hex.DecodeString("0100000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000000000f0108bc32f7de18a5f6e1e7d6ee7aff9f5fc858d0d87ac0da94dd8d2a5d267d6b00160000000000000000000000000000000000000000000000000000000000000000") txHashBytes, _ := hex.DecodeString("88029cf0e7432cec04c266a3e72903ee6650b4624c7f9c8e22b04d78e18e87f8") @@ -421,7 +425,7 @@ func TestWatcherDelayedFinalAndGaps(t *testing.T) { "6eCgeVSC4Hwm8tAVy4qNQpnLs4S9EpzRjGtAipwZ632A", // 76538236 block 7: tx3 receipt */ func TestWatcherSynthetic(t *testing.T) { - logger, _ := zap.NewDevelopment() + logger := zaptest.NewLogger(t) pl, _ := hex.DecodeString("0100000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000000000f0108bc32f7de18a5f6e1e7d6ee7aff9f5fc858d0d87ac0da94dd8d2a5d267d6b00160000000000000000000000000000000000000000000000000000000000000000") @@ -514,7 +518,7 @@ func TestWatcherSynthetic(t *testing.T) { "6eCgeVSC4Hwm8tAVy4qNQpnLs4S9EpzRjGtAipwZ632A", // 76538236 block 7: tx3 receipt */ func TestWatcherUnfinalized(t *testing.T) { - logger, _ := zap.NewDevelopment() + logger := zaptest.NewLogger(t) pl, _ := hex.DecodeString("0100000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000000000f0108bc32f7de18a5f6e1e7d6ee7aff9f5fc858d0d87ac0da94dd8d2a5d267d6b00160000000000000000000000000000000000000000000000000000000000000000") From ef555ba78e7cda1bd0546004be8c940af2e2cd90 Mon Sep 17 00:00:00 2001 From: tbjump Date: Tue, 11 Jul 2023 18:07:48 +0000 Subject: [PATCH 14/23] node/node: Address review nits --- node/pkg/node/node_test.go | 14 ++++---------- node/pkg/node/options.go | 6 +++--- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/node/pkg/node/node_test.go b/node/pkg/node/node_test.go index 62724fdb92..bbd727b388 100644 --- a/node/pkg/node/node_test.go +++ b/node/pkg/node/node_test.go @@ -154,7 +154,7 @@ func mockGuardianRunnable(testId uint, gs []*mockGuardian, mockGuardianIndex uin ChainID: vaa.ChainIDSolana, MockObservationC: gs[mockGuardianIndex].MockObservationC, MockSetC: gs[mockGuardianIndex].MockSetC, - ObservationDb: obsDb, // TODO(future work) add observation DB to support re-observation request + ObservationDb: obsDb, }, } @@ -249,7 +249,7 @@ func waitForHeartbeatsInLogs(t testing.TB, zapObserver *observer.ObservedLogs, g } } -// waitForPromMetricExceed waits until prometheus metric `metric` >= `min` on all guardians in `gs`. +// waitForPromMetricGte waits until prometheus metric `metric` >= `min` on all guardians in `gs`. // WARNING: Currently, there is only a global registry for all prometheus metrics, leading to all guardian nodes writing to the same one. // // As long as this is the case, you probably don't want to use this function. @@ -257,7 +257,6 @@ func waitForPromMetricGte(t testing.TB, testId uint, ctx context.Context, gs []* metricBytes := []byte(metric) requests := make([]*http.Request, len(gs)) readyFlags := make([]bool, len(gs)) - //logger := supervisor.Logger(ctx) // create the prom api clients for i := range gs { @@ -304,7 +303,6 @@ func waitForPromMetricGte(t testing.TB, testId uint, ctx context.Context, gs []* readyFlags[i] = true readyCounter++ } - } time.Sleep(time.Second * 5) // TODO } @@ -315,7 +313,6 @@ func waitForVaa(ctx context.Context, c publicrpcv1.PublicRPCServiceClient, msgId var r *publicrpcv1.GetSignedVAAResponse var err error - //logger := supervisor.Logger(ctx) for { select { case <-ctx.Done(): @@ -862,7 +859,6 @@ func testGuardianConfigurations(t *testing.T, testCases []testCaseGuardianConfig // wait for all options to get applied // If we were expecting an error, we should never get past this point. for len(zapObserver.FilterMessage("GuardianNode initialization done.").All()) == 0 { - //logger.Info("Guardian not yet initialized. Waiting 10ms...") time.Sleep(time.Millisecond * 10) } @@ -973,7 +969,6 @@ func BenchmarkCrypto(b *testing.B) { b.Run("ethcrypto (secp256k1)", func(b *testing.B) { gk := devnet.InsecureDeterministicEcdsaKeyByIndex(ethcrypto.S256(), 0) - //gkc := ethcrypto.CompressPubkey(&gk.PublicKey) b.Run("sign", func(b *testing.B) { msgs := signingMsgs(b.N) @@ -990,7 +985,6 @@ func BenchmarkCrypto(b *testing.B) { for i := 0; i < b.N; i++ { _, err := ethcrypto.Ecrecover(msgs[i], signatures[i]) assert.NoError(b, err) - //assert.Equal(b, signer, gkc) } }) }) @@ -1143,8 +1137,8 @@ func benchmarkConsensus(t *testing.B, name string, numGuardians int, numMessages assert.NotEqual(t, rootCtx.Err(), context.DeadlineExceeded) zapLogger.Info("Test root context cancelled, exiting...") - // wait for everything to shut down gracefully TODO since switching to portIds by `testId`, this is no longer necessary - //time.Sleep(time.Second * 11) // 11s is needed to gracefully shutdown libp2p + // wait for everything to shut down gracefully + //time.Sleep(time.Second * 11) // 11s is needed to gracefully shutdown libp2p, but since switching to dedicated ports per `testId`, this is no longer necessary time.Sleep(time.Second * 1) // 1s is needed to gracefully shutdown BadgerDB }) } diff --git a/node/pkg/node/options.go b/node/pkg/node/options.go index 9de54f705d..eefe609a44 100644 --- a/node/pkg/node/options.go +++ b/node/pkg/node/options.go @@ -338,7 +338,7 @@ func GuardianOptionWatchers(watcherConfigs []watchers.WatcherConfig, ibcWatcherC readiness.RegisterComponent(common.ReadinessIBCSyncing) g.runnablesWithScissors["ibcwatch"] = ibc.NewWatcher(ibcWatcherConfig.Websocket, ibcWatcherConfig.Lcd, ibcWatcherConfig.Contract, chainConfig).Run } else { - return errors.New("Although IBC is enabled, there are no chains for it to monitor") + return errors.New("although IBC is enabled, there are no chains for it to monitor") } } @@ -370,7 +370,7 @@ func GuardianOptionAdminService(socketPath string, ethRpc *string, ethContract * rpcMap, ) if err != nil { - return err + return fmt.Errorf("failed to create admin service: %w", err) } g.runnables["admin"] = adminService @@ -388,7 +388,7 @@ func GuardianOptionPublicRpcSocket(publicGRPCSocketPath string, publicRpcLogDeta // local public grpc service socket publicrpcUnixService, publicrpcServer, err := publicrpcUnixServiceRunnable(logger, publicGRPCSocketPath, publicRpcLogDetail, g.db, g.gst, g.gov) if err != nil { - return err + return fmt.Errorf("failed to create publicrpc service: %w", err) } g.runnables["publicrpcsocket"] = publicrpcUnixService From 0896d028bdf565ed8447d115c4d14ca39511b138 Mon Sep 17 00:00:00 2001 From: tbjump Date: Tue, 11 Jul 2023 23:45:21 +0000 Subject: [PATCH 15/23] node/p2p: fix capitalization of error messages --- node/pkg/p2p/p2p.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/node/pkg/p2p/p2p.go b/node/pkg/p2p/p2p.go index 6e5dc1e037..ff63fe652e 100644 --- a/node/pkg/p2p/p2p.go +++ b/node/pkg/p2p/p2p.go @@ -152,12 +152,12 @@ func bootstrapAddrs(logger *zap.Logger, bootstrapPeers string, self peer.ID) (bo } ma, err := multiaddr.NewMultiaddr(addr) if err != nil { - logger.Error("Invalid bootstrap address", zap.String("peer", addr), zap.Error(err)) + logger.Error("invalid bootstrap address", zap.String("peer", addr), zap.Error(err)) continue } pi, err := peer.AddrInfoFromP2pAddr(ma) if err != nil { - logger.Error("Invalid bootstrap address", zap.String("peer", addr), zap.Error(err)) + logger.Error("invalid bootstrap address", zap.String("peer", addr), zap.Error(err)) continue } if pi.ID == self { @@ -175,7 +175,7 @@ func connectToPeers(ctx context.Context, logger *zap.Logger, h host.Host, peers successes = 0 for _, p := range peers { if err := h.Connect(ctx, p); err != nil { - logger.Error("Failed to connect to bootstrap peer", zap.String("peer", p.String()), zap.Error(err)) + logger.Error("failed to connect to bootstrap peer", zap.String("peer", p.String()), zap.Error(err)) } else { successes += 1 } @@ -263,7 +263,7 @@ func Run( defer func() { if err := h.Close(); err != nil { - logger.Error("Error closing the host", zap.Error(err)) + logger.Error("error closing the host", zap.Error(err)) } }() From d6f2e618618e4ae9497ecff8bd6864dfb7bd0015 Mon Sep 17 00:00:00 2001 From: tbjump Date: Wed, 12 Jul 2023 00:07:16 +0000 Subject: [PATCH 16/23] node/node_test: cleanup guardianConfig --- node/pkg/node/node_test.go | 108 ++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 57 deletions(-) diff --git a/node/pkg/node/node_test.go b/node/pkg/node/node_test.go index bbd727b388..d318fffde9 100644 --- a/node/pkg/node/node_test.go +++ b/node/pkg/node/node_test.go @@ -76,9 +76,30 @@ type mockGuardian struct { gk *ecdsa.PrivateKey guardianAddr eth_common.Address ready bool + config *guardianConfig } -func newMockGuardianSet(n int) []*mockGuardian { +type guardianConfig struct { + publicSocket string + adminSocket string + publicRpc string + publicWeb string + statusPort uint + p2pPort uint +} + +func createGuardianConfig(testId uint, mockGuardianIndex uint) *guardianConfig { + return &guardianConfig{ + publicSocket: fmt.Sprintf("/tmp/test_guardian_%d_public.socket", mockGuardianIndex+testId*20), + adminSocket: fmt.Sprintf("/tmp/test_guardian_%d_admin.socket", mockGuardianIndex+testId*20), // TODO consider using os.CreateTemp("/tmp", "test_guardian_adminXXXXX.socket"), + publicRpc: fmt.Sprintf("127.0.0.1:%d", mockGuardianIndex+LOCAL_RPC_PORTRANGE_START+testId*20), + publicWeb: fmt.Sprintf("127.0.0.1:%d", mockGuardianIndex+LOCAL_PUBLICWEB_PORTRANGE_START+testId*20), + statusPort: mockGuardianIndex + LOCAL_STATUS_PORTRANGE_START + testId*20, + p2pPort: mockGuardianIndex + LOCAL_P2P_PORTRANGE_START + testId*20, + } +} + +func newMockGuardianSet(testId uint, n int) []*mockGuardian { gs := make([]*mockGuardian, n) for i := 0; i < n; i++ { @@ -94,6 +115,7 @@ func newMockGuardianSet(n int) []*mockGuardian { MockSetC: make(chan *common.GuardianSet), gk: gk, guardianAddr: ethcrypto.PubkeyToAddress(gk.PublicKey), + config: createGuardianConfig(testId, uint(i)), } } @@ -108,32 +130,8 @@ func mockGuardianSetToGuardianAddrList(gs []*mockGuardian) []eth_common.Address return result } -func mockPublicSocket(testId uint, mockGuardianIndex uint) string { - return fmt.Sprintf("/tmp/test_guardian_%d_public.socket", mockGuardianIndex+testId*20) -} - -func mockAdminStocket(testId uint, mockGuardianIndex uint) string { - return fmt.Sprintf("/tmp/test_guardian_%d_admin.socket", mockGuardianIndex+testId*20) -} - -func mockPublicRpc(testId uint, mockGuardianIndex uint) string { - return fmt.Sprintf("127.0.0.1:%d", mockGuardianIndex+LOCAL_RPC_PORTRANGE_START+testId*20) -} - -func mockPublicWeb(testId uint, mockGuardianIndex uint) string { - return fmt.Sprintf("127.0.0.1:%d", mockGuardianIndex+LOCAL_PUBLICWEB_PORTRANGE_START+testId*20) -} - -func mockStatusPort(testId uint, mockGuardianIndex uint) uint { - return mockGuardianIndex + LOCAL_STATUS_PORTRANGE_START + testId*20 -} - -func mockP2PPort(testId uint, mockGuardianIndex uint) uint { - return mockGuardianIndex + LOCAL_P2P_PORTRANGE_START + testId*20 -} - // mockGuardianRunnable returns a runnable that first sets up a mock guardian an then runs it. -func mockGuardianRunnable(testId uint, gs []*mockGuardian, mockGuardianIndex uint, obsDb mock.ObservationDb) supervisor.Runnable { +func mockGuardianRunnable(gs []*mockGuardian, mockGuardianIndex uint, obsDb mock.ObservationDb) supervisor.Runnable { return func(ctx context.Context) error { // Create a sub-context with cancel function that we can pass to G.run. ctx, ctxCancel := context.WithCancel(ctx) @@ -165,32 +163,28 @@ func mockGuardianRunnable(testId uint, gs []*mockGuardian, mockGuardianIndex uin if err != nil { return err } - bootstrapPeers := fmt.Sprintf("/ip4/127.0.0.1/udp/%d/quic/p2p/%s", mockP2PPort(testId, 0), zeroPeerId.String()) - p2pPort := mockP2PPort(testId, mockGuardianIndex) - - // configure publicRpc - publicSocketPath := mockPublicSocket(testId, mockGuardianIndex) - publicRpc := mockPublicRpc(testId, mockGuardianIndex) + bootstrapPeers := fmt.Sprintf("/ip4/127.0.0.1/udp/%d/quic/p2p/%s", gs[0].config.p2pPort, zeroPeerId.String()) // configure adminservice - adminSocketPath := mockAdminStocket(testId, mockGuardianIndex) rpcMap := make(map[string]string) // We set this to None because we don't want to count these logs when counting the amount of logs generated per message publicRpcLogDetail := common.GrpcLogDetailNone + cfg := gs[mockGuardianIndex].config + // assemble all the options guardianOptions := []*GuardianOption{ GuardianOptionDatabase(db), GuardianOptionWatchers(watcherConfigs, nil), GuardianOptionNoAccountant(), // disable accountant GuardianOptionGovernor(true), - GuardianOptionP2P(gs[mockGuardianIndex].p2pKey, networkID, bootstrapPeers, nodeName, false, p2pPort, func() string { return "" }), - GuardianOptionPublicRpcSocket(publicSocketPath, publicRpcLogDetail), - GuardianOptionPublicrpcTcpService(publicRpc, publicRpcLogDetail), - GuardianOptionPublicWeb(mockPublicWeb(testId, mockGuardianIndex), publicSocketPath, "", false, ""), - GuardianOptionAdminService(adminSocketPath, nil, nil, rpcMap), - GuardianOptionStatusServer(fmt.Sprintf("[::]:%d", mockStatusPort(testId, mockGuardianIndex))), + GuardianOptionP2P(gs[mockGuardianIndex].p2pKey, networkID, bootstrapPeers, nodeName, false, cfg.p2pPort, func() string { return "" }), + GuardianOptionPublicRpcSocket(cfg.publicSocket, publicRpcLogDetail), + GuardianOptionPublicrpcTcpService(cfg.publicRpc, publicRpcLogDetail), + GuardianOptionPublicWeb(cfg.publicWeb, cfg.publicSocket, "", false, ""), + GuardianOptionAdminService(cfg.adminSocket, nil, nil, rpcMap), + GuardianOptionStatusServer(fmt.Sprintf("[::]:%d", cfg.statusPort)), GuardianOptionProcessor(), } @@ -253,14 +247,14 @@ func waitForHeartbeatsInLogs(t testing.TB, zapObserver *observer.ObservedLogs, g // WARNING: Currently, there is only a global registry for all prometheus metrics, leading to all guardian nodes writing to the same one. // // As long as this is the case, you probably don't want to use this function. -func waitForPromMetricGte(t testing.TB, testId uint, ctx context.Context, gs []*mockGuardian, metric string, min int) { +func waitForPromMetricGte(t testing.TB, ctx context.Context, gs []*mockGuardian, metric string, min int) { metricBytes := []byte(metric) requests := make([]*http.Request, len(gs)) readyFlags := make([]bool, len(gs)) // create the prom api clients for i := range gs { - url := fmt.Sprintf("http://localhost:%d/metrics", mockStatusPort(testId, uint(i))) + url := fmt.Sprintf("http://localhost:%d/metrics", gs[i].config.statusPort) req, err := http.NewRequestWithContext(ctx, "GET", url, nil) assert.NoError(t, err) requests[i] = req @@ -582,13 +576,13 @@ func testConsensus(t *testing.T, testCases []testCase, numGuardians int) { logger := supervisor.Logger(ctx) // create the Guardian Set - gs := newMockGuardianSet(numGuardians) + gs := newMockGuardianSet(testId, numGuardians) obsDb := makeObsDb(testCases) // run the guardians for i := 0; i < numGuardians; i++ { - gRun := mockGuardianRunnable(testId, gs, uint(i), obsDb) + gRun := mockGuardianRunnable(gs, uint(i), obsDb) err := supervisor.Run(ctx, fmt.Sprintf("g-%d", i), gRun) if i == 0 && numGuardians > 1 { time.Sleep(time.Second) // give the bootstrap guardian some time to start up @@ -609,8 +603,8 @@ func testConsensus(t *testing.T, testCases []testCase, numGuardians int) { } // wait for the status server to come online and check that it works - for i := range gs { - err := testStatusServer(ctx, logger, fmt.Sprintf("http://127.0.0.1:%d/metrics", mockStatusPort(testId, uint(i)))) + for _, g := range gs { + err := testStatusServer(ctx, logger, fmt.Sprintf("http://127.0.0.1:%d/metrics", g.config.statusPort)) assert.NoError(t, err) } @@ -619,7 +613,7 @@ func testConsensus(t *testing.T, testCases []testCase, numGuardians int) { assert.True(t, WAIT_FOR_LOGS || WAIT_FOR_METRICS) assert.False(t, WAIT_FOR_LOGS && WAIT_FOR_METRICS) // can't do both, because they both write to gs[].ready if WAIT_FOR_METRICS { - waitForPromMetricGte(t, testId, ctx, gs, PROMETHEUS_METRIC_VALID_HEARTBEAT_RECEIVED, 1) + waitForPromMetricGte(t, ctx, gs, PROMETHEUS_METRIC_VALID_HEARTBEAT_RECEIVED, 1) } if WAIT_FOR_LOGS { waitForHeartbeatsInLogs(t, zapObserver, gs) @@ -645,14 +639,14 @@ func testConsensus(t *testing.T, testCases []testCase, numGuardians int) { } // Wait for adminrpc to come online - for zapObserver.FilterMessage("admin server listening on").FilterField(zap.String("path", mockAdminStocket(testId, adminRpcGuardianIndex))).Len() == 0 { + for zapObserver.FilterMessage("admin server listening on").FilterField(zap.String("path", gs[adminRpcGuardianIndex].config.adminSocket)).Len() == 0 { logger.Info("admin server seems to be offline (according to logs). Waiting 100ms...") time.Sleep(time.Microsecond * 100) } // Send manual re-observation requests func() { // put this in own function to use defer - s := fmt.Sprintf("unix:///%s", mockAdminStocket(testId, vaaCheckGuardianIndex)) + s := fmt.Sprintf("unix:///%s", gs[adminRpcGuardianIndex].config.adminSocket) conn, err := grpc.DialContext(ctx, s, grpc.WithTransportCredentials(insecure.NewCredentials())) require.NoError(t, err) defer conn.Close() @@ -677,14 +671,14 @@ func testConsensus(t *testing.T, testCases []testCase, numGuardians int) { }() // Wait for publicrpc to come online - for zapObserver.FilterMessage("publicrpc server listening").FilterField(zap.String("addr", mockPublicRpc(testId, vaaCheckGuardianIndex))).Len() == 0 { + for zapObserver.FilterMessage("publicrpc server listening").FilterField(zap.String("addr", gs[vaaCheckGuardianIndex].config.publicRpc)).Len() == 0 { logger.Info("publicrpc seems to be offline (according to logs). Waiting 100ms...") time.Sleep(time.Microsecond * 100) } // check that the VAAs were generated logger.Info("Connecting to publicrpc...") - conn, err := grpc.DialContext(ctx, mockPublicRpc(testId, vaaCheckGuardianIndex), grpc.WithTransportCredentials(insecure.NewCredentials())) + conn, err := grpc.DialContext(ctx, gs[vaaCheckGuardianIndex].config.publicRpc, grpc.WithTransportCredentials(insecure.NewCredentials())) require.NoError(t, err) defer conn.Close() @@ -1022,13 +1016,13 @@ func benchmarkConsensus(t *testing.B, name string, numGuardians int, numMessages logger := supervisor.Logger(ctx) // create the Guardian Set - gs := newMockGuardianSet(numGuardians) + gs := newMockGuardianSet(testId, numGuardians) var obsDb mock.ObservationDb = nil // TODO // run the guardians for i := 0; i < numGuardians; i++ { - gRun := mockGuardianRunnable(testId, gs, uint(i), obsDb) + gRun := mockGuardianRunnable(gs, uint(i), obsDb) err := supervisor.Run(ctx, fmt.Sprintf("g-%d", i), gRun) if i == 0 && numGuardians > 1 { time.Sleep(time.Second) // give the bootstrap guardian some time to start up @@ -1049,8 +1043,8 @@ func benchmarkConsensus(t *testing.B, name string, numGuardians int, numMessages } // wait for the status server to come online and check that it works - for i := range gs { - err := testStatusServer(ctx, logger, fmt.Sprintf("http://127.0.0.1:%d/metrics", mockStatusPort(testId, uint(i)))) + for _, g := range gs { + err := testStatusServer(ctx, logger, fmt.Sprintf("http://127.0.0.1:%d/metrics", g.config.statusPort)) assert.NoError(t, err) } @@ -1058,7 +1052,7 @@ func benchmarkConsensus(t *testing.B, name string, numGuardians int, numMessages // This is necessary because if they have not joined the p2p network yet, gossip messages may get dropped silently. assert.True(t, WAIT_FOR_LOGS || WAIT_FOR_METRICS) if WAIT_FOR_METRICS { - waitForPromMetricGte(t, testId, ctx, gs, PROMETHEUS_METRIC_VALID_HEARTBEAT_RECEIVED, 1) + waitForPromMetricGte(t, ctx, gs, PROMETHEUS_METRIC_VALID_HEARTBEAT_RECEIVED, 1) } if WAIT_FOR_LOGS { waitForHeartbeatsInLogs(t, zapObserver, gs) @@ -1066,12 +1060,12 @@ func benchmarkConsensus(t *testing.B, name string, numGuardians int, numMessages logger.Info("All Guardians have received at least one heartbeat.") // Wait for publicrpc to come online. - for zapObserver.FilterMessage("publicrpc server listening").FilterField(zap.String("addr", mockPublicRpc(testId, 0))).Len() == 0 { + for zapObserver.FilterMessage("publicrpc server listening").FilterField(zap.String("addr", gs[0].config.publicRpc)).Len() == 0 { logger.Info("publicrpc seems to be offline (according to logs). Waiting 100ms...") time.Sleep(time.Microsecond * 100) } // now that it's online, connect to publicrpc of guardian-0 - conn, err := grpc.DialContext(ctx, mockPublicRpc(testId, 0), grpc.WithTransportCredentials(insecure.NewCredentials())) + conn, err := grpc.DialContext(ctx, gs[0].config.publicRpc, grpc.WithTransportCredentials(insecure.NewCredentials())) require.NoError(t, err) defer conn.Close() c := publicrpcv1.NewPublicRPCServiceClient(conn) From 590cd562bbffaa4a54946c356b2bc8728e0d5b8e Mon Sep 17 00:00:00 2001 From: tbjump Date: Fri, 14 Jul 2023 16:07:43 +0000 Subject: [PATCH 17/23] node/node_test: Rename helper functions --- node/pkg/node/node_test.go | 65 ++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/node/pkg/node/node_test.go b/node/pkg/node/node_test.go index d318fffde9..219d4a9cfc 100644 --- a/node/pkg/node/node_test.go +++ b/node/pkg/node/node_test.go @@ -88,7 +88,8 @@ type guardianConfig struct { p2pPort uint } -func createGuardianConfig(testId uint, mockGuardianIndex uint) *guardianConfig { +func createGuardianConfig(t testing.TB, testId uint, mockGuardianIndex uint) *guardianConfig { + t.Helper() return &guardianConfig{ publicSocket: fmt.Sprintf("/tmp/test_guardian_%d_public.socket", mockGuardianIndex+testId*20), adminSocket: fmt.Sprintf("/tmp/test_guardian_%d_admin.socket", mockGuardianIndex+testId*20), // TODO consider using os.CreateTemp("/tmp", "test_guardian_adminXXXXX.socket"), @@ -99,7 +100,8 @@ func createGuardianConfig(testId uint, mockGuardianIndex uint) *guardianConfig { } } -func newMockGuardianSet(testId uint, n int) []*mockGuardian { +func newMockGuardianSet(t testing.TB, testId uint, n int) []*mockGuardian { + t.Helper() gs := make([]*mockGuardian, n) for i := 0; i < n; i++ { @@ -115,14 +117,15 @@ func newMockGuardianSet(testId uint, n int) []*mockGuardian { MockSetC: make(chan *common.GuardianSet), gk: gk, guardianAddr: ethcrypto.PubkeyToAddress(gk.PublicKey), - config: createGuardianConfig(testId, uint(i)), + config: createGuardianConfig(t, testId, uint(i)), } } return gs } -func mockGuardianSetToGuardianAddrList(gs []*mockGuardian) []eth_common.Address { +func mockGuardianSetToGuardianAddrList(t testing.TB, gs []*mockGuardian) []eth_common.Address { + t.Helper() result := make([]eth_common.Address, len(gs)) for i, g := range gs { result[i] = g.guardianAddr @@ -131,7 +134,8 @@ func mockGuardianSetToGuardianAddrList(gs []*mockGuardian) []eth_common.Address } // mockGuardianRunnable returns a runnable that first sets up a mock guardian an then runs it. -func mockGuardianRunnable(gs []*mockGuardian, mockGuardianIndex uint, obsDb mock.ObservationDb) supervisor.Runnable { +func mockGuardianRunnable(t testing.TB, gs []*mockGuardian, mockGuardianIndex uint, obsDb mock.ObservationDb) supervisor.Runnable { + t.Helper() return func(ctx context.Context) error { // Create a sub-context with cancel function that we can pass to G.run. ctx, ctxCancel := context.WithCancel(ctx) @@ -207,6 +211,7 @@ func mockGuardianRunnable(gs []*mockGuardian, mockGuardianIndex uint, obsDb mock // setupLogsCapture is a helper function for making a zap logger/observer combination for testing that certain logs have been made func setupLogsCapture(t testing.TB, options ...zap.Option) (*zap.Logger, *observer.ObservedLogs, *LogSizeCounter) { + t.Helper() observedCore, observedLogs := observer.New(zap.InfoLevel) consoleLogger := zaptest.NewLogger(t, zaptest.Level(CONSOLE_LOG_LEVEL)) lc := NewLogSizeCounter(zap.InfoLevel) @@ -215,6 +220,7 @@ func setupLogsCapture(t testing.TB, options ...zap.Option) (*zap.Logger, *observ } func waitForHeartbeatsInLogs(t testing.TB, zapObserver *observer.ObservedLogs, gs []*mockGuardian) { + t.Helper() // example log entry that we're looking for: // INFO root.g-2.g.p2p p2p/p2p.go:465 valid signed heartbeat received {"value": "node_name:\"g-0\" timestamp:1685677055425243683 version:\"development\" guardian_addr:\"0xeF2a03eAec928DD0EEAf35aD31e34d2b53152c07\" boot_timestamp:1685677040424855922 p2p_node_id:\"\\x00$\\x08\\x01\\x12 \\x97\\xf3\\xbd\\x87\\x13\\x15(\\x1e\\x8b\\x83\\xedǩ\\xfd\\x05A\\x06aTD\\x90p\\xcc\\xdb<\\xddB\\xcfi\\xccވ\"", "from": "12D3KooWL3XJ9EMCyZvmmGXL2LMiVBtrVa2BuESsJiXkSj7333Jw"} re := regexp.MustCompile("g-[0-9]+") @@ -248,6 +254,7 @@ func waitForHeartbeatsInLogs(t testing.TB, zapObserver *observer.ObservedLogs, g // // As long as this is the case, you probably don't want to use this function. func waitForPromMetricGte(t testing.TB, ctx context.Context, gs []*mockGuardian, metric string, min int) { + t.Helper() metricBytes := []byte(metric) requests := make([]*http.Request, len(gs)) readyFlags := make([]bool, len(gs)) @@ -303,7 +310,8 @@ func waitForPromMetricGte(t testing.TB, ctx context.Context, gs []*mockGuardian, } // waitForVaa polls the publicRpc service every 5ms until there is a response. -func waitForVaa(ctx context.Context, c publicrpcv1.PublicRPCServiceClient, msgId *publicrpcv1.MessageID, mustNotReachQuorum bool) (*publicrpcv1.GetSignedVAAResponse, error) { +func waitForVaa(t testing.TB, ctx context.Context, c publicrpcv1.PublicRPCServiceClient, msgId *publicrpcv1.MessageID, mustNotReachQuorum bool) (*publicrpcv1.GetSignedVAAResponse, error) { + t.Helper() var r *publicrpcv1.GetSignedVAAResponse var err error @@ -443,8 +451,9 @@ func makeObsDb(tc []testCase) mock.ObservationDb { return db } +// waitForStatusServer queries the /readyz and /metrics endpoints at `statusAddr` every 100ms until they are online. // #nosec G107 -- it's OK to make http requests with `statusAddr` because `statusAddr` is trusted. -func testStatusServer(ctx context.Context, logger *zap.Logger, statusAddr string) error { +func waitForStatusServer(ctx context.Context, logger *zap.Logger, statusAddr string) error { var httpClient = &http.Client{ Timeout: time.Second * 10, } @@ -555,11 +564,11 @@ func TestConsensus(t *testing.T) { // TODO add a testcase to test the automatic re-observation requests. // Need to refactor various usage of wall time to a mockable time first. E.g. using https://github.com/benbjohnson/clock } - testConsensus(t, testCases, numGuardians) + runConsensusTests(t, testCases, numGuardians) } -// testConsensus spins up `numGuardians` guardians and runs & verifies the testCases -func testConsensus(t *testing.T, testCases []testCase, numGuardians int) { +// runConsensusTests spins up `numGuardians` guardians and runs & verifies the testCases +func runConsensusTests(t *testing.T, testCases []testCase, numGuardians int) { const testTimeout = time.Second * 30 const guardianSetIndex = 5 // index of the active guardian set (can be anything, just needs to be set to something) const vaaCheckGuardianIndex uint = 0 // we will query this guardian's publicrpc for VAAs @@ -576,13 +585,13 @@ func testConsensus(t *testing.T, testCases []testCase, numGuardians int) { logger := supervisor.Logger(ctx) // create the Guardian Set - gs := newMockGuardianSet(testId, numGuardians) + gs := newMockGuardianSet(t, testId, numGuardians) obsDb := makeObsDb(testCases) // run the guardians for i := 0; i < numGuardians; i++ { - gRun := mockGuardianRunnable(gs, uint(i), obsDb) + gRun := mockGuardianRunnable(t, gs, uint(i), obsDb) err := supervisor.Run(ctx, fmt.Sprintf("g-%d", i), gRun) if i == 0 && numGuardians > 1 { time.Sleep(time.Second) // give the bootstrap guardian some time to start up @@ -594,7 +603,7 @@ func testConsensus(t *testing.T, testCases []testCase, numGuardians int) { // Inform them of the Guardian Set commonGuardianSet := common.GuardianSet{ - Keys: mockGuardianSetToGuardianAddrList(gs), + Keys: mockGuardianSetToGuardianAddrList(t, gs), Index: guardianSetIndex, } for i, g := range gs { @@ -604,7 +613,7 @@ func testConsensus(t *testing.T, testCases []testCase, numGuardians int) { // wait for the status server to come online and check that it works for _, g := range gs { - err := testStatusServer(ctx, logger, fmt.Sprintf("http://127.0.0.1:%d/metrics", g.config.statusPort)) + err := waitForStatusServer(ctx, logger, fmt.Sprintf("http://127.0.0.1:%d/metrics", g.config.statusPort)) assert.NoError(t, err) } @@ -684,7 +693,7 @@ func testConsensus(t *testing.T, testCases []testCase, numGuardians int) { defer conn.Close() c := publicrpcv1.NewPublicRPCServiceClient(conn) - gsAddrList := mockGuardianSetToGuardianAddrList(gs) + gsAddrList := mockGuardianSetToGuardianAddrList(t, gs) // ensure that all test cases have passed for i, testCase := range testCases { @@ -698,7 +707,7 @@ func testConsensus(t *testing.T, testCases []testCase, numGuardians int) { EmitterAddress: msg.EmitterAddress.String(), Sequence: msg.Sequence, } - r, err := waitForVaa(ctx, c, msgId, testCase.mustNotReachQuorum) + r, err := waitForVaa(t, ctx, c, msgId, testCase.mustNotReachQuorum) assert.NotEqual(t, testCase.mustNotReachQuorum, testCase.mustReachQuorum) // either or if testCase.mustNotReachQuorum { @@ -797,7 +806,7 @@ func TestWatcherConfigs(t *testing.T) { err: "L1finalizer does not exist. Please check the order of the watcher configurations in watcherConfigs.", }, } - testGuardianConfigurations(t, tc) + runGuardianConfigTests(t, tc) } func TestGuardianConfigs(t *testing.T) { @@ -818,10 +827,10 @@ func TestGuardianConfigs(t *testing.T) { err: "Component bigtable is already configured and cannot be configured a second time", }, } - testGuardianConfigurations(t, tc) + runGuardianConfigTests(t, tc) } -func testGuardianConfigurations(t *testing.T, testCases []testCaseGuardianConfig) { +func runGuardianConfigTests(t *testing.T, testCases []testCaseGuardianConfig) { for _, tc := range testCases { // because we're only instantiating the guardians and kill them right after they started running, 2s should be plenty of time const testTimeout = time.Second * 2 @@ -992,12 +1001,12 @@ func BenchmarkConsensus(b *testing.B) { //CONSOLE_LOG_LEVEL = zap.DebugLevel //CONSOLE_LOG_LEVEL = zap.InfoLevel CONSOLE_LOG_LEVEL = zap.WarnLevel - benchmarkConsensus(b, "1", 19, 1000, 10) // ~10s - //benchmarkConsensus(b, "1", 19, 1000, 5) // ~10s - //benchmarkConsensus(b, "1", 19, 1000, 1) // ~13s + runConsensusBenchmark(b, "1", 19, 1000, 10) // ~10s + //runConsensusBenchmark(b, "1", 19, 1000, 5) // ~10s + //runConsensusBenchmark(b, "1", 19, 1000, 1) // ~13s } -func benchmarkConsensus(t *testing.B, name string, numGuardians int, numMessages int, maxPendingObs int) { +func runConsensusBenchmark(t *testing.B, name string, numGuardians int, numMessages int, maxPendingObs int) { t.Run(name, func(t *testing.B) { require.Equal(t, t.N, 1) testId := getTestId() @@ -1016,13 +1025,13 @@ func benchmarkConsensus(t *testing.B, name string, numGuardians int, numMessages logger := supervisor.Logger(ctx) // create the Guardian Set - gs := newMockGuardianSet(testId, numGuardians) + gs := newMockGuardianSet(t, testId, numGuardians) var obsDb mock.ObservationDb = nil // TODO // run the guardians for i := 0; i < numGuardians; i++ { - gRun := mockGuardianRunnable(gs, uint(i), obsDb) + gRun := mockGuardianRunnable(t, gs, uint(i), obsDb) err := supervisor.Run(ctx, fmt.Sprintf("g-%d", i), gRun) if i == 0 && numGuardians > 1 { time.Sleep(time.Second) // give the bootstrap guardian some time to start up @@ -1034,7 +1043,7 @@ func benchmarkConsensus(t *testing.B, name string, numGuardians int, numMessages // Inform them of the Guardian Set commonGuardianSet := common.GuardianSet{ - Keys: mockGuardianSetToGuardianAddrList(gs), + Keys: mockGuardianSetToGuardianAddrList(t, gs), Index: guardianSetIndex, } for i, g := range gs { @@ -1044,7 +1053,7 @@ func benchmarkConsensus(t *testing.B, name string, numGuardians int, numMessages // wait for the status server to come online and check that it works for _, g := range gs { - err := testStatusServer(ctx, logger, fmt.Sprintf("http://127.0.0.1:%d/metrics", g.config.statusPort)) + err := waitForStatusServer(ctx, logger, fmt.Sprintf("http://127.0.0.1:%d/metrics", g.config.statusPort)) assert.NoError(t, err) } @@ -1105,7 +1114,7 @@ func benchmarkConsensus(t *testing.B, name string, numGuardians int, numMessages } // a VAA should not take longer than 10s to be produced, no matter what. waitCtx, cancelFunc := context.WithTimeout(ctx, time.Second*10) - _, err := waitForVaa(waitCtx, c, msgId, false) + _, err := waitForVaa(t, waitCtx, c, msgId, false) cancelFunc() assert.NoError(t, err) if err != nil { From bd89e4e3ab41957524f2848ffdcb287224e00058 Mon Sep 17 00:00:00 2001 From: bruce-riley <96066700+bruce-riley@users.noreply.github.com> Date: Wed, 19 Jul 2023 10:23:16 -0500 Subject: [PATCH 18/23] Node: Processor delay metrics (#3210) --- node/cmd/spy/spy.go | 2 +- node/pkg/common/msg_with_timestamp.go | 29 ++++++++++++++++++++++ node/pkg/node/node.go | 4 +-- node/pkg/p2p/p2p.go | 7 +++--- node/pkg/p2p/watermark_test.go | 4 +-- node/pkg/processor/broadcast.go | 5 ++-- node/pkg/processor/observation.go | 5 +++- node/pkg/processor/processor.go | 35 +++++++++++++++++++++++++-- 8 files changed, 77 insertions(+), 14 deletions(-) create mode 100644 node/pkg/common/msg_with_timestamp.go diff --git a/node/cmd/spy/spy.go b/node/cmd/spy/spy.go index d6b561fb49..0498c49f01 100644 --- a/node/cmd/spy/spy.go +++ b/node/cmd/spy/spy.go @@ -266,7 +266,7 @@ func runSpy(cmd *cobra.Command, args []string) { sendC := make(chan []byte) // Inbound observations - obsvC := make(chan *gossipv1.SignedObservation, 1024) + obsvC := make(chan *common.MsgWithTimeStamp[gossipv1.SignedObservation], 1024) // Inbound observation requests obsvReqC := make(chan *gossipv1.ObservationRequest, 1024) diff --git a/node/pkg/common/msg_with_timestamp.go b/node/pkg/common/msg_with_timestamp.go new file mode 100644 index 0000000000..c40807d378 --- /dev/null +++ b/node/pkg/common/msg_with_timestamp.go @@ -0,0 +1,29 @@ +package common + +import ( + "time" +) + +// MsgWithTimeStamp allows us to track the time of receipt of an event. +type MsgWithTimeStamp[T any] struct { + Msg *T + Timestamp time.Time +} + +// CreateMsgWithTimestamp creates a new MsgWithTimeStamp with the current time. +func CreateMsgWithTimestamp[T any](msg *T) *MsgWithTimeStamp[T] { + return &MsgWithTimeStamp[T]{ + Msg: msg, + Timestamp: time.Now(), + } +} + +// PostMsgWithTimestamp sends the message to the specified channel using the current timestamp. Returns ErrChanFull on error. +func PostMsgWithTimestamp[T any](msg *T, c chan<- *MsgWithTimeStamp[T]) error { + select { + case c <- CreateMsgWithTimestamp[T](msg): + return nil + default: + return ErrChanFull + } +} diff --git a/node/pkg/node/node.go b/node/pkg/node/node.go index 4bb0fed17b..b741b4c4a3 100644 --- a/node/pkg/node/node.go +++ b/node/pkg/node/node.go @@ -54,7 +54,7 @@ type G struct { // Outbound gossip message queue (needs to be read/write because p2p needs read/write) gossipSendC chan []byte // Inbound observations. This is read/write because the processor also writes to it as a fast-path when handling locally made observations. - obsvC chan *gossipv1.SignedObservation + obsvC chan *common.MsgWithTimeStamp[gossipv1.SignedObservation] // Finalized guardian observations aggregated across all chains msgC channelPair[*common.MessagePublication] // Ethereum incoming guardian set updates @@ -88,7 +88,7 @@ func (g *G) initializeBasic(logger *zap.Logger, rootCtxCancel context.CancelFunc // Setup various channels... g.gossipSendC = make(chan []byte) - g.obsvC = make(chan *gossipv1.SignedObservation, inboundObservationBufferSize) + g.obsvC = make(chan *common.MsgWithTimeStamp[gossipv1.SignedObservation], inboundObservationBufferSize) g.msgC = makeChannelPair[*common.MessagePublication](0) g.setC = makeChannelPair[*common.GuardianSet](1) // This needs to be a buffered channel because of a circular dependency between processor and accountant during startup. g.signedInC = makeChannelPair[*gossipv1.SignedVAAWithQuorum](inboundSignedVaaBufferSize) diff --git a/node/pkg/p2p/p2p.go b/node/pkg/p2p/p2p.go index ff63fe652e..0cf00a2d38 100644 --- a/node/pkg/p2p/p2p.go +++ b/node/pkg/p2p/p2p.go @@ -184,7 +184,7 @@ func connectToPeers(ctx context.Context, logger *zap.Logger, h host.Host, peers } func Run( - obsvC chan<- *gossipv1.SignedObservation, + obsvC chan<- *common.MsgWithTimeStamp[gossipv1.SignedObservation], obsvReqC chan<- *gossipv1.ObservationRequest, obsvReqSendC <-chan *gossipv1.ObservationRequest, gossipSendC chan []byte, @@ -572,10 +572,9 @@ func Run( }() } case *gossipv1.GossipMessage_SignedObservation: - select { - case obsvC <- m.SignedObservation: + if err := common.PostMsgWithTimestamp[gossipv1.SignedObservation](m.SignedObservation, obsvC); err == nil { p2pMessagesReceived.WithLabelValues("observation").Inc() - default: + } else { if components.WarnChannelOverflow { logger.Warn("Ignoring SignedObservation because obsvC full", zap.String("hash", hex.EncodeToString(m.SignedObservation.Hash))) } diff --git a/node/pkg/p2p/watermark_test.go b/node/pkg/p2p/watermark_test.go index f3a49ea668..687f0e3eb6 100644 --- a/node/pkg/p2p/watermark_test.go +++ b/node/pkg/p2p/watermark_test.go @@ -27,7 +27,7 @@ const LOCAL_P2P_PORTRANGE_START = 11000 type G struct { // arguments passed to p2p.New - obsvC chan *gossipv1.SignedObservation + obsvC chan *node_common.MsgWithTimeStamp[gossipv1.SignedObservation] obsvReqC chan *gossipv1.ObservationRequest obsvReqSendC chan *gossipv1.ObservationRequest sendC chan []byte @@ -62,7 +62,7 @@ func NewG(t *testing.T, nodeName string) *G { } g := &G{ - obsvC: make(chan *gossipv1.SignedObservation, cs), + obsvC: make(chan *node_common.MsgWithTimeStamp[gossipv1.SignedObservation], cs), obsvReqC: make(chan *gossipv1.ObservationRequest, cs), obsvReqSendC: make(chan *gossipv1.ObservationRequest, cs), sendC: make(chan []byte, cs), diff --git a/node/pkg/processor/broadcast.go b/node/pkg/processor/broadcast.go index bf097c7350..7d5b6a1ea6 100644 --- a/node/pkg/processor/broadcast.go +++ b/node/pkg/processor/broadcast.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "google.golang.org/protobuf/proto" + node_common "github.com/certusone/wormhole/node/pkg/common" gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1" "github.com/wormhole-foundation/wormhole/sdk/vaa" ) @@ -64,8 +65,8 @@ func (p *Processor) broadcastSignature( p.state.signatures[hash].source = o.GetEmitterChain().String() p.state.signatures[hash].gs = p.gs // guaranteed to match ourObservation - there's no concurrent access to p.gs - // Fast path for our own signature - go func() { p.obsvC <- &obsv }() + // Fast path for our own signature. Put this in a go routine so it can block if the channel is full. That's also why we're not using node_common.PostMsgWithTimestamp. + go func() { p.obsvC <- node_common.CreateMsgWithTimestamp[gossipv1.SignedObservation](&obsv) }() observationsBroadcastTotal.Inc() } diff --git a/node/pkg/processor/observation.go b/node/pkg/processor/observation.go index f843227328..9714fd4870 100644 --- a/node/pkg/processor/observation.go +++ b/node/pkg/processor/observation.go @@ -46,12 +46,13 @@ var ( // handleObservation processes a remote VAA observation, verifies it, checks whether the VAA has met quorum, // and assembles and submits a valid VAA if possible. -func (p *Processor) handleObservation(ctx context.Context, m *gossipv1.SignedObservation) { +func (p *Processor) handleObservation(ctx context.Context, obs *node_common.MsgWithTimeStamp[gossipv1.SignedObservation]) { // SECURITY: at this point, observations received from the p2p network are fully untrusted (all fields!) // // Note that observations are never tied to the (verified) p2p identity key - the p2p network // identity is completely decoupled from the guardian identity, p2p is just transport. + m := obs.Msg hash := hex.EncodeToString(m.Hash) p.logger.Debug("received observation", @@ -218,6 +219,8 @@ func (p *Processor) handleObservation(ctx context.Context, m *gossipv1.SignedObs zap.Bools("aggregation", agg)) } + + observationTotalDelay.Observe(float64(time.Since(obs.Timestamp).Microseconds())) } func (p *Processor) handleInboundSignedVAAWithQuorum(ctx context.Context, m *gossipv1.SignedVAAWithQuorum) { diff --git a/node/pkg/processor/processor.go b/node/pkg/processor/processor.go index 0cf9fb6a43..3bedd0fe00 100644 --- a/node/pkg/processor/processor.go +++ b/node/pkg/processor/processor.go @@ -19,6 +19,10 @@ import ( "github.com/certusone/wormhole/node/pkg/reporter" "github.com/certusone/wormhole/node/pkg/supervisor" "github.com/wormhole-foundation/wormhole/sdk/vaa" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + dto "github.com/prometheus/client_model/go" ) type ( @@ -86,7 +90,7 @@ type Processor struct { // gossipSendC is a channel of outbound messages to broadcast on p2p gossipSendC chan<- []byte // obsvC is a channel of inbound decoded observations from p2p - obsvC chan *gossipv1.SignedObservation + obsvC chan *common.MsgWithTimeStamp[gossipv1.SignedObservation] // obsvReqSendC is a send-only channel of outbound re-observation requests to broadcast on p2p obsvReqSendC chan<- *gossipv1.ObservationRequest @@ -127,13 +131,29 @@ type Processor struct { pythnetVaas map[string]PythNetVaaEntry } +var ( + observationChanDelay = promauto.NewHistogram( + prometheus.HistogramOpts{ + Name: "wormhole_signed_observation_channel_delay_us", + Help: "Latency histogram for delay of signed observations in channel", + Buckets: []float64{10.0, 20.0, 50.0, 100.0, 1000.0, 5000.0, 10000.0}, + }) + + observationTotalDelay = promauto.NewHistogram( + prometheus.HistogramOpts{ + Name: "wormhole_signed_observation_total_delay_us", + Help: "Latency histogram for total time to process signed observations", + Buckets: []float64{10.0, 20.0, 50.0, 100.0, 1000.0, 5000.0, 10000.0}, + }) +) + func NewProcessor( ctx context.Context, db *db.Database, msgC <-chan *common.MessagePublication, setC <-chan *common.GuardianSet, gossipSendC chan<- []byte, - obsvC chan *gossipv1.SignedObservation, + obsvC chan *common.MsgWithTimeStamp[gossipv1.SignedObservation], obsvReqSendC chan<- *gossipv1.ObservationRequest, injectC <-chan *vaa.VAA, signedInC <-chan *gossipv1.SignedVAAWithQuorum, @@ -181,6 +201,16 @@ func (p *Processor) Run(ctx context.Context) error { if p.acct != nil { p.acct.Close() } + + // Log these as warnings so they show up in the benchmark logs. + metric := &dto.Metric{} + _ = observationChanDelay.Write(metric) + p.logger.Warn("PROCESSOR_METRICS", zap.Any("observationChannelDelay", metric.String())) + + metric = &dto.Metric{} + _ = observationTotalDelay.Write(metric) + p.logger.Warn("PROCESSOR_METRICS", zap.Any("observationProcessingDelay", metric.String())) + return ctx.Err() case p.gs = <-p.setC: p.logger.Info("guardian set updated", @@ -216,6 +246,7 @@ func (p *Processor) Run(ctx context.Context) error { case v := <-p.injectC: p.handleInjection(ctx, v) case m := <-p.obsvC: + observationChanDelay.Observe(float64(time.Since(m.Timestamp).Microseconds())) p.handleObservation(ctx, m) case m := <-p.signedInC: p.handleInboundSignedVAAWithQuorum(ctx, m) From 761c917427a792223bda10ccd8aedcd1cfd4f4ef Mon Sep 17 00:00:00 2001 From: tbjump Date: Thu, 13 Jul 2023 16:51:22 +0000 Subject: [PATCH 19/23] node/processor: Make time configs public --- node/pkg/node/node_test.go | 2 +- node/pkg/processor/backoff.go | 2 +- node/pkg/processor/backoff_test.go | 20 ++++++++++---------- node/pkg/processor/cleanup.go | 8 +++++--- node/pkg/processor/processor.go | 13 +++++++------ 5 files changed, 24 insertions(+), 21 deletions(-) diff --git a/node/pkg/node/node_test.go b/node/pkg/node/node_test.go index 219d4a9cfc..a87421c44e 100644 --- a/node/pkg/node/node_test.go +++ b/node/pkg/node/node_test.go @@ -713,7 +713,7 @@ func runConsensusTests(t *testing.T, testCases []testCase, numGuardians int) { if testCase.mustNotReachQuorum { assert.EqualError(t, err, "rpc error: code = NotFound desc = requested VAA not found in store") } else if testCase.mustReachQuorum { - assert.NotNil(t, r) + require.NotNil(t, r) returnedVaa, err := vaa.Unmarshal(r.VaaBytes) assert.NoError(t, err) diff --git a/node/pkg/processor/backoff.go b/node/pkg/processor/backoff.go index 7f006dcc1a..720e47bc44 100644 --- a/node/pkg/processor/backoff.go +++ b/node/pkg/processor/backoff.go @@ -7,7 +7,7 @@ import ( func nextRetryDuration(ctr uint) time.Duration { m := 1 << ctr - wait := firstRetryMinWait * time.Duration(m) + wait := FirstRetryMinWait * time.Duration(m) jitter := time.Duration(mathrand.Int63n(int64(wait))) // nolint:gosec return wait + jitter } diff --git a/node/pkg/processor/backoff_test.go b/node/pkg/processor/backoff_test.go index 96e900b62f..0d010170c1 100644 --- a/node/pkg/processor/backoff_test.go +++ b/node/pkg/processor/backoff_test.go @@ -9,19 +9,19 @@ import ( func TestBackoff(t *testing.T) { for i := 0; i < 10; i++ { - assert.Greater(t, firstRetryMinWait*1*2+time.Second, nextRetryDuration(0)) - assert.Less(t, firstRetryMinWait*1-time.Second, nextRetryDuration(0)) + assert.Greater(t, FirstRetryMinWait*1*2+time.Second, nextRetryDuration(0)) + assert.Less(t, FirstRetryMinWait*1-time.Second, nextRetryDuration(0)) - assert.Greater(t, firstRetryMinWait*2*2+time.Second, nextRetryDuration(1)) - assert.Less(t, firstRetryMinWait*2-time.Second, nextRetryDuration(1)) + assert.Greater(t, FirstRetryMinWait*2*2+time.Second, nextRetryDuration(1)) + assert.Less(t, FirstRetryMinWait*2-time.Second, nextRetryDuration(1)) - assert.Greater(t, firstRetryMinWait*4*2+time.Second, nextRetryDuration(2)) - assert.Less(t, firstRetryMinWait*4-time.Second, nextRetryDuration(2)) + assert.Greater(t, FirstRetryMinWait*4*2+time.Second, nextRetryDuration(2)) + assert.Less(t, FirstRetryMinWait*4-time.Second, nextRetryDuration(2)) - assert.Greater(t, firstRetryMinWait*8*2+time.Second, nextRetryDuration(3)) - assert.Less(t, firstRetryMinWait*8-time.Second, nextRetryDuration(3)) + assert.Greater(t, FirstRetryMinWait*8*2+time.Second, nextRetryDuration(3)) + assert.Less(t, FirstRetryMinWait*8-time.Second, nextRetryDuration(3)) - assert.Greater(t, firstRetryMinWait*1024*2+time.Second, nextRetryDuration(10)) - assert.Less(t, firstRetryMinWait*1024-time.Second, nextRetryDuration(10)) + assert.Greater(t, FirstRetryMinWait*1024*2+time.Second, nextRetryDuration(10)) + assert.Less(t, FirstRetryMinWait*1024-time.Second, nextRetryDuration(10)) } } diff --git a/node/pkg/processor/cleanup.go b/node/pkg/processor/cleanup.go index c72baf099a..96e24627cc 100644 --- a/node/pkg/processor/cleanup.go +++ b/node/pkg/processor/cleanup.go @@ -57,10 +57,12 @@ var ( const ( settlementTime = time.Second * 30 - retryTime = time.Minute * 5 retryLimitOurs = time.Hour * 24 retryLimitNotOurs = time.Hour - firstRetryMinWait = time.Minute * 5 +) + +var ( + FirstRetryMinWait = time.Minute * 5 ) // handleCleanup handles periodic retransmissions and cleanup of observations @@ -148,7 +150,7 @@ func (p *Processor) handleCleanup(ctx context.Context) { p.logger.Info("expiring unsubmitted observation after exhausting retries", zap.String("digest", hash), zap.Duration("delta", delta), zap.Bool("weObserved", s.ourMsg != nil)) delete(p.state.signatures, hash) aggregationStateTimeout.Inc() - case !s.submitted && delta >= firstRetryMinWait && time.Since(s.nextRetry) >= 0: + case !s.submitted && delta >= FirstRetryMinWait && time.Since(s.nextRetry) >= 0: // Poor observation has been unsubmitted for five minutes - clearly, something went wrong. // If we have previously submitted an observation, and it was reliable, we can make another attempt to get // it over the finish line by sending a re-observation request to the network and rebroadcasting our diff --git a/node/pkg/processor/processor.go b/node/pkg/processor/processor.go index 3bedd0fe00..4f8dfd7d77 100644 --- a/node/pkg/processor/processor.go +++ b/node/pkg/processor/processor.go @@ -25,6 +25,9 @@ import ( dto "github.com/prometheus/client_model/go" ) +var GovInterval = time.Minute +var CleanupInterval = time.Second * 30 + type ( // Observation defines the interface for any events observed by the guardian. Observation interface { @@ -122,8 +125,6 @@ type Processor struct { state *aggregationState // gk pk as eth address ourAddr ethcommon.Address - // cleanup triggers periodic state cleanup - cleanup *time.Ticker governor *governor.ChainGovernor acct *accountant.Accountant @@ -190,10 +191,10 @@ func NewProcessor( } func (p *Processor) Run(ctx context.Context) error { - p.cleanup = time.NewTicker(30 * time.Second) + cleanup := time.NewTicker(CleanupInterval) // Always initialize the timer so don't have a nil pointer in the case below. It won't get rearmed after that. - govTimer := time.NewTimer(time.Minute) + govTimer := time.NewTimer(GovInterval) for { select { @@ -250,7 +251,7 @@ func (p *Processor) Run(ctx context.Context) error { p.handleObservation(ctx, m) case m := <-p.signedInC: p.handleInboundSignedVAAWithQuorum(ctx, m) - case <-p.cleanup.C: + case <-cleanup.C: p.handleCleanup(ctx) case <-govTimer.C: if p.governor != nil { @@ -280,7 +281,7 @@ func (p *Processor) Run(ctx context.Context) error { } } if (p.governor != nil) || (p.acct != nil) { - govTimer = time.NewTimer(time.Minute) + govTimer.Reset(GovInterval) } } } From 940a906e191b3bf6831957051ac419005ea0e551 Mon Sep 17 00:00:00 2001 From: tbjump Date: Thu, 13 Jul 2023 17:05:07 +0000 Subject: [PATCH 20/23] node/node_test: Test automatic re-observation requests --- node/pkg/node/node_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/node/pkg/node/node_test.go b/node/pkg/node/node_test.go index a87421c44e..cebfc188c7 100644 --- a/node/pkg/node/node_test.go +++ b/node/pkg/node/node_test.go @@ -25,6 +25,7 @@ import ( "github.com/certusone/wormhole/node/pkg/common" "github.com/certusone/wormhole/node/pkg/db" "github.com/certusone/wormhole/node/pkg/devnet" + "github.com/certusone/wormhole/node/pkg/processor" gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1" publicrpcv1 "github.com/certusone/wormhole/node/pkg/proto/publicrpc/v1" "github.com/certusone/wormhole/node/pkg/readiness" @@ -505,6 +506,10 @@ func TestMain(m *testing.M) { // TestConsensus tests that a set of guardians can form consensus on certain messages and reject certain other messages func TestConsensus(t *testing.T) { + // adjust processor time intervals to make tests pass faster + processor.FirstRetryMinWait = time.Second * 3 + processor.CleanupInterval = time.Second * 1 + const numGuardians = 4 // Quorum will be 3 out of 4 guardians. msgZeroEmitter := someMessage() @@ -556,6 +561,11 @@ func TestConsensus(t *testing.T) { mustReachQuorum: true, performManualReobservationRequest: true, }, + { // Only one Guardian makes the observation while watching and needs to do an automatic re-observation request. + msg: someMessage(), + numGuardiansObserve: 1, + mustReachQuorum: true, + }, { // Message covered by Governor that should pass immediately msg: governedMsg(false), numGuardiansObserve: numGuardians, From 5028600a1c5bb0d47b345fcc715398952497c5d6 Mon Sep 17 00:00:00 2001 From: tbjump Date: Thu, 13 Jul 2023 18:08:05 +0000 Subject: [PATCH 21/23] node/node_test: prePopulateVAA --- node/pkg/node/node_test.go | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/node/pkg/node/node_test.go b/node/pkg/node/node_test.go index cebfc188c7..6a132fb971 100644 --- a/node/pkg/node/node_test.go +++ b/node/pkg/node/node_test.go @@ -78,6 +78,7 @@ type mockGuardian struct { guardianAddr eth_common.Address ready bool config *guardianConfig + db *db.Database } type guardianConfig struct { @@ -146,6 +147,7 @@ func mockGuardianRunnable(t testing.TB, gs []*mockGuardian, mockGuardianIndex ui // setup db db := db.OpenDb(logger, nil) defer db.Close() + gs[mockGuardianIndex].db = db // set environment env := common.GoTest @@ -346,6 +348,8 @@ type testCase struct { // if true, the test environment will inject a reobservation request signed by Guardian 1, // as if that Guardian had made a manual reobservation request through an admin command performManualReobservationRequest bool + // if true, we will put the VAA into each guardian's DB + prePopulateVAA bool // if true, assert that a VAA eventually exists for this message mustReachQuorum bool // if true, assert that no VAA exists for this message at the end of the test. @@ -524,6 +528,15 @@ func TestConsensus(t *testing.T) { // define the test cases to be executed // The ones with mustNotReachQuorum=true should be defined first to give them more time to execute. testCases := []testCase{ + { + // Only two Guardian gets the message, but one already has it in the local database. + // Hence the first Guardian (index 0) should not make an automatic re-observation request + // We currently don't explicitly verify the non-existence of the re-observation request, but can see it through the code coverage + msg: someMessage(), + numGuardiansObserve: 2, + mustReachQuorum: true, + prePopulateVAA: true, + }, { // one malicious Guardian makes an observation + sends a re-observation request; this should not reach quorum msg: someMessage(), numGuardiansObserve: 1, @@ -627,6 +640,16 @@ func runConsensusTests(t *testing.T, testCases []testCase, numGuardians int) { assert.NoError(t, err) } + // pre-populate VAAs + for _, testCase := range testCases { + if testCase.prePopulateVAA { + v := testCase.msg.CreateVAA(guardianSetIndex) + v.Signatures = []*vaa.Signature{{Index: 0}} + err := gs[0].db.StoreSignedVAA(v) + assert.NoError(t, err) + } + } + // Wait for them to connect each other and receive at least one heartbeat. // This is necessary because if they have not joined the p2p network yet, gossip messages may get dropped silently. assert.True(t, WAIT_FOR_LOGS || WAIT_FOR_METRICS) @@ -728,8 +751,10 @@ func runConsensusTests(t *testing.T, testCases []testCase, numGuardians int) { assert.NoError(t, err) // Check signatures - err = returnedVaa.Verify(gsAddrList) - assert.NoError(t, err) + if !testCase.prePopulateVAA { // if the VAA is pre-populated with a dummy, then this is expected to fail + err = returnedVaa.Verify(gsAddrList) + assert.NoError(t, err) + } // Match all the fields assert.Equal(t, returnedVaa.Version, uint8(1)) From 94321a2d844996c299c9753d61ed701fc0288896 Mon Sep 17 00:00:00 2001 From: Kevin Peters Date: Tue, 18 Jul 2023 15:50:16 +0000 Subject: [PATCH 22/23] sdk/js: v0.9.22 release --- sdk/js/CHANGELOG.md | 6 ++++++ sdk/js/package-lock.json | 4 ++-- sdk/js/package.json | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/sdk/js/CHANGELOG.md b/sdk/js/CHANGELOG.md index 2267799790..80e0419c6a 100644 --- a/sdk/js/CHANGELOG.md +++ b/sdk/js/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.9.22 + +### Added + +Base mainnet contract addresses + ## 0.9.21 ### Changes diff --git a/sdk/js/package-lock.json b/sdk/js/package-lock.json index 4135f4618f..164640ab05 100644 --- a/sdk/js/package-lock.json +++ b/sdk/js/package-lock.json @@ -1,12 +1,12 @@ { "name": "@certusone/wormhole-sdk", - "version": "0.9.21", + "version": "0.9.22", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@certusone/wormhole-sdk", - "version": "0.9.21", + "version": "0.9.22", "license": "Apache-2.0", "dependencies": { "@certusone/wormhole-sdk-proto-web": "0.0.6", diff --git a/sdk/js/package.json b/sdk/js/package.json index 463aa95ba3..b7df3bfefa 100644 --- a/sdk/js/package.json +++ b/sdk/js/package.json @@ -1,6 +1,6 @@ { "name": "@certusone/wormhole-sdk", - "version": "0.9.21", + "version": "0.9.22", "description": "SDK for interacting with Wormhole", "homepage": "https://wormhole.com", "main": "./lib/cjs/index.js", From 94dedc2cbdc7915e0c67b8e7fdbf18d219f7c9c8 Mon Sep 17 00:00:00 2001 From: bruce-riley <96066700+bruce-riley@users.noreply.github.com> Date: Thu, 20 Jul 2023 13:02:40 -0500 Subject: [PATCH 23/23] Node/Deploy: Add base VAAs (#3226) --- deployments/mainnet/nftBridgeVAAs.csv | 3 ++- deployments/mainnet/tokenBridgeVAAs.csv | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/deployments/mainnet/nftBridgeVAAs.csv b/deployments/mainnet/nftBridgeVAAs.csv index f9a94c409a..0a94eff227 100644 --- a/deployments/mainnet/nftBridgeVAAs.csv +++ b/deployments/mainnet/nftBridgeVAAs.csv @@ -13,4 +13,5 @@ Celo (14) NFT Bridge,01000000030d0263d5c79a244b703002adc5b73a199269ee18420a83518 Moonbeam (16) NFT Bridge,01000000030d027c278a867a1fccaad86abda98823e88f72beb5d24443f73e54aa54a18d940b910c43e937f301f71631977fc372ec0c16e81f52f5bb68999ec76a9888a46b82df0103475264d19fda61894e6733818703882b60574472b58fadf06f57a7898bf0a1ac19be8da74b552e99a70a00f1d384c45ce6c860bf5595b5a8d3f2a3c8213d2ec90004c1f29bed4587eab2e79dcebd510b952565a3fa7724f3e9d418232c03f30723a96c46589c5c2520a4a8099f4a9f681be5937dd97e41ee89aa47c4af802f3153b90105c21f38d9e5ffdb145e31cc5986542294528a1d904b037c733cf689f57a108b6d27e7f0acd5936b39bba443b9924effc4cfa1c8f79926d632f080c463c6092acb01069d5ea984697349505dd3b7d6cee7450deb957fbaf5c399ba37a4b2813e26728e08cc0182b02b2d283d9b7ea0e655b3e377f09446de9a57c47f0aa393cb22f50101075fa7fae6f405c08b876711341a96f391bbceb99c158f716d4545ded8cf2a4da51ec8de6ee8f70cf0b59a3efb9bb424308041974ced4f338e41c50b055b822686010847b3db69dc6f0f0e9ed638800069f036902cc1bbab4644bc0bade650451619eb438675e5aae57777747315b315d3c484cb2789d942b98bc299ad2b6de89263d00109cfb0a27732f55aa5052dd585bf94b0f26b961e76d39a3b9d11ee3207b9b73eb81789286b01fc82f87a07efd03761f9cd7602371d80f2afba0fb8768b6e596e7b000c338f9035c27a0cff17be1890dab4dafe92580385238db217683cafc30c9ff1e96f3dca3cd9eb8824ae92be655759334a7985b0f8f8afc9e672b023ff51c316ba000d0c5f5a36ba8128f14a6faa8b9de50ecd542a02ac5050b1efb9dd405f6fc67a7c07f271f7131578b1b7e9a4bee01b45adda565d1490a985f9b610e018e7222167010f4c6873e8cae7a937d50dcb14f99117bee6e3b2876ea877a19212d1dfbd4789e46a6185c4f854ea3e175469a9b369d5a5d129296fc3d41f55135eabc7f11549c401106c97984735b6754e0c40603525c188a1fefc4f07715d3e5c9fcc42bda40bccf11e673815e7515d56c69c4dcedcea09fff1c3ca58d57bbde8a27dfaa7ad77f96d0011da29584ca47e223d3068d91101068f96651aa0e1b8bcebb2c016960b58fbb979396f31b4f9a1a1201f7cc44fd88ec25dd739111510f9e5e4548172d6e4a081fe0100000000bf64a8c200010000000000000000000000000000000000000000000000000000000000000004b09124788a3e42902000000000000000000000000000000000000000000000004e46544272696467650100000010000000000000000000000000453cfbe096c0f8d763e8c5f24b441097d577bde2 Aptos (22) NFT Bridge,01000000030e00b8f342f2ff02181898963d527dcf9be957bffc2ac99e5a2843c384d927e4eff443e37df202e58723c8b5089365b323e32440c56510d4bb31d7ba80d0209b678a0002a2f5059713c7f4296a8d68ffdfdd069ce5298da796b25e8a055f93e79bd70fdb5399ee82bf1ef0feea0ce574139a01d85c167ac4ddd334bcfa4bd2a863d28ffd00031477fbb8cb8574acb482efac8464b55f788b3acbea52e5c4705b9d0fc561b1cc19a4113e649f574823c03f56d309e824dde34ce91a9808e5214b2b220f09085201042819a071eb387495e7e57e790621ebe00057b5657004becbffb5164981bcc8f9470183439f62b9f7da7f3b09d1e00742fdb5601e84327fd969f22973eaccd2a9010562ec32ea5e4c327e69444c6553befa6de6453311c97d4a01e82dfd63189e6f73581af86f622708ceb563d63b05666b09588e4233d2edd6c1dc39742e4943dc040107bcc05555fc96d4966ad4c8387768ec312d14ad724d62f3206879029fd6415c3a402e0634149e0a12544cb068ae660def87a1d2e4f7e23e3ea422ea386c2fd5d5010809154249651a415705658d5f5d7f6049fa23c1f002bf00e3e4d49d51c597fa8b7ab0f5146a226d56a36b95efac59db27e4ee0741c6014fe55004e4a234f6842a01097e289f8bfbbfa29c9c4fc4f3c0d2b64e8bed9921dabc7362a6c125093a41af8a1daad45cb8b0eb18d3f6684368812f7a904b3203081a7b20aa26a7b4726dd483000a47d5067e1955034d732722120e5bfd074a1142bc7619c98d5b9cf37a856c92401618bda2b3c0e2d7a9eced68e4867c83b96bed9d3630c2b276e714be65f1cb39000c24c594eec0802315e67b7074954c0b4a050dc24e5b09741d15154bbe075aec8c015c86968ced954ada7fa9799783f495302512dc5c45a38c6ce778f0b6da73c8000d00db3542acac0b5dec0f63dcefd3d7411dc2c7e48feeb7efcfd28ff2172f39d139811ad02184376819a5b2c582cfb6b92d414af31e04526f02568eb6a49027b1001013d822a17d5af2021e6f7075508749478cf3b081ecfeb698ce4f679cf695444c55818d9e8c003a05674bda642f119f0483e9d2552e6b833fe4a4baef056c687800111b8900571c773f2dd6b11f0ca728d6f52489239a9aec11f47504b3935035b1806703d9ae7857b62f891587c5c9610a16ddbfb245c1e43f8841db5210ec2437eb011266f914c5b1fe8af0bdba9e4c8060453d951d8107bc78764f3eec110799c7b75a1299d51c80e155464f0cc490272938f182cf51bededb7b26fea96d00b1e54011010000000054d9b29b00010000000000000000000000000000000000000000000000000000000000000004d498b724f89a5faa2000000000000000000000000000000000000000000000004e465442726964676501000000160000000000000000000000000000000000000000000000000000000000000005 Arbitrum (23) NFT Bridge,01000000030e008bde59c741a9c84df6e5e7306a63d21bf5d3fcf83a081be4d8bbd5c355b37b4f597852bd173eec2f67d78e3993335e70e9e4900cccc3d9d89d3656fb17f1346d010304208fdc8353c494ef9614a90bda9a7a1dde7646317b9ebb607840db37aad52309168433dc688fad3980728c46d380d2aeaadc827bbf991c2051aaceb0b8074301049e66babe85d4ca61acbf0afe42647f7a3c23abd95f5a28a8e914d2805247ecde2fbda69d1db282c5ed517e40191b7a77859baf4c9d18269015bc2bf213a616d90005d9168152262d4062d02a4791e7158c128a85d5cc438ec80a5726a3d4d5c600ef61900b10a1d89b66d2d14a9897d2e3e80ff37e2a5ba99d5b0738ba7c470f908c000652c13a2267f139ee1c71841568433bbc5447e21ad41cdb5a2e2e39e668b024b9708f7f1e17b4a89dea05410e57dedf32ae301d05534d8a783bfea31de826f76d00070ec67d3bd18b94402553e1993cdfd0a3f80e1e3043967283d8f87a95bb0b877e09040fdaafdbb93555ba23a347f05771c27918732acf2f5a7736cada13097bb4000894f73504ca75547727ed276d8c9e52365ac4b4f56bf43e85c1b8490d6a1bc6f6319c70ae2fa519dce95e2abe0126a2a8a2f298b1d9f44449dec5bc492e8a756500091991a74bb229aca9087e313f91af986c4d0ab8d039f2273c27b663277fcb47f308b931816d1f1390060c03482234f1a706b7f6a83746223ba4f7b306a101fc98010b75f1019131f0030f6f56eb8d4fcdbadf6369f488da38a001b22351fbccbe2b2b51d6e5c9b82cf29acddefd5add8b05e6c30c175bb9466da335e63be9d6d4c518010c54e7ebe13e848457624e674748013997c48d9507cbcc12173c89c5b9b6df82014ec99ce0c3940da296bcca623181a26f715d854ad648894cfb74039fe5fd85bf000dc92b22236a1195bc83fad9ee475a32c8e18f88f956a87a160f0da08f31354d2441b1952bb7cd290a93cc55cc783451c3f020f642bf63617234f7a35dee0c4076010f32db50b935d5c3384a038aff7b9727fe41e73bc46db6ccb73996ffb362d6405966e2731e48339691c33a6dfe4a6dd3d374e88e466d80177aff223c5056423d660010078747b0fa64ff87bc34b6ed544439d1634754ab99ad49a3c649eb21567ede0c44ece6965dd55c1dff495f1ed3b70f78d6a872489a89560ad8ebf2461aacf63500121daa8ab0680206b267ffaf532ff73b1bd54b7e40e8ba0da72180cc4dc0705e6441f6a5f0f8d3498feca84c89112dac88b8edce7186fa476ba65e6a5cde9196cd0100000000cee78285000100000000000000000000000000000000000000000000000000000000000000045d30edcfd5559bf22000000000000000000000000000000000000000000000004e465442726964676501000000170000000000000000000000003dd14d553cfd986eac8e3bddf629d82073e188c8 -Optimism (24) NFT Bridge,01000000030e009d60423b92b72aec94e491b16a1856665cf65a5e3328564a55c92abe3ef564bf3153e541366eea5bee0115e3ff3e9da42b97b32dc04ee99f18d33265486a5dad0102adccb42ceb38d4a8ec6a8bc0c118520484264967e9c0bce49373fbf54d4980357b642cfb949128760f36ccb9f574afe301a363545c3236275dd4e7cb8184f329000397f9af5fb1423c6912d332de9ad9f0a144f1cfb71d40aea91fb1ba2337be50250171419d89edc1656ba45525b67abcd202343cd00df1ce965859536554dce3040104559761877722562b0a39cf7817a7dad52ea794e99ba3c202e8303144cc195ba14d305620b870ba96a132f30299a9f6d27793550bad4ab16ad8aac91c72838d7f0105658436eee5956bec76402f01582013155c0199d76193fa74f7926ed1c84c93d0224739e5aee380677d94b0adf864a1e2cdb77fb6740bc000dd2207e16421aa780107904cf4c0150ce0662916a3ba8e11186b09ce3da06636dca0788dd578ed4667975245bdf2741c93240a9709bcc168d09da04377350545b2303999650932ce7abd0008912993e689dd37b7e4c33ecfbe34c5ee7d00b2774e31608edaad9b6b77bca80f7ab94e5e136b35fd329b68d5d690df1a4a22c190989562a0411b3d9456f86c8e01092a2823aef77c0fd2c0a685ad703d637afb58554025ad0c2d5ba534896f019b98098b587b44f7449b3f98057aae69d43c31eda8af31f008947e0d9d22e4badb51010a8d5c207e4991b2781902c428cd52dad3d953ce55c2590c890b6670e16eff9a27011813b0b2c41cc7c58501ccfbcea2d8cf187b20b94ee7fa4194b1a060871530010c81261db5d93cb7673b22bf35c6b0d4f943ba7ae0f3f0ab47856dc0f93d1da0796770ec54c17c9ca9b1cb61cb3103a9f1807ccf94610d19e3f5f59177a99f467e010dba5dc0c654fd7299d4dead1ced1f950d326cab0bd1028ab71e216ec95a3aa66b555d7af768244ce3ab3105fc31378db13a981d6e5f8c640d63bb58e393ef15ba011058b3076008534cbc8b6432068e9d666375a66ce6d1bdcdea71a6854897dd7dfe40f62304d641ab467832a88a963465affcd82ad5d76cdd413bb87482f2298e6e0111f09630696430245be1ac67c2c346d0823d2ad539d6d6439874a1a7a7a81abbbf50c6228d13c896f59ef62e26b2814a35de4c7c2d07d3d28ae269bc62d444760a00121ce1f3eaa677b04b64ab07ba92d8a3fee5f4312c64ea521fca751c68f2133c0b460ee00235b4ef7d374ce3aff4d91e1c304f6c16fd94f98b74fb6b1079073e6f01000000006a2a60dd00010000000000000000000000000000000000000000000000000000000000000004cbb704b14864918a2000000000000000000000000000000000000000000000004e46544272696467650100000018000000000000000000000000fe8cd454b4a1ca468b57d79c0cc77ef5b6f64585 \ No newline at end of file +Optimism (24) NFT Bridge,01000000030e009d60423b92b72aec94e491b16a1856665cf65a5e3328564a55c92abe3ef564bf3153e541366eea5bee0115e3ff3e9da42b97b32dc04ee99f18d33265486a5dad0102adccb42ceb38d4a8ec6a8bc0c118520484264967e9c0bce49373fbf54d4980357b642cfb949128760f36ccb9f574afe301a363545c3236275dd4e7cb8184f329000397f9af5fb1423c6912d332de9ad9f0a144f1cfb71d40aea91fb1ba2337be50250171419d89edc1656ba45525b67abcd202343cd00df1ce965859536554dce3040104559761877722562b0a39cf7817a7dad52ea794e99ba3c202e8303144cc195ba14d305620b870ba96a132f30299a9f6d27793550bad4ab16ad8aac91c72838d7f0105658436eee5956bec76402f01582013155c0199d76193fa74f7926ed1c84c93d0224739e5aee380677d94b0adf864a1e2cdb77fb6740bc000dd2207e16421aa780107904cf4c0150ce0662916a3ba8e11186b09ce3da06636dca0788dd578ed4667975245bdf2741c93240a9709bcc168d09da04377350545b2303999650932ce7abd0008912993e689dd37b7e4c33ecfbe34c5ee7d00b2774e31608edaad9b6b77bca80f7ab94e5e136b35fd329b68d5d690df1a4a22c190989562a0411b3d9456f86c8e01092a2823aef77c0fd2c0a685ad703d637afb58554025ad0c2d5ba534896f019b98098b587b44f7449b3f98057aae69d43c31eda8af31f008947e0d9d22e4badb51010a8d5c207e4991b2781902c428cd52dad3d953ce55c2590c890b6670e16eff9a27011813b0b2c41cc7c58501ccfbcea2d8cf187b20b94ee7fa4194b1a060871530010c81261db5d93cb7673b22bf35c6b0d4f943ba7ae0f3f0ab47856dc0f93d1da0796770ec54c17c9ca9b1cb61cb3103a9f1807ccf94610d19e3f5f59177a99f467e010dba5dc0c654fd7299d4dead1ced1f950d326cab0bd1028ab71e216ec95a3aa66b555d7af768244ce3ab3105fc31378db13a981d6e5f8c640d63bb58e393ef15ba011058b3076008534cbc8b6432068e9d666375a66ce6d1bdcdea71a6854897dd7dfe40f62304d641ab467832a88a963465affcd82ad5d76cdd413bb87482f2298e6e0111f09630696430245be1ac67c2c346d0823d2ad539d6d6439874a1a7a7a81abbbf50c6228d13c896f59ef62e26b2814a35de4c7c2d07d3d28ae269bc62d444760a00121ce1f3eaa677b04b64ab07ba92d8a3fee5f4312c64ea521fca751c68f2133c0b460ee00235b4ef7d374ce3aff4d91e1c304f6c16fd94f98b74fb6b1079073e6f01000000006a2a60dd00010000000000000000000000000000000000000000000000000000000000000004cbb704b14864918a2000000000000000000000000000000000000000000000004e46544272696467650100000018000000000000000000000000fe8cd454b4a1ca468b57d79c0cc77ef5b6f64585 +Base (30) NFT Bridge,01000000030e0244b05bf7235695b63627c86eed6ff96e17602b4f44bc5c39e6aabe36b75a5fa4796f253864fe5268480894c0a65283d14740a0ccc1cceb8c04208ea93a45ac2d010383a46e27fcfb607fce71384baafdb5af8415064d8d36100a89e2a5607632d06d3f4423391206c1acbfb1783a0bda32320e4daf78d50437352782cfc4ea95371d0105c4206607233ce6ac9c6f5c3696a17ba7769b79d7d01b72414c0d66f3f320977b43ab5766035fcfdcf700f59b7e91c2a4f4b288ad7fc4bdabd41bc8a2b991ada30007060dee194e150303e24ec672b60d9a52375ddbbc0b1193be8d8ba5d5b1c5a546531d444b28f89fe82fc22d9ba18e0f770208c1c0eaddf52756cd609a23b78b2501087479ab444d381814fd7e4778dbede50e6e803cc4bb800ded4589c9f5f638ac37660dcbedef73365dfb661858261d793c1a6d60201f9fdc9e1907f8238016c06b0009ed43918a6733c9716ee1cbc86c3b1f20f783a78e969a513e6ddaa6bacd1d7f7b737e745b8d29551b292f6372a085887316279f1b789c657438893f39753edc6c000a2d17048cd4fada98ee47ae2e59bd967ddc14ebc0442eea1424ddfa75d5fd8516353d12787d558847e145dfdcd5ab69c7425866a587e19e8eaf67e857a6f35331010b34d9e178ba387bad5a237287c06277721d279f6733fd60027337adab6b3f8e3356782cce80780efbddfcea959929651ee3c3a83b68c6367153ca4965b56ddc26000cd6807952724facb7adc79bf34644942bd186b27de6f28c34c77c980e71e545663c60149869baac14cda0ef1a4e0d50a6c7cc2753e2f761298687a8c672cb5d5b000d89b029934a34f5d9baf968978093831d22eb31468b690d02f87887a08568751812e67f4fea4f52da5a116cf51c2bf0410b35588dda12d50963d3610ea834e51d000e331eb40ff250722d56a739bd3267e099cf5bf4b608021a54dc517fa6f12d1ac012ac914502c5270cd9a5321f1a65cb73f0ec15eae104748122de55a6c910716b0110140f07813df50ee5615a9722b88eea5476962de214d12ea1703811b87ea30cea5ef486be7a137bdcdadeb0f0a7f41864e6cee0b9e4b78e7a53373dfe27eac12400119b2cc107b5a51dd3aa091e7eeb68105fab09ee50320868daa4585a9e8be3541c4ab9f358229166359b88f2d95c6d2aac50b451e21c912629558bbde69210ff340012c8273be78fae037efd6b27189ba0b4089c45ea5db3393b7aa4874e69fa655b04171dbbc85d1ce63f8bde06dad3946ccae7374ef4ab1b1a5404f82244ea0c53550100000000603f9da200010000000000000000000000000000000000000000000000000000000000000004f8c74e434d0159562000000000000000000000000000000000000000000000004e4654427269646765010000001e000000000000000000000000da3adc6621b2677bef9ad26598e6939cf0d92f88 \ No newline at end of file diff --git a/deployments/mainnet/tokenBridgeVAAs.csv b/deployments/mainnet/tokenBridgeVAAs.csv index 47e040ea09..5c2775e39b 100644 --- a/deployments/mainnet/tokenBridgeVAAs.csv +++ b/deployments/mainnet/tokenBridgeVAAs.csv @@ -21,4 +21,5 @@ Arbitrum (23) Token Bridge,01000000030e00e3b6513ce65007fd3bfa7b082e481ac527659a8 Optimism (24) Token Bridge,01000000030e0034614d6c092ddab582ba61976b34697c2f5bfc22b6dd070ec45db9a7df409f78586c9cde3b92fe9d7b1e6b12a954177d70125af0285c8fe7e6003483f95946370002c0d0e7d5841cf2448159e79e1c1587c79cb91aa926db2dc885c62a4639da3b2064ecfbf9827daca8cb2821a8e54ac927cc3aada700789dea3aa0ea0420e9f0fd010374e8665ab3d4da057625525eaee0ba008606c5cc3ebd4d8c1415f11593b1e19d7da43269cc199da176c51601767a7241d21c9673e18d4a3af42c33d7371152b700043d807c4f269e41b225873be372417f8f5aac00d2cee5d07f0edac1dc6d6f77b63b372f931ded0afa47ddd2ee1d9a83776397c29989b75b398ca1a51175c30ac70105d15c6fb69cc99df796feb08a7c2e6c1c28a4fdc93d2672a42858c797f23e5831529a3e152e814ecfc63f10f1d57887256475d3cda24c22eefff387411b610e3d0107ad3d9208100418b6d264da75b25044e8dd122d92b5f85e1a8ecb6a0f5a988efc465db0ea31b8274d7957618111899799c17c93a4a3752352eb4cb462ae0c915a0108800de72a5099a2c973ac9d4dd8a3a820ac574bf7a5826e7a8eec1f268bff39da59e86e7e5360ffc5b61591a40497fb6093021926ab5fa7499ea4f43fdbccaa0f01091f167188bfc31b575948edbe5adbf1c532226f0baad6973c34f57d5b81a54dde3c5826db5b543442e17ba8fc3e3524dbba0a9abbbde8b4808078702c0c5fced2010a4e1f40f6ca78ff3132b9acd9596f4d4257bdbceb34beac05d8fac07e23e5a62325621f1cb74a0a39850621a7b81d5fd08fe56c88257a4bc2b4f395145f37221a000cfacd7f14083511dff87b62f41d35a02c8d379927f431d8aa4f7cf2c594566ed30f1f951d3687bb7b86bb05726f1302292889c32376e2eb3349c3fbbb2619debd010d76d65f7e10775e5a76f50913042abac05261ae233451c6db1b0c4e7ca9311b4a05f2d67f65fa9766f6c657a92f384f47199eadce527b0bccf6378e5b21fef86b00109acaef1f871dc6223b7a3a6c628506b29b117023718e0b34aee888ad7eabdf1552b734d16c52350b9c2250d713f7f50476a370c47c0172a4510c0efd409602af0111b0dad38dfe1ae34cd8651d17a762459b9e2444362eee6fdabeb8e8ba08b5c37c120fe7d58ef31267238afae085e6c46f35996d6700739372f7940eab4c9b6b9c0112c682c5b9b09a22e7330c9646fcc88c52e6016510450c02e1d2fb3c9db43302604f7fd50249074fd61e6fa21ee2ee42b421ff22ba6f2e9feb8a0be3edd3c0023b010000000017c4c6a400010000000000000000000000000000000000000000000000000000000000000004562b3615eb987f3420000000000000000000000000000000000000000000546f6b656e42726964676501000000180000000000000000000000001d68124e65fafc907325e3edbf8c4d84499daa8b XPLA (28) Token Bridge,01000000030f00e94b4808ace06e1795157c10dd9e23f3936e326136f179a3fb7b00a18415f5a361c72b195a76d51928b1ed721bad2ef63f509ac852fd46f83fb5800cf2c9fbfc01036ca0758b0992c1a5f18cb5bacce13db20c18e03946084c36478e905d6e36e30b6ef593515ae8b0768a59c4d2632d52aefb8e4921449ba6268bb447de67e75d0d0104a2292bdc92363c459019a820e8834643f35b1477689e7e4a33e62ca9839e44ab3bc7562f893ed75a64afb245355f5a1155797de999aeda645cce8faf885547a0000523fa45cf92d7eeb11c36343ba1c5e17f99e417bee10d9f3a6b73a0e9b251fd46772fa1687d5d0c41e8480c511292bac1f521ed8d329189747abfce3da1f1128f010783727dd2756b30d7b77cbc179915ee150e9647105002d46e4f1d4ae44b19e7434aeef2bdd46c911fa4fdf01dacab3f80e246b752f24633218d0994adaf9f1cf10008192ab5ea8f2206d5b309a9834cb132067b588ab005bc38e181353b8ca9a3750d21be49e99ff8618a9231bec03e78a332379857384a0a130e93c01e6f6f8e32d300099ac58b0f16b460cc987aee0d06256875bbbd541254aefecf891946d93a8c70080183dc2255710acc87a8ccc1dbd41b14fefde1bbd5af2d28077eef081e8b22da010a20c1f57cc59c9fc896ffb347e0a3d4b8d2d8212448d9519a52b1fc5d0e6087f74913fe890c344c0369b30c41167ca19a405159e3ba0dacaa72406b23b0ee735f000beca147e892548b2b771061fcc7674fc027a4cdf86a273cae32be91d3d84a7620421f91562b00ef96da7c2f2a6e4513f1191a93f56d221d52724f3b3b7edf3f48010c24d08d8a33663ce9756c69c744b47ffee750d0f7f1c1a3cea105873f0c0a0279563f4b92b130ae3debd73c7342461d7d177ec9a9fe45d2b6175ba106ec292c86010de1694567f84b429c0fdeee88b0d65b8fbe0b1eb0f9574d6b6d99001bc8532d79203571d8360ef565c7108a25e2f7ade1d61cf808c0a2b436eb86ecd510e652c5010e7079a3a6c9f32ce20e726463d03aa650e81a677d452f76787e639984d6cf3b3e2cfea642b08c8ae6172e5e2f6c6114b787c6d9b2770ad17d1edad2917334bce8010f86d02cdc1cef14d43ddcfb47c9a16020da4feba940b4cfffa474365a3d9e31fa14dd4b10026415178ca4bf9449b9b30f86b4d79664e2f4abbcf183f221539286011136426e8ab92889c310079e1c85e5249d8ff5de32a9576edfefa4950379d6b2002c1ddeb03682d5b68e69b1c1a5aaf54e1763e6ac0ed1eaabdf8d90ab97e432a40012454e859e894e375defb136926a692171a3ddca9ef069ea1cd0501536cbcb3cfb1a4a700a837205237ce44393ed2cf2110592020a11c79ea27d2c3c09095c537e0000000000bb75823600010000000000000000000000000000000000000000000000000000000000000004c55a1c78b494ff3120000000000000000000000000000000000000000000546f6b656e427269646765010000001c8f9cf727175353b17a5f574270e370776123d90fd74956ae4277962b4fdee24c Sui (21) Token Bridge,01000000030d00a42d36048a27a763413bfa1d261daeb86f7feed7a9850d9915d8c44acee97dd4327c0351648ac4d496050e6af6534badd2b86416a5675f373e5a5ebf876505700002349ec8ae9aeef147e87b2b06a475d6ec760c20680285ecdd6b7b400cbd67504232e0cae965d27dc9f0a83fbed40ec0a759bd7ad9a014a69c90364a1df88048dc000323d01a78c01887d981e22ffb0f91d02c1d7f015393513db3f215af64bd9cb74829eb08fc9895f8b1247933dd23c2b71ac1bf5bbefb8bea829ab2f6b94317eaa901040305b91b17227313395a861365c8b110414e961ccf25a2b645226e6a307e488f6dace0896c354425f5dfb6b0cb968ef1f752653cd85179b9344f215b555601dd01060dcfd80b54b6f43e502b9628a5a2b92b453fa96718397ecbe0e279495ff37c0835fff5ee7f3b749de287a0c3440105ddd705d06975d56384792373645b7787df000715985ff2cf28e3a8062d9ffc7ef69fbd7c56082f0938cc564586d0dbcacf14986579e7d7a8629dc8a9cfcbf0c97c46ae6492d05c5fba193400746a43b6f1123d000a5db8adfca6d43dd345e130fa0cae250ac7cfa364a29d47cf219ce2b50d6f930a0e1bc399b5b92cdec3d00fcf6f2c7f0732996344812dae85afcfa077c67d94b8010bf68fe7c2ed3aaa180b01ba28052fa63d72509e642bf45f8b5c14c582d8e6eb99514d41ab7fd3f6451470e02a054a3630e347020b6330a8ec23efdc3e4da4550b010d7f3ec58dbb8ae21a2fb71941ed80d646469f1992e7fdc32706c327bfbe01b98011e8b377fce487237f9238fe9af09991f5da11d85aba5a4a81e99df8d066aead010e18e7de979a55bd568b26754fdd7d9e7b03572d742e5657f944ab35b44398a40e07a0c2399e13a244277138375e7e980bf6b666f39bc2f86afd2605f0249a5a53000f77d089279a354b7faa1f3fdc084f6e0ef684d9bcce8d9fb11b5568c0d0b215f15d54cc4383e1b7112fadc238f750b885f5f81a21f84e00ec4487a8064386cc2e01106d9d3067e19413e985f76852eb0cdd071fef659540ddf3a9d5610d492a68a13c61cd109f64c977c1274f9782dcbddfa46ee94331e02f98ba8fb37e22300bd63e0111819a499e30feb82190736054d2993918aeb591e3098b4df77630e93512fec4c122f2cccfe88f2b735f42a06571944d800f3dfcb07de7956330515ddb3c9a41360000000000764b7752000100000000000000000000000000000000000000000000000000000000000000047654167e9520c1b820000000000000000000000000000000000000000000546f6b656e4272696467650100000015ccceeb29348f71bdd22ffef43a2a19c1f5b5e17c5cca5411529120182672ade5 +Base (30) Token Bridge,01000000030d025ab9fcaa02dfe689251b2e7906e4a19d0badba35758178dcf2f17b80cde8d26c765848111afd52f03aede53c2f560e9760947d9cb2b8f0e5f1b841db41f9831e00039a06438cdc753aa4ae800f10c78fafff1296dc778449869104d26a944f8a735a57da0fe3c867a6741dd98ca589f7f8c293acd8507e8cdf9995cba118c97c59000005ffc63796fd35fab17fbf213f1e65295e5e0bf0c2d36c596c7750b3977b9cc41d1d2e63511abee319f05d753cc24de51d69a528e64c7664fdc01ab565e9dda816000680fa2ec4e1bcaf2e017a78642ee5fec60751b815a20f1e37fe31957478997fe61630aeefca76cc458dd44504a02fe34d880e81a960f34ee654bee523d5315cd10007d5a108f92a4ef89ca96f1d4153f94dfca65a469c1b0367f1e4ba24f8273ea3572173f3febd60c1f4a33ced3a56e284e23b90780fc1f1a1679057fd304a6a24570108f092e76509257f8eee0f630e18ed44046859aaa86451cb48ca8ece8b29f794205ea1a56b2a69a9d9c07ce21f91eed72d6fbebcc59eb17c06b4497338fa4c6a810009c778d7624d4dcfbf5c725ab3c057900eaee4ac9728c0e5a65216455c6c89afca4e27eea911e65bba285b4275e6b10f7cb4b119e940ee58981b986bdd4be650da000cd21048771f2be56761ba2a8501e28dd3c59e2ddffe1121156dad1cc2d0fb5bbd3423a7b19b6e024d834719fee64625b8e0715de37ba84dd74ce634e8c4a0f08c000d038bd77a6ffe73974b7ae8c9d7aed8029b6b9fa42c2f661630b00ed2b4b74df713ed414760db30f6bf762879cd27b44f6bd71bdcbd92cbdd4776d79636428c4d010e8cb6d1cd45bafc85a0574f1db14ee629511d40870eb1f7614d4e93a8c2997d1c3c413f59a8fc5daddee533bc9b6c3c286e8bd1a5dc11a4f23708ced3d4cd0fe2011006418481fb3854bf787d77b08bc264bd1152c64f44fa86e24c9864ea2440789335476848d347a85bf6c2e31a83b3874455d8a912bea8aeeb7db302a81e0e8343001129e206512e5047c51ff799eeaff663d95c41ff22ba9f21730e1dfd0298bc863f3db21622f6388ff38ed69bd2dccace0c2eb8df2d493a74729b4965181221093001125b9582ae58fe454f633d410266fdcd4a098ff98d9643d754f18f4184007598f929a70149b48ef58eaa5badbafbed9b0670df8195058b3c1628531508501e2fd00100000000a62a41cd00010000000000000000000000000000000000000000000000000000000000000004e4f4552fd06aa28820000000000000000000000000000000000000000000546f6b656e427269646765010000001e0000000000000000000000008d2de8d2f73f1f4cab472ac9a881c9b123c79627 Sei (32) Token Bridge,01000000030d03e6be465d35dbbcc957b5803283ee89586e56d0ff29f0a530d4fb96fdc69028221623127de2d6115fe060f55840053f1f9c73b8d4f5bf8043cbe9a77ad5f428fc00046ae1f149be927d2dd8cd4b5d38c9aa0d6b4e2883dd52e742fea2d20e22af25e10581732281dcf59e97755219ae64e408ee6be20c68059da62dbd8e6f86690da0000539b965de3dcfd312cfe5a1b1b3999d299b83d263b8817278bc7109c061649c513582c8ab46f819cc98f243e3b0b8dd543b7f1263d443dbef34ea1e9c023159380006dde501b50c618965ab1766d896d9b919effc337136a72795cf0c47a5460286c4006685ab5da24c21f8558c1a392b62291e0172deafaf486918b01f6ffc73d420010839a1b899424789485a8e2f5cfe59bd731ca62a37228ee7bd09252aa11e6995f93c650360e66ef878cdafb6feca334b0a4d69dadf802a8fd0e228ed0af5467d1701093ac07f14460a31e577bf0910718ee0dc4a28743d24c61e58e1e4f275ae05bb16238043863aa160e522b3a32ac276584129e98a6a21490ea26e0e60c9a71d6f4f010adb8ed8e54ab1f9f7a7073a0135bbdde881d2baf9699076ca15d0fc0438dc4a681d27659205fc3075b414881b602731fd6d53a2eba8c92c78337e8ccc00b325ab010bcc9c74a50ff0243ae8c3189449ca86ce66ede52a495b53d7461e4001b75e762e55a9ce5256b4d145189027c32f85d56a0a891214afaa521607858b9d7a8e043b010cd70ded635f6a295b4697cfe25adb8c10223e2253d0637ef7a0bba951e34f62bf0216e186e406be8ab707fdbab7cc5673c0de3177cbdbc6ca9591b27b5fed37d0000da39343ac0b8551d7bf5aaf4a2fb4c606649783ab2fa803012174a9974da5b8ca7c0e1633e35ccf8d539831df9636ddc613e654e7b3e207265b3457a6e6d81cae000e5d70e2931e4f223d5b06f87706f308c933f6247ffc3b261e8a9f5c75faddfa7c66a452ed16337b0bac026dd5974a82ea71ba32975ecd1cd490eee3326330a2b90010784a772c9a9d3ae20d3114c74c6eecaa3ff467b8e7f3199d0bd0d45bcc719a26579d6fdcd69f02d044e45118c94c92224a2da9a18f9891d82818fc170c2838ac0012cd71e7f13c2122ddae87806b2c531cb8ed6c7cb0df5243413f751914ffc46780294d91d651d4f76efd5fdffdd3859053c4204bce0fe63b9c3d88e5438a5a790701000000009f745aeb00010000000000000000000000000000000000000000000000000000000000000004a69638d9f869868020000000000000000000000000000000000000000000546f6b656e427269646765010000002086c5fd957e2db8389553e1728f9c27964b22a8154091ccba54d75f4b10c61f5e \ No newline at end of file