From 227a8a0c703c5f647aa7f2ec73a5da53381043d8 Mon Sep 17 00:00:00 2001 From: Joe C Date: Tue, 5 Mar 2024 09:36:38 -0600 Subject: [PATCH] program: introduce core BPF implementation --- Cargo.lock | 507 ++++++++++++++++++----------------- Cargo.toml | 2 +- program/Cargo.toml | 35 +++ program/build.rs | 28 ++ program/src/entrypoint.rs | 22 ++ program/src/error.rs | 33 +++ program/src/instruction.rs | 193 ++++++++++++++ program/src/lib.rs | 25 ++ program/src/processor.rs | 485 +++++++++++++++++++++++++++++++++ program/src/state.rs | 531 +++++++++++++++++++++++++++++++++++++ 10 files changed, 1623 insertions(+), 238 deletions(-) create mode 100644 program/Cargo.toml create mode 100644 program/build.rs create mode 100644 program/src/entrypoint.rs create mode 100644 program/src/error.rs create mode 100644 program/src/instruction.rs create mode 100644 program/src/lib.rs create mode 100644 program/src/processor.rs create mode 100644 program/src/state.rs diff --git a/Cargo.lock b/Cargo.lock index b9ada96..f552517 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,6 +21,24 @@ dependencies = [ "gimli", ] +[[package]] +name = "address-lookup-table" +version = "0.1.0" +dependencies = [ + "assert_matches", + "bincode", + "bytemuck", + "log", + "rustc_version", + "serde", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-program", + "solana-program-test", + "solana-sdk", + "spl-program-error", +] + [[package]] name = "adler" version = "1.0.2" @@ -65,9 +83,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ "getrandom 0.2.12", "once_cell", @@ -143,9 +161,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.79" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" [[package]] name = "aquamarine" @@ -383,7 +401,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -580,7 +598,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", "syn_derive", ] @@ -657,9 +675,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" [[package]] name = "bv" @@ -688,7 +706,7 @@ checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -736,9 +754,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723" dependencies = [ "jobserver", "libc", @@ -758,9 +776,9 @@ checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ "android-tzdata", "iana-time-zone", @@ -768,7 +786,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.48.5", + "windows-targets 0.52.4", ] [[package]] @@ -817,7 +835,7 @@ dependencies = [ "once_cell", "strsim 0.10.0", "termcolor", - "textwrap 0.16.0", + "textwrap 0.16.1", ] [[package]] @@ -923,18 +941,18 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-channel" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ "crossbeam-utils", ] @@ -1015,9 +1033,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.3" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" dependencies = [ "darling_core", "darling_macro", @@ -1025,27 +1043,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.3" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] name = "darling_macro" -version = "0.20.3" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -1173,7 +1191,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -1196,7 +1214,7 @@ checksum = "a6cbae11b3de8fce2a456e8ea3dada226b35fe791f0dc1d360c0941f0bb681f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -1260,9 +1278,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "encode_unicode" @@ -1296,7 +1314,7 @@ checksum = "03cdc46ec28bd728e67540c528013c6a10eb69a02eb31078a1bda695438cbfb8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -1309,7 +1327,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -1467,7 +1485,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -1576,7 +1594,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.1.0", + "indexmap 2.2.5", "slab", "tokio", "tokio-util 0.7.10", @@ -1598,7 +1616,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" dependencies = [ - "ahash 0.7.7", + "ahash 0.7.8", ] [[package]] @@ -1607,7 +1625,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.7", + "ahash 0.7.8", ] [[package]] @@ -1642,9 +1660,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.4" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -1690,9 +1708,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -1768,9 +1786,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.59" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1859,9 +1877,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -1870,9 +1888,9 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" dependencies = [ "console", "instant", @@ -1913,18 +1931,18 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "jobserver" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.67" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -1961,9 +1979,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.152" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libsecp256k1" @@ -2043,9 +2061,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "lru" @@ -2135,18 +2153,18 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", @@ -2276,6 +2294,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-derive" version = "0.3.3" @@ -2289,30 +2313,29 @@ dependencies = [ [[package]] name = "num-derive" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" dependencies = [ "autocfg", "num-integer", @@ -2333,9 +2356,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] @@ -2346,7 +2369,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.4", + "hermit-abi 0.3.9", "libc", ] @@ -2377,7 +2400,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -2389,7 +2412,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -2424,9 +2447,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl-probe" @@ -2555,22 +2578,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -2598,9 +2621,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plain" @@ -2693,7 +2716,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "toml_edit 0.21.0", + "toml_edit 0.21.1", ] [[package]] @@ -2746,7 +2769,7 @@ checksum = "9e2e25ee72f5b24d773cae88422baddefff7714f97aab68d96fe2b6fc4a28fb2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -2888,9 +2911,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" +checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" dependencies = [ "either", "rayon-core", @@ -2958,9 +2981,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.23" +version = "0.11.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" dependencies = [ "async-compression", "base64 0.21.7", @@ -2985,6 +3008,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-rustls", @@ -2994,7 +3018,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.25.3", + "webpki-roots 0.25.4", "winreg", ] @@ -3015,16 +3039,17 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", + "cfg-if", "getrandom 0.2.12", "libc", "spin 0.9.8", "untrusted 0.9.0", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3080,9 +3105,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.30" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ "bitflags 2.4.2", "errno", @@ -3098,7 +3123,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", - "ring 0.17.7", + "ring 0.17.8", "rustls-webpki", "sct", ] @@ -3130,7 +3155,7 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.7", + "ring 0.17.8", "untrusted 0.9.0", ] @@ -3142,9 +3167,9 @@ checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "same-file" @@ -3187,7 +3212,7 @@ checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -3196,7 +3221,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.7", + "ring 0.17.8", "untrusted 0.9.0", ] @@ -3225,9 +3250,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "seqlock" @@ -3240,9 +3265,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.195" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] @@ -3258,20 +3283,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] name = "serde_json" -version = "1.0.111" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -3302,18 +3327,19 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.4.0" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" +checksum = "15d167997bd841ec232f5b2b8e0e26606df2e7caa4c31b95ea9ca52b200bd270" dependencies = [ "base64 0.21.7", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.1.0", + "indexmap 2.2.5", "serde", + "serde_derive", "serde_json", - "serde_with_macros 3.4.0", + "serde_with_macros 3.6.1", "time", ] @@ -3326,19 +3352,19 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] name = "serde_with_macros" -version = "3.4.0" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" +checksum = "865f9743393e638991566a8b7a479043c2c8da94a33e0a31f18214c9cae0a64d" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -3461,12 +3487,12 @@ checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3519,7 +3545,7 @@ dependencies = [ "lz4", "memmap2", "modular-bitfield", - "num-derive 0.4.1", + "num-derive 0.4.2", "num-traits", "num_cpus", "num_enum 0.7.2", @@ -3564,7 +3590,7 @@ dependencies = [ "bincode", "bytemuck", "log", - "num-derive 0.4.1", + "num-derive 0.4.2", "num-traits", "rustc_version", "serde", @@ -3689,7 +3715,7 @@ dependencies = [ "dashmap", "futures", "futures-util", - "indexmap 2.1.0", + "indexmap 2.2.5", "indicatif", "log", "quinn", @@ -3720,7 +3746,7 @@ dependencies = [ "num-derive 0.3.3", "num-traits", "serde", - "serde_with 3.4.0", + "serde_with 3.6.1", "solana-program", "solana-program-test", "solana-sdk", @@ -3761,7 +3787,7 @@ dependencies = [ "bincode", "crossbeam-channel", "futures-util", - "indexmap 2.1.0", + "indexmap 2.2.5", "log", "rand 0.8.5", "rayon", @@ -3831,7 +3857,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -3974,7 +4000,7 @@ dependencies = [ "log", "memoffset 0.9.0", "num-bigint 0.4.4", - "num-derive 0.4.1", + "num-derive 0.4.2", "num-traits", "parking_lot", "rand 0.8.5", @@ -4008,7 +4034,7 @@ dependencies = [ "itertools", "libc", "log", - "num-derive 0.4.1", + "num-derive 0.4.2", "num-traits", "percentage", "rand 0.8.5", @@ -4124,7 +4150,7 @@ dependencies = [ "console", "dialoguer", "log", - "num-derive 0.4.1", + "num-derive 0.4.2", "num-traits", "parking_lot", "qstring", @@ -4225,7 +4251,7 @@ dependencies = [ "memmap2", "mockall", "modular-bitfield", - "num-derive 0.4.1", + "num-derive 0.4.2", "num-traits", "num_cpus", "num_enum 0.7.2", @@ -4299,7 +4325,7 @@ dependencies = [ "libsecp256k1", "log", "memmap2", - "num-derive 0.4.1", + "num-derive 0.4.2", "num-traits", "num_enum 0.7.2", "pbkdf2 0.11.0", @@ -4337,7 +4363,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -4388,7 +4414,7 @@ dependencies = [ "crossbeam-channel", "futures-util", "histogram", - "indexmap 2.1.0", + "indexmap 2.2.5", "itertools", "libc", "log", @@ -4447,7 +4473,7 @@ dependencies = [ "async-trait", "bincode", "futures-util", - "indexmap 2.1.0", + "indexmap 2.2.5", "indicatif", "log", "rayon", @@ -4545,7 +4571,7 @@ checksum = "2880e99200ac244ffd63ff7b978f989d9b42a16d2639dce0bf7db1f029c07050" dependencies = [ "bincode", "log", - "num-derive 0.4.1", + "num-derive 0.4.2", "num-traits", "rustc_version", "serde", @@ -4566,7 +4592,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4bba72e165175cccc749755026d23d7e406e29ae1df16b2e43722c749f5beb7" dependencies = [ "bytemuck", - "num-derive 0.4.1", + "num-derive 0.4.2", "num-traits", "solana-program-runtime", "solana-sdk", @@ -4589,7 +4615,7 @@ dependencies = [ "itertools", "lazy_static", "merlin", - "num-derive 0.4.1", + "num-derive 0.4.2", "num-traits", "rand 0.7.3", "serde", @@ -4651,7 +4677,7 @@ checksum = "992d9c64c2564cc8f63a4b508bf3ebcdf2254b0429b13cd1d31adb6162432a5f" dependencies = [ "assert_matches", "borsh 0.10.3", - "num-derive 0.4.1", + "num-derive 0.4.2", "num-traits", "solana-program", "spl-token", @@ -4661,9 +4687,9 @@ dependencies = [ [[package]] name = "spl-discriminator" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce5d563b58ef1bb2cdbbfe0dfb9ffdc24903b10ae6a4df2d8f425ece375033f" +checksum = "daa600f2fe56f32e923261719bae640d873edadbc5237681a39b8e37bfd4d263" dependencies = [ "bytemuck", "solana-program", @@ -4672,25 +4698,25 @@ dependencies = [ [[package]] name = "spl-discriminator-derive" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadbefec4f3c678215ca72bd71862697bb06b41fd77c0088902dd3203354387b" +checksum = "07fd7858fc4ff8fb0e34090e41d7eb06a823e1057945c26d480bfc21d2338a93" dependencies = [ "quote", "spl-discriminator-syn", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] name = "spl-discriminator-syn" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e5f2044ca42c8938d54d1255ce599c79a1ffd86b677dfab695caa20f9ffc3f2" +checksum = "18fea7be851bd98d10721782ea958097c03a0c2a07d8d4997041d0ece6319a63" dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.48", + "syn 2.0.52", "thiserror", ] @@ -4705,9 +4731,9 @@ dependencies = [ [[package]] name = "spl-pod" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2881dddfca792737c0706fa0175345ab282b1b0879c7d877bad129645737c079" +checksum = "85a5db7e4efb1107b0b8e52a13f035437cdcb36ef99c58f6d467f089d9b2915a" dependencies = [ "borsh 0.10.3", "bytemuck", @@ -4718,11 +4744,11 @@ dependencies = [ [[package]] name = "spl-program-error" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "249e0318493b6bcf27ae9902600566c689b7dfba9f1bdff5893e92253374e78c" +checksum = "7e0657b6490196971d9e729520ba934911ff41fbb2cb9004463dbe23cf8b4b4f" dependencies = [ - "num-derive 0.4.1", + "num-derive 0.4.2", "num-traits", "solana-program", "spl-program-error-derive", @@ -4731,14 +4757,14 @@ dependencies = [ [[package]] name = "spl-program-error-derive" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5269c8e868da17b6552ef35a51355a017bd8e0eae269c201fef830d35fa52c" +checksum = "1845dfe71fd68f70382232742e758557afe973ae19e6c06807b2c30f5d5cb474" dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -4778,7 +4804,7 @@ checksum = "d697fac19fd74ff472dfcc13f0b442dd71403178ce1de7b5d16f83a33561c059" dependencies = [ "arrayref", "bytemuck", - "num-derive 0.4.1", + "num-derive 0.4.2", "num-traits", "num_enum 0.7.2", "solana-program", @@ -4839,9 +4865,9 @@ dependencies = [ [[package]] name = "spl-type-length-value" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a468e6f6371f9c69aae760186ea9f1a01c2908351b06a5e0026d21cfc4d7ecac" +checksum = "8f9ebd75d29c5f48de5f6a9c114e08531030b75b8ac2c557600ac7da0b73b1e8" dependencies = [ "bytemuck", "solana-program", @@ -4915,9 +4941,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" dependencies = [ "proc-macro2", "quote", @@ -4933,9 +4959,15 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "synstructure" version = "0.12.6" @@ -5017,13 +5049,12 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.9.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", "rustix", "windows-sys 0.52.0", ] @@ -5061,7 +5092,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -5072,7 +5103,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", "test-case-core", ] @@ -5087,35 +5118,35 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -5123,12 +5154,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.31" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" dependencies = [ "deranged", "itoa", + "num-conv", "powerfmt", "serde", "time-core", @@ -5143,10 +5175,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" dependencies = [ + "num-conv", "time-core", ] @@ -5186,9 +5219,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -5211,7 +5244,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -5263,7 +5296,7 @@ dependencies = [ "tokio", "tokio-rustls", "tungstenite", - "webpki-roots 0.25.3", + "webpki-roots 0.25.4", ] [[package]] @@ -5316,18 +5349,18 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.5", "toml_datetime", "winnow", ] [[package]] name = "toml_edit" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.5", "toml_datetime", "winnow", ] @@ -5358,7 +5391,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -5442,9 +5475,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] @@ -5545,9 +5578,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -5576,9 +5609,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.90" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -5586,24 +5619,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.90" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.40" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -5613,9 +5646,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.90" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5623,28 +5656,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.90" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.90" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" -version = "0.3.67" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -5661,9 +5694,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.3" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "winapi" @@ -5702,7 +5735,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.4", ] [[package]] @@ -5720,7 +5753,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.4", ] [[package]] @@ -5740,17 +5773,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", ] [[package]] @@ -5761,9 +5794,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" @@ -5773,9 +5806,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" @@ -5785,9 +5818,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" @@ -5797,9 +5830,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" @@ -5809,9 +5842,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" @@ -5821,9 +5854,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" @@ -5833,15 +5866,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "winnow" -version = "0.5.34" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] @@ -5911,7 +5944,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -5931,7 +5964,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ffc89ba..3221c22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] resolver = "2" -members = ["clients/rust"] +members = ["clients/rust", "program"] diff --git a/program/Cargo.toml b/program/Cargo.toml new file mode 100644 index 0000000..7fe6bbc --- /dev/null +++ b/program/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "address-lookup-table" +version = "0.1.0" +edition = "2021" +readme = "./README.md" +license-file = "../LICENSE" +publish = false + +[package.metadata.solana] +program-id = "AddressLookupTab1e1111111111111111111111111" + +[features] +no-entrypoint = [] +test-sbf = [] + +[dependencies] +bincode = "1.3.3" +bytemuck = "1.14.1" +log = "0.4.20" +serde = { version = "1.0.193", features = ["derive"] } +solana-frozen-abi = "1.18.2" +solana-frozen-abi-macro = "1.18.2" +solana-program = "1.18.2" +spl-program-error = "0.3.1" + +[dev-dependencies] +assert_matches = "1.5.0" +solana-program-test = "1.18.2" +solana-sdk = "1.18.2" + +[lib] +crate-type = ["cdylib", "lib"] + +[build-dependencies] +rustc_version = "0.4" \ No newline at end of file diff --git a/program/build.rs b/program/build.rs new file mode 100644 index 0000000..0e1edcf --- /dev/null +++ b/program/build.rs @@ -0,0 +1,28 @@ +//! Required for `solana-frozen-abi-macro` to work. +extern crate rustc_version; +use rustc_version::{version_meta, Channel}; + +fn main() { + // Copied and adapted from + // https://github.com/Kimundi/rustc-version-rs/blob/1d692a965f4e48a8cb72e82cda953107c0d22f47/README.md#example + // Licensed under Apache-2.0 + MIT + match version_meta().unwrap().channel { + Channel::Stable => { + println!("cargo:rustc-cfg=RUSTC_WITHOUT_SPECIALIZATION"); + } + Channel::Beta => { + println!("cargo:rustc-cfg=RUSTC_WITHOUT_SPECIALIZATION"); + } + Channel::Nightly => { + println!("cargo:rustc-cfg=RUSTC_WITH_SPECIALIZATION"); + } + Channel::Dev => { + println!("cargo:rustc-cfg=RUSTC_WITH_SPECIALIZATION"); + // See https://github.com/solana-labs/solana/issues/11055 + // We may be running the custom `rust-bpf-builder` toolchain, + // which currently needs `#![feature(proc_macro_hygiene)]` to + // be applied. + println!("cargo:rustc-cfg=RUSTC_NEEDS_PROC_MACRO_HYGIENE"); + } + } +} diff --git a/program/src/entrypoint.rs b/program/src/entrypoint.rs new file mode 100644 index 0000000..d1a2e46 --- /dev/null +++ b/program/src/entrypoint.rs @@ -0,0 +1,22 @@ +//! Program entrypoint + +use { + crate::{error::AddressLookupError, processor}, + solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, program_error::PrintProgramError, + pubkey::Pubkey, + }, +}; + +solana_program::entrypoint!(process_instruction); +fn process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_data: &[u8], +) -> ProgramResult { + if let Err(error) = processor::process(program_id, accounts, instruction_data) { + error.print::(); + return Err(error); + } + Ok(()) +} diff --git a/program/src/error.rs b/program/src/error.rs new file mode 100644 index 0000000..74df4f4 --- /dev/null +++ b/program/src/error.rs @@ -0,0 +1,33 @@ +//! Program error types + +#[cfg(not(target_os = "solana"))] +use solana_program::message::AddressLoaderError; +use spl_program_error::*; + +#[spl_program_error] +pub enum AddressLookupError { + /// Attempted to lookup addresses from a table that does not exist + #[error("Attempted to lookup addresses from a table that does not exist")] + LookupTableAccountNotFound, + /// Attempted to lookup addresses from an account owned by the wrong program + #[error("Attempted to lookup addresses from an account owned by the wrong program")] + InvalidAccountOwner, + /// Attempted to lookup addresses from an invalid account + #[error("Attempted to lookup addresses from an invalid account")] + InvalidAccountData, + /// Address lookup contains an invalid index + #[error("Address lookup contains an invalid index")] + InvalidLookupIndex, +} + +#[cfg(not(target_os = "solana"))] +impl From for AddressLoaderError { + fn from(err: AddressLookupError) -> Self { + match err { + AddressLookupError::LookupTableAccountNotFound => Self::LookupTableAccountNotFound, + AddressLookupError::InvalidAccountOwner => Self::InvalidAccountOwner, + AddressLookupError::InvalidAccountData => Self::InvalidAccountData, + AddressLookupError::InvalidLookupIndex => Self::InvalidLookupIndex, + } + } +} diff --git a/program/src/instruction.rs b/program/src/instruction.rs new file mode 100644 index 0000000..381eb4d --- /dev/null +++ b/program/src/instruction.rs @@ -0,0 +1,193 @@ +//! Program instruction types + +use { + serde::{Deserialize, Serialize}, + solana_program::{ + clock::Slot, + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, + system_program, + }, +}; + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +pub enum ProgramInstruction { + /// Create an address lookup table + /// + /// # Account references + /// 0. `[WRITE]` Uninitialized address lookup table account + /// 1. `[SIGNER]` Account used to derive and control the new address + /// lookup table. + /// 2. `[SIGNER, WRITE]` Account that will fund the new address lookup + /// table. + /// 3. `[]` System program for CPI. + CreateLookupTable { + /// A recent slot must be used in the derivation path + /// for each initialized table. When closing table accounts, + /// the initialization slot must no longer be "recent" to prevent + /// address tables from being recreated with reordered or + /// otherwise malicious addresses. + recent_slot: Slot, + /// Address tables are always initialized at program-derived + /// addresses using the funding address, recent blockhash, and + /// the user-passed `bump_seed`. + bump_seed: u8, + }, + + /// Permanently freeze an address lookup table, making it immutable. + /// + /// # Account references + /// 0. `[WRITE]` Address lookup table account to freeze + /// 1. `[SIGNER]` Current authority + FreezeLookupTable, + + /// Extend an address lookup table with new addresses. Funding account and + /// system program account references are only required if the lookup table + /// account requires additional lamports to cover the rent-exempt balance + /// after being extended. + /// + /// # Account references + /// 0. `[WRITE]` Address lookup table account to extend + /// 1. `[SIGNER]` Current authority + /// 2. `[SIGNER, WRITE, OPTIONAL]` Account that will fund the table + /// reallocation + /// 3. `[OPTIONAL]` System program for CPI. + ExtendLookupTable { new_addresses: Vec }, + + /// Deactivate an address lookup table, making it unusable and + /// eligible for closure after a short period of time. + /// + /// # Account references + /// 0. `[WRITE]` Address lookup table account to deactivate + /// 1. `[SIGNER]` Current authority + DeactivateLookupTable, + + /// Close an address lookup table account + /// + /// # Account references + /// 0. `[WRITE]` Address lookup table account to close + /// 1. `[SIGNER]` Current authority + /// 2. `[WRITE]` Recipient of closed account lamports + CloseLookupTable, +} + +/// Derives the address of an address table account from a wallet address and a +/// recent block's slot. +pub fn derive_lookup_table_address( + authority_address: &Pubkey, + recent_block_slot: Slot, +) -> (Pubkey, u8) { + Pubkey::find_program_address( + &[authority_address.as_ref(), &recent_block_slot.to_le_bytes()], + &crate::id(), + ) +} + +// [Core BPF]: `create_lookup_table_signed` has been removed, since feature +// "FKAcEvNgSY79RpqsPNUV5gDyumopH4cEHqUxyfm8b8Ap" +// (relax_authority_signer_check_for_lookup_table_creation) has been activated +// on all clusters. + +/// Constructs an instruction to create a table account and returns +/// the instruction and the table account's derived address. +pub fn create_lookup_table( + authority_address: Pubkey, + payer_address: Pubkey, + recent_slot: Slot, +) -> (Instruction, Pubkey) { + let (lookup_table_address, bump_seed) = + derive_lookup_table_address(&authority_address, recent_slot); + + let instruction = Instruction::new_with_bincode( + crate::id(), + &ProgramInstruction::CreateLookupTable { + recent_slot, + bump_seed, + }, + vec![ + AccountMeta::new(lookup_table_address, false), + AccountMeta::new_readonly(authority_address, false), + AccountMeta::new(payer_address, true), + AccountMeta::new_readonly(system_program::id(), false), + ], + ); + + (instruction, lookup_table_address) +} + +/// Constructs an instruction that freezes an address lookup +/// table so that it can never be closed or extended again. Empty +/// lookup tables cannot be frozen. +pub fn freeze_lookup_table(lookup_table_address: Pubkey, authority_address: Pubkey) -> Instruction { + Instruction::new_with_bincode( + crate::id(), + &ProgramInstruction::FreezeLookupTable, + vec![ + AccountMeta::new(lookup_table_address, false), + AccountMeta::new_readonly(authority_address, true), + ], + ) +} + +/// Constructs an instruction which extends an address lookup +/// table account with new addresses. +pub fn extend_lookup_table( + lookup_table_address: Pubkey, + authority_address: Pubkey, + payer_address: Option, + new_addresses: Vec, +) -> Instruction { + let mut accounts = vec![ + AccountMeta::new(lookup_table_address, false), + AccountMeta::new_readonly(authority_address, true), + ]; + + if let Some(payer_address) = payer_address { + accounts.extend([ + AccountMeta::new(payer_address, true), + AccountMeta::new_readonly(system_program::id(), false), + ]); + } + + Instruction::new_with_bincode( + crate::id(), + &ProgramInstruction::ExtendLookupTable { new_addresses }, + accounts, + ) +} + +/// Constructs an instruction that deactivates an address lookup +/// table so that it cannot be extended again and will be unusable +/// and eligible for closure after a short amount of time. +pub fn deactivate_lookup_table( + lookup_table_address: Pubkey, + authority_address: Pubkey, +) -> Instruction { + Instruction::new_with_bincode( + crate::id(), + &ProgramInstruction::DeactivateLookupTable, + vec![ + AccountMeta::new(lookup_table_address, false), + AccountMeta::new_readonly(authority_address, true), + ], + ) +} + +/// Returns an instruction that closes an address lookup table +/// account. The account will be deallocated and the lamports +/// will be drained to the recipient address. +pub fn close_lookup_table( + lookup_table_address: Pubkey, + authority_address: Pubkey, + recipient_address: Pubkey, +) -> Instruction { + Instruction::new_with_bincode( + crate::id(), + &ProgramInstruction::CloseLookupTable, + vec![ + AccountMeta::new(lookup_table_address, false), + AccountMeta::new_readonly(authority_address, true), + AccountMeta::new(recipient_address, false), + ], + ) +} diff --git a/program/src/lib.rs b/program/src/lib.rs new file mode 100644 index 0000000..5821a14 --- /dev/null +++ b/program/src/lib.rs @@ -0,0 +1,25 @@ +//! Address Lookup Table Program +// [Core BPF]: Required for `solana-frozen-abi-macro` to work. +#![allow(incomplete_features)] +#![cfg_attr(RUSTC_WITH_SPECIALIZATION, feature(specialization))] + +#[cfg(not(feature = "no-entrypoint"))] +mod entrypoint; +pub mod error; +pub mod instruction; +pub mod processor; +pub mod state; + +// [Core BPF]: TODO: Program-test will not overwrite existing built-ins. +// See https://github.com/solana-labs/solana/pull/35233 +// solana_program::declare_id!("AddressLookupTab1e1111111111111111111111111"); +solana_program::declare_id!("AaoNx79M6YE3DcXfrRN4nmBcQvQPqdpowi6uEESuJdnm"); + +/// The definition of address lookup table accounts. +/// +/// As used by the `crate::message::v0` message format. +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct AddressLookupTableAccount { + pub key: solana_program::pubkey::Pubkey, + pub addresses: Vec, +} diff --git a/program/src/processor.rs b/program/src/processor.rs new file mode 100644 index 0000000..69251cb --- /dev/null +++ b/program/src/processor.rs @@ -0,0 +1,485 @@ +//! Program processor + +use { + crate::{ + check_id, + instruction::ProgramInstruction, + state::{ + AddressLookupTable, LookupTableStatus, ProgramState, LOOKUP_TABLE_MAX_ADDRESSES, + LOOKUP_TABLE_META_SIZE, + }, + }, + solana_program::{ + account_info::{next_account_info, AccountInfo}, + clock::{Clock, Slot}, + entrypoint::ProgramResult, + msg, + program::{invoke, invoke_signed}, + program_error::ProgramError, + pubkey::{Pubkey, PUBKEY_BYTES}, + rent::Rent, + slot_hashes::MAX_ENTRIES, + system_instruction, + sysvar::Sysvar, + }, +}; + +// [Core BPF]: Locally-implemented +// `solana_sdk::program_utils::limited_deserialize`. +fn limited_deserialize(input: &[u8]) -> Result +where + T: serde::de::DeserializeOwned, +{ + solana_program::program_utils::limited_deserialize( + input, 1232, // [Core BPF]: See `solana_sdk::packet::PACKET_DATA_SIZE` + ) + .map_err(|_| ProgramError::InvalidInstructionData) +} + +// [Core BPF]: Feature "FKAcEvNgSY79RpqsPNUV5gDyumopH4cEHqUxyfm8b8Ap" +// (relax_authority_signer_check_for_lookup_table_creation) is now enabled on +// all clusters, so the relevant checks have not been included in the Core BPF +// implementation. +// - Testnet: Epoch 586 +// - Devnet: Epoch 591 +// - Mainnet-Beta: epoch 577 +fn process_create_lookup_table( + program_id: &Pubkey, + accounts: &[AccountInfo], + untrusted_recent_slot: Slot, + bump_seed: u8, +) -> ProgramResult { + let accounts_iter = &mut accounts.iter(); + + let lookup_table_info = next_account_info(accounts_iter)?; + let authority_info = next_account_info(accounts_iter)?; + let payer_info = next_account_info(accounts_iter)?; + let _system_program_info = next_account_info(accounts_iter)?; + + if !payer_info.is_signer { + msg!("Payer account must be a signer"); + return Err(ProgramError::MissingRequiredSignature); + } + + // [Core BPF]: Since the `SlotHashes` sysvar is not available to BPF + // programs, checking if a slot is a valid recent slot must be done + // differently. + // The `SlotHashes` sysvar stores up to `512` recent slots (`MAX_ENTRIES`). + // We can instead use the `Clock` sysvar and do this math manually. + // + // Note this will no longer consider skipped slots wherein a block was not + // produced. + let derivation_slot = { + let clock = ::get()?; + let oldest_possible_slot = clock.slot.saturating_sub(MAX_ENTRIES as u64); + if untrusted_recent_slot > oldest_possible_slot && untrusted_recent_slot <= clock.slot { + Ok(untrusted_recent_slot) + } else { + msg!("{} is not a recent slot", untrusted_recent_slot); + Err(ProgramError::InvalidInstructionData) + } + }?; + + // Use a derived address to ensure that an address table can never be + // initialized more than once at the same address. + let derived_table_key = Pubkey::create_program_address( + &[ + authority_info.key.as_ref(), + &derivation_slot.to_le_bytes(), + &[bump_seed], + ], + program_id, + )?; + + if lookup_table_info.key != &derived_table_key { + msg!( + "Table address must match derived address: {}", + derived_table_key + ); + return Err(ProgramError::InvalidArgument); + } + + // [Core BPF]: This check _is required_ since + // "FKAcEvNgSY79RpqsPNUV5gDyumopH4cEHqUxyfm8b8Ap" was activated on + // mainnet-beta. + // See https://github.com/solana-labs/solana/blob/e4064023bf7936ced97b0d4de22137742324983d/programs/address-lookup-table/src/processor.rs#L129-L135. + if check_id(lookup_table_info.owner) { + return Ok(()); + } + + let lookup_table_data_len = LOOKUP_TABLE_META_SIZE; + let rent = ::get()?; + let required_lamports = rent + .minimum_balance(lookup_table_data_len) + .max(1) + .saturating_sub(lookup_table_info.lamports()); + + if required_lamports > 0 { + invoke( + &system_instruction::transfer(payer_info.key, lookup_table_info.key, required_lamports), + &[payer_info.clone(), lookup_table_info.clone()], + )?; + } + + invoke_signed( + &system_instruction::allocate(lookup_table_info.key, lookup_table_data_len as u64), + &[lookup_table_info.clone()], + &[&[ + authority_info.key.as_ref(), + &derivation_slot.to_le_bytes(), + &[bump_seed], + ]], + )?; + + invoke_signed( + &system_instruction::assign(lookup_table_info.key, program_id), + &[lookup_table_info.clone()], + &[&[ + authority_info.key.as_ref(), + &derivation_slot.to_le_bytes(), + &[bump_seed], + ]], + )?; + + ProgramState::serialize_new_lookup_table( + *lookup_table_info.try_borrow_mut_data()?, + authority_info.key, + )?; + + Ok(()) +} + +fn process_freeze_lookup_table(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { + let accounts_iter = &mut accounts.iter(); + + let lookup_table_info = next_account_info(accounts_iter)?; + let authority_info = next_account_info(accounts_iter)?; + + if lookup_table_info.owner != program_id { + return Err(ProgramError::InvalidAccountOwner); + } + + if !authority_info.is_signer { + msg!("Authority account must be a signer"); + return Err(ProgramError::MissingRequiredSignature); + } + + let mut lookup_table_meta = { + let lookup_table_data = lookup_table_info.try_borrow_data()?; + let lookup_table = AddressLookupTable::deserialize(&lookup_table_data)?; + + if lookup_table.meta.authority.is_none() { + msg!("Lookup table is already frozen"); + // [Core BPF]: TODO: Should be `ProgramError::Immutable` + // See https://github.com/solana-labs/solana/pull/35113 + return Err(ProgramError::Custom(0)); + } + if lookup_table.meta.authority != Some(*authority_info.key) { + // [Core BPF]: TODO: Should be `ProgramError::IncorrectAuthority` + // See https://github.com/solana-labs/solana/pull/35113 + return Err(ProgramError::Custom(0)); + } + if lookup_table.meta.deactivation_slot != Slot::MAX { + msg!("Deactivated tables cannot be frozen"); + return Err(ProgramError::InvalidArgument); + } + if lookup_table.addresses.is_empty() { + msg!("Empty lookup tables cannot be frozen"); + return Err(ProgramError::InvalidInstructionData); + } + + lookup_table.meta + }; + + lookup_table_meta.authority = None; + AddressLookupTable::overwrite_meta_data( + *lookup_table_info.try_borrow_mut_data()?, + lookup_table_meta, + )?; + + Ok(()) +} + +fn process_extend_lookup_table( + program_id: &Pubkey, + accounts: &[AccountInfo], + new_addresses: Vec, +) -> ProgramResult { + let accounts_iter = &mut accounts.iter(); + + let lookup_table_info = next_account_info(accounts_iter)?; + let authority_info = next_account_info(accounts_iter)?; + + if lookup_table_info.owner != program_id { + return Err(ProgramError::InvalidAccountOwner); + } + + if !authority_info.is_signer { + msg!("Authority account must be a signer"); + return Err(ProgramError::MissingRequiredSignature); + } + + let (lookup_table_meta, old_table_data_len, new_table_data_len) = { + let lookup_table_data = lookup_table_info.try_borrow_data()?; + let mut lookup_table = AddressLookupTable::deserialize(&lookup_table_data)?; + + if lookup_table.meta.authority.is_none() { + msg!("Lookup table is frozen"); + // [Core BPF]: TODO: Should be `ProgramError::Immutable` + // See https://github.com/solana-labs/solana/pull/35113 + return Err(ProgramError::Custom(0)); + } + if lookup_table.meta.authority != Some(*authority_info.key) { + // [Core BPF]: TODO: Should be `ProgramError::IncorrectAuthority` + // See https://github.com/solana-labs/solana/pull/35113 + return Err(ProgramError::Custom(0)); + } + if lookup_table.meta.deactivation_slot != Slot::MAX { + msg!("Deactivated tables cannot be extended"); + return Err(ProgramError::InvalidArgument); + } + if lookup_table.addresses.len() >= LOOKUP_TABLE_MAX_ADDRESSES { + msg!("Lookup table is full and cannot contain more addresses"); + return Err(ProgramError::InvalidArgument); + } + + if new_addresses.is_empty() { + msg!("Must extend with at least one address"); + return Err(ProgramError::InvalidInstructionData); + } + + let old_table_addresses_len = lookup_table.addresses.len(); + let new_table_addresses_len = old_table_addresses_len.saturating_add(new_addresses.len()); + + if new_table_addresses_len > LOOKUP_TABLE_MAX_ADDRESSES { + msg!( + "Extended lookup table length {} would exceed max capacity of {}", + new_table_addresses_len, + LOOKUP_TABLE_MAX_ADDRESSES, + ); + return Err(ProgramError::InvalidInstructionData); + } + + let clock = ::get()?; + if clock.slot != lookup_table.meta.last_extended_slot { + lookup_table.meta.last_extended_slot = clock.slot; + lookup_table.meta.last_extended_slot_start_index = + u8::try_from(old_table_addresses_len).map_err(|_| { + // This is impossible as long as the length of new_addresses + // is non-zero and LOOKUP_TABLE_MAX_ADDRESSES == u8::MAX + 1. + ProgramError::InvalidAccountData + })?; + } + + let old_table_data_len = LOOKUP_TABLE_META_SIZE + .checked_add(old_table_addresses_len.saturating_mul(PUBKEY_BYTES)) + .ok_or(ProgramError::ArithmeticOverflow)?; + let new_table_data_len = LOOKUP_TABLE_META_SIZE + .checked_add(new_table_addresses_len.saturating_mul(PUBKEY_BYTES)) + .ok_or(ProgramError::ArithmeticOverflow)?; + + (lookup_table.meta, old_table_data_len, new_table_data_len) + }; + + AddressLookupTable::overwrite_meta_data( + *lookup_table_info.try_borrow_mut_data()?, + lookup_table_meta, + )?; + + lookup_table_info.realloc(new_table_data_len, false)?; + + { + let mut lookup_table_data = lookup_table_info.try_borrow_mut_data()?; + let uninitialized_addresses = AddressLookupTable::deserialize_addresses_from_index_mut( + &mut lookup_table_data, + old_table_data_len, + )?; + uninitialized_addresses.copy_from_slice(&new_addresses); + } + + let rent = ::get()?; + let required_lamports = rent + .minimum_balance(new_table_data_len) + .max(1) + .saturating_sub(lookup_table_info.lamports()); + + if required_lamports > 0 { + let payer_info = next_account_info(accounts_iter)?; + let _system_program_info = next_account_info(accounts_iter)?; + + if !payer_info.is_signer { + msg!("Payer account must be a signer"); + return Err(ProgramError::MissingRequiredSignature); + } + + invoke( + &system_instruction::transfer(payer_info.key, lookup_table_info.key, required_lamports), + &[payer_info.clone(), lookup_table_info.clone()], + )?; + } + + Ok(()) +} + +fn process_deactivate_lookup_table(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { + let accounts_iter = &mut accounts.iter(); + + let lookup_table_info = next_account_info(accounts_iter)?; + let authority_info = next_account_info(accounts_iter)?; + + if lookup_table_info.owner != program_id { + return Err(ProgramError::InvalidAccountOwner); + } + + if !authority_info.is_signer { + msg!("Authority account must be a signer"); + return Err(ProgramError::MissingRequiredSignature); + } + + let mut lookup_table_meta = { + let lookup_table_data = lookup_table_info.try_borrow_data()?; + let lookup_table = AddressLookupTable::deserialize(&lookup_table_data)?; + + if lookup_table.meta.authority.is_none() { + msg!("Lookup table is frozen"); + // [Core BPF]: TODO: Should be `ProgramError::Immutable` + // See https://github.com/solana-labs/solana/pull/35113 + return Err(ProgramError::Custom(0)); + } + if lookup_table.meta.authority != Some(*authority_info.key) { + // [Core BPF]: TODO: Should be `ProgramError::IncorrectAuthority` + // See https://github.com/solana-labs/solana/pull/35113 + return Err(ProgramError::Custom(0)); + } + if lookup_table.meta.deactivation_slot != Slot::MAX { + msg!("Lookup table is already deactivated"); + return Err(ProgramError::InvalidArgument); + } + + lookup_table.meta + }; + + let clock = ::get()?; + lookup_table_meta.deactivation_slot = clock.slot; + + AddressLookupTable::overwrite_meta_data( + *lookup_table_info.try_borrow_mut_data()?, + lookup_table_meta, + )?; + + Ok(()) +} + +fn process_close_lookup_table(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { + let accounts_iter = &mut accounts.iter(); + + let lookup_table_info = next_account_info(accounts_iter)?; + let authority_info = next_account_info(accounts_iter)?; + let recipient_info = next_account_info(accounts_iter)?; + + if lookup_table_info.owner != program_id { + return Err(ProgramError::InvalidAccountOwner); + } + + if !authority_info.is_signer { + msg!("Authority account must be a signer"); + return Err(ProgramError::MissingRequiredSignature); + } + + // [Core BPF]: Here the legacy built-in version of ALT fallibly checks to + // ensure the number of instruction accounts is 3. + // It also checks that the recipient account is not the same as the lookup + // table account. + // The built-in does this by specifically checking the account keys at + // their respective indices in the instruction context. + // In BPF, we can just compare the addresses directly. + if lookup_table_info.key == recipient_info.key { + msg!("Lookup table cannot be the recipient of reclaimed lamports"); + return Err(ProgramError::InvalidArgument); + } + + { + let lookup_table_data = lookup_table_info.try_borrow_data()?; + let lookup_table = AddressLookupTable::deserialize(&lookup_table_data)?; + + if lookup_table.meta.authority.is_none() { + msg!("Lookup table is frozen"); + // [Core BPF]: TODO: Should be `ProgramError::Immutable` + // See https://github.com/solana-labs/solana/pull/35113 + return Err(ProgramError::Custom(0)); + } + if lookup_table.meta.authority != Some(*authority_info.key) { + // [Core BPF]: TODO: Should be `ProgramError::IncorrectAuthority` + // See https://github.com/solana-labs/solana/pull/35113 + return Err(ProgramError::Custom(0)); + } + + let clock = ::get()?; + + // [Core BPF]: Again, since the `SlotHashes` sysvar is not available to + // BPF programs, we can't use the `SlotHashes` sysvar to check the + // status of a lookup table. + // Again we instead use the `Clock` sysvar here. + // This will no longer consider skipped slots wherein a block was not + // produced. + // See `state::LookupTableMeta::status` for more details. + match lookup_table.meta.status(clock.slot) { + LookupTableStatus::Activated => { + msg!("Lookup table is not deactivated"); + Err(ProgramError::InvalidArgument) + } + LookupTableStatus::Deactivating { remaining_blocks } => { + msg!( + "Table cannot be closed until it's fully deactivated in {} blocks", + remaining_blocks + ); + Err(ProgramError::InvalidArgument) + } + LookupTableStatus::Deactivated => Ok(()), + }?; + } + + let new_recipient_lamports = lookup_table_info + .lamports() + .checked_add(recipient_info.lamports()) + .ok_or::(ProgramError::ArithmeticOverflow)?; + + **lookup_table_info.try_borrow_mut_lamports()? = 0; + **recipient_info.try_borrow_mut_lamports()? = new_recipient_lamports; + + // Lookup tables are _not_ reassigned when closed. + lookup_table_info.realloc(0, true)?; + + Ok(()) +} + +/// Processes a +/// `solana_programs_address_lookup_table::instruction::ProgramInstruction` +pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult { + let instruction = limited_deserialize(input)?; + match instruction { + ProgramInstruction::CreateLookupTable { + recent_slot, + bump_seed, + } => { + msg!("Instruction: CreateLookupTable"); + process_create_lookup_table(program_id, accounts, recent_slot, bump_seed) + } + ProgramInstruction::FreezeLookupTable => { + msg!("Instruction: FreezeLookupTable"); + process_freeze_lookup_table(program_id, accounts) + } + ProgramInstruction::ExtendLookupTable { new_addresses } => { + msg!("Instruction: ExtendLookupTable"); + process_extend_lookup_table(program_id, accounts, new_addresses) + } + ProgramInstruction::DeactivateLookupTable => { + msg!("Instruction: DeactivateLookupTable"); + process_deactivate_lookup_table(program_id, accounts) + } + ProgramInstruction::CloseLookupTable => { + msg!("Instruction: CloseLookupTable"); + process_close_lookup_table(program_id, accounts) + } + } +} diff --git a/program/src/state.rs b/program/src/state.rs new file mode 100644 index 0000000..0aab3fe --- /dev/null +++ b/program/src/state.rs @@ -0,0 +1,531 @@ +use { + crate::error::AddressLookupError, + serde::{Deserialize, Serialize}, + solana_frozen_abi_macro::{AbiEnumVisitor, AbiExample}, + solana_program::{ + clock::Slot, program_error::ProgramError, pubkey::Pubkey, slot_hashes::MAX_ENTRIES, + }, + std::borrow::Cow, +}; + +/// The maximum number of addresses that a lookup table can hold +pub const LOOKUP_TABLE_MAX_ADDRESSES: usize = 256; + +/// The serialized size of lookup table metadata +pub const LOOKUP_TABLE_META_SIZE: usize = 56; + +// [Core BPF]: Newly-implemented logic for calculating slot position relative +// to the current slot on the `Clock`. +fn calculate_slot_position(target_slot: &Slot, current_slot: &Slot) -> Option { + let position = current_slot.saturating_sub(*target_slot); + + if position >= (MAX_ENTRIES as u64) { + return None; + } + Some(position as usize) +} + +/// Activation status of a lookup table +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum LookupTableStatus { + Activated, + Deactivating { remaining_blocks: usize }, + Deactivated, +} + +/// Address lookup table metadata +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, AbiExample)] +pub struct LookupTableMeta { + // [Core BPF]: TODO: `Clock` instead of `SlotHashes`. + /// Lookup tables cannot be closed until the deactivation slot is + /// no longer "recent" (not accessible in the `SlotHashes` sysvar). + pub deactivation_slot: Slot, + /// The slot that the table was last extended. Address tables may + /// only be used to lookup addresses that were extended before + /// the current bank's slot. + pub last_extended_slot: Slot, + /// The start index where the table was last extended from during + /// the `last_extended_slot`. + pub last_extended_slot_start_index: u8, + /// Authority address which must sign for each modification. + pub authority: Option, + // Padding to keep addresses 8-byte aligned + pub _padding: u16, + // Raw list of addresses follows this serialized structure in + // the account's data, starting from `LOOKUP_TABLE_META_SIZE`. +} + +impl Default for LookupTableMeta { + fn default() -> Self { + Self { + deactivation_slot: Slot::MAX, + last_extended_slot: 0, + last_extended_slot_start_index: 0, + authority: None, + _padding: 0, + } + } +} + +impl LookupTableMeta { + pub fn new(authority: Pubkey) -> Self { + LookupTableMeta { + authority: Some(authority), + ..LookupTableMeta::default() + } + } + + /// Returns whether the table is considered active for address lookups + pub fn is_active(&self, current_slot: Slot) -> bool { + match self.status(current_slot) { + LookupTableStatus::Activated => true, + LookupTableStatus::Deactivating { .. } => true, + LookupTableStatus::Deactivated => false, + } + } + + // [Core BPF]: This function has been modified from its legacy built-in + // counterpart to no longer use the `SlotHashes` sysvar, since it is not + // available for BPF programs. Instead, it uses the `current_slot` + // parameter to calculate the table's status. + // This will no longer consider the case where a slot has been skipped + // and no block was produced. + // If it's imperative to ensure we are only considering slots where blocks + // were created, then we'll need to revisit this function, and possibly + // provide the `SlotHashes` account so we can reliably check slot hashes. + /// Return the current status of the lookup table + pub fn status(&self, current_slot: Slot) -> LookupTableStatus { + if self.deactivation_slot == Slot::MAX { + LookupTableStatus::Activated + } else if self.deactivation_slot == current_slot { + LookupTableStatus::Deactivating { + remaining_blocks: MAX_ENTRIES.saturating_add(1), + } + } else if let Some(slot_position) = + calculate_slot_position(&self.deactivation_slot, ¤t_slot) + { + // [Core BPF]: TODO: `Clock` instead of `SlotHashes`. + // Deactivation requires a cool-down period to give in-flight transactions + // enough time to land and to remove indeterminism caused by transactions + // loading addresses in the same slot when a table is closed. The + // cool-down period is equivalent to the amount of time it takes for + // a slot to be removed from the slot hash list. + // + // By using the slot hash to enforce the cool-down, there is a side effect + // of not allowing lookup tables to be recreated at the same derived address + // because tables must be created at an address derived from a recent slot. + LookupTableStatus::Deactivating { + remaining_blocks: MAX_ENTRIES.saturating_sub(slot_position), + } + } else { + LookupTableStatus::Deactivated + } + } +} + +/// Program account states +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, AbiExample, AbiEnumVisitor)] +#[allow(clippy::large_enum_variant)] +pub enum ProgramState { + /// Account is not initialized. + Uninitialized, + /// Initialized `LookupTable` account. + LookupTable(LookupTableMeta), +} + +impl ProgramState { + /// Serialize a new lookup table into uninitialized account data. + pub fn serialize_new_lookup_table( + data: &mut [u8], + authority_key: &Pubkey, + ) -> Result<(), ProgramError> { + let lookup_table = ProgramState::LookupTable(LookupTableMeta::new(*authority_key)); + // [Core BPF]: The original builtin implementation mapped `bincode` + // serialization errors to `InstructionError::GenericError`, but this + // error is deprecated. The error code for failed serialization has + // changed. + bincode::serialize_into(data, &lookup_table).map_err(|_| ProgramError::InvalidAccountData) + } +} + +#[derive(Debug, PartialEq, Eq, Clone, AbiExample)] +pub struct AddressLookupTable<'a> { + pub meta: LookupTableMeta, + pub addresses: Cow<'a, [Pubkey]>, +} + +impl<'a> AddressLookupTable<'a> { + /// Serialize an address table's updated meta data and zero + /// any leftover bytes. + pub fn overwrite_meta_data( + data: &mut [u8], + lookup_table_meta: LookupTableMeta, + ) -> Result<(), ProgramError> { + let meta_data = data + .get_mut(0..LOOKUP_TABLE_META_SIZE) + .ok_or(ProgramError::InvalidAccountData)?; + meta_data.fill(0); + bincode::serialize_into(meta_data, &ProgramState::LookupTable(lookup_table_meta)) + // [Core BPF]: The original builtin implementation mapped `bincode` + // serialization errors to `InstructionError::GenericError`, but this + // error is deprecated. The error code for failed serialization has + // changed. + .map_err(|_| ProgramError::InvalidAccountData) + } + + /// Get the length of addresses that are active for lookups + pub fn get_active_addresses_len( + &self, + current_slot: Slot, + ) -> Result { + if !self.meta.is_active(current_slot) { + // Once a lookup table is no longer active, it can be closed + // at any point, so returning a specific error for deactivated + // lookup tables could result in a race condition. + return Err(AddressLookupError::LookupTableAccountNotFound); + } + + // If the address table was extended in the same slot in which it is used + // to lookup addresses for another transaction, the recently extended + // addresses are not considered active and won't be accessible. + let active_addresses_len = if current_slot > self.meta.last_extended_slot { + self.addresses.len() + } else { + self.meta.last_extended_slot_start_index as usize + }; + + Ok(active_addresses_len) + } + + /// Lookup addresses for provided table indexes. Since lookups are performed + /// on tables which are not read-locked, this implementation needs to be + /// careful about resolving addresses consistently. + pub fn lookup( + &self, + current_slot: Slot, + indexes: &[u8], + ) -> Result, AddressLookupError> { + let active_addresses_len = self.get_active_addresses_len(current_slot)?; + let active_addresses = &self.addresses[0..active_addresses_len]; + indexes + .iter() + .map(|idx| active_addresses.get(*idx as usize).cloned()) + .collect::>() + .ok_or(AddressLookupError::InvalidLookupIndex) + } + + /// Serialize an address table including its addresses + pub fn serialize_for_tests(self) -> Result, ProgramError> { + let mut data = vec![0; LOOKUP_TABLE_META_SIZE]; + Self::overwrite_meta_data(&mut data, self.meta)?; + self.addresses.iter().for_each(|address| { + data.extend_from_slice(address.as_ref()); + }); + Ok(data) + } + + /// Mutably deserialize addresses from a lookup table's data. + pub fn deserialize_addresses_from_index_mut( + data: &mut [u8], + start_index: usize, + ) -> Result<&mut [Pubkey], ProgramError> { + if start_index < LOOKUP_TABLE_META_SIZE || start_index >= data.len() { + return Err(ProgramError::InvalidArgument); + } + bytemuck::try_cast_slice_mut(&mut data[start_index..]).map_err(|_| { + // Should be impossible because raw address data + // should be aligned and sized in multiples of 32 bytes + ProgramError::InvalidAccountData + }) + } + + /// Efficiently deserialize an address table without allocating + /// for stored addresses. + pub fn deserialize(data: &'a [u8]) -> Result, ProgramError> { + let program_state: ProgramState = + bincode::deserialize(data).map_err(|_| ProgramError::InvalidAccountData)?; + + let meta = match program_state { + ProgramState::LookupTable(meta) => Ok(meta), + ProgramState::Uninitialized => Err(ProgramError::UninitializedAccount), + }?; + + let raw_addresses_data = data.get(LOOKUP_TABLE_META_SIZE..).ok_or({ + // Should be impossible because table accounts must + // always be LOOKUP_TABLE_META_SIZE in length + ProgramError::InvalidAccountData + })?; + let addresses: &[Pubkey] = bytemuck::try_cast_slice(raw_addresses_data).map_err(|_| { + // Should be impossible because raw address data + // should be aligned and sized in multiples of 32 bytes + ProgramError::InvalidAccountData + })?; + + Ok(Self { + meta, + addresses: Cow::Borrowed(addresses), + }) + } +} + +#[cfg(test)] +mod tests { + use { + super::*, + solana_sdk::{hash::Hash, slot_hashes::SlotHashes}, + }; + + impl AddressLookupTable<'_> { + fn new_for_tests(meta: LookupTableMeta, num_addresses: usize) -> Self { + let mut addresses = Vec::with_capacity(num_addresses); + addresses.resize_with(num_addresses, Pubkey::new_unique); + AddressLookupTable { + meta, + addresses: Cow::Owned(addresses), + } + } + } + + impl LookupTableMeta { + fn new_for_tests() -> Self { + Self { + authority: Some(Pubkey::new_unique()), + ..LookupTableMeta::default() + } + } + } + + #[test] + fn test_lookup_table_meta_size() { + let lookup_table = ProgramState::LookupTable(LookupTableMeta::new_for_tests()); + let meta_size = bincode::serialized_size(&lookup_table).unwrap(); + assert!(meta_size as usize <= LOOKUP_TABLE_META_SIZE); + assert_eq!(meta_size as usize, 56); + + let lookup_table = ProgramState::LookupTable(LookupTableMeta::default()); + let meta_size = bincode::serialized_size(&lookup_table).unwrap(); + assert!(meta_size as usize <= LOOKUP_TABLE_META_SIZE); + assert_eq!(meta_size as usize, 24); + } + + #[test] + fn test_lookup_table_meta_status() { + let mut slot_hashes = SlotHashes::default(); + for slot in 1..=MAX_ENTRIES as Slot { + slot_hashes.add(slot, Hash::new_unique()); + } + + let most_recent_slot = slot_hashes.first().unwrap().0; + let least_recent_slot = slot_hashes.last().unwrap().0; + assert!(least_recent_slot < most_recent_slot); + + // 10 was chosen because the current slot isn't necessarily the next + // slot after the most recent block + let current_slot = most_recent_slot + 10; + + let active_table = LookupTableMeta { + deactivation_slot: Slot::MAX, + ..LookupTableMeta::default() + }; + + let just_started_deactivating_table = LookupTableMeta { + deactivation_slot: current_slot, + ..LookupTableMeta::default() + }; + + let _recently_started_deactivating_table = LookupTableMeta { + deactivation_slot: most_recent_slot, + ..LookupTableMeta::default() + }; + + let _almost_deactivated_table = LookupTableMeta { + deactivation_slot: least_recent_slot, + ..LookupTableMeta::default() + }; + + let deactivated_table = LookupTableMeta { + deactivation_slot: least_recent_slot - 1, + ..LookupTableMeta::default() + }; + + assert_eq!( + active_table.status(current_slot), + LookupTableStatus::Activated + ); + assert_eq!( + just_started_deactivating_table.status(current_slot), + LookupTableStatus::Deactivating { + remaining_blocks: MAX_ENTRIES.saturating_add(1), + } + ); + // [Core BPF]: TODO: These tests relies on specifically slot hashes + // being divergent from the current slot. + // assert_eq!( + // recently_started_deactivating_table.status(current_slot), + // LookupTableStatus::Deactivating { + // remaining_blocks: MAX_ENTRIES, + // } + // ); + // assert_eq!( + // almost_deactivated_table.status(current_slot), + // LookupTableStatus::Deactivating { + // remaining_blocks: 1, + // } + // ); + assert_eq!( + deactivated_table.status(current_slot), + LookupTableStatus::Deactivated + ); + } + + #[test] + fn test_overwrite_meta_data() { + let meta = LookupTableMeta::new_for_tests(); + let empty_table = ProgramState::LookupTable(meta.clone()); + let mut serialized_table_1 = bincode::serialize(&empty_table).unwrap(); + serialized_table_1.resize(LOOKUP_TABLE_META_SIZE, 0); + + let address_table = AddressLookupTable::new_for_tests(meta, 0); + let mut serialized_table_2 = vec![0; LOOKUP_TABLE_META_SIZE]; + AddressLookupTable::overwrite_meta_data(&mut serialized_table_2, address_table.meta) + .unwrap(); + + assert_eq!(serialized_table_1, serialized_table_2); + } + + #[test] + fn test_deserialize() { + assert_eq!( + AddressLookupTable::deserialize(&[]).err(), + Some(ProgramError::InvalidAccountData), + ); + + assert_eq!( + AddressLookupTable::deserialize(&[0u8; LOOKUP_TABLE_META_SIZE]).err(), + Some(ProgramError::UninitializedAccount), + ); + + fn test_case(num_addresses: usize) { + let lookup_table_meta = LookupTableMeta::new_for_tests(); + let address_table = AddressLookupTable::new_for_tests(lookup_table_meta, num_addresses); + let address_table_data = + AddressLookupTable::serialize_for_tests(address_table.clone()).unwrap(); + assert_eq!( + AddressLookupTable::deserialize(&address_table_data).unwrap(), + address_table, + ); + } + + for case in [0, 1, 10, 255, 256] { + test_case(case); + } + } + + #[test] + fn test_lookup_from_empty_table() { + let lookup_table = AddressLookupTable { + meta: LookupTableMeta::default(), + addresses: Cow::Owned(vec![]), + }; + + assert_eq!(lookup_table.lookup(0, &[]), Ok(vec![])); + assert_eq!( + lookup_table.lookup(0, &[0]), + Err(AddressLookupError::InvalidLookupIndex) + ); + } + + #[test] + fn test_lookup_from_deactivating_table() { + let current_slot = 1; + let addresses = vec![Pubkey::new_unique()]; + let lookup_table = AddressLookupTable { + meta: LookupTableMeta { + deactivation_slot: current_slot, + last_extended_slot: current_slot - 1, + ..LookupTableMeta::default() + }, + addresses: Cow::Owned(addresses.clone()), + }; + + assert_eq!( + lookup_table.meta.status(current_slot), + LookupTableStatus::Deactivating { + remaining_blocks: MAX_ENTRIES + 1 + } + ); + + assert_eq!( + lookup_table.lookup(current_slot, &[0]), + Ok(vec![addresses[0]]), + ); + } + + #[test] + fn test_lookup_from_deactivated_table() { + let current_slot = (MAX_ENTRIES + 1) as Slot; + let lookup_table = AddressLookupTable { + meta: LookupTableMeta { + deactivation_slot: 0, + last_extended_slot: 0, + ..LookupTableMeta::default() + }, + addresses: Cow::Owned(vec![]), + }; + + assert_eq!( + lookup_table.meta.status(current_slot), + LookupTableStatus::Deactivated + ); + assert_eq!( + lookup_table.lookup(current_slot, &[0]), + Err(AddressLookupError::LookupTableAccountNotFound) + ); + } + + #[test] + fn test_lookup_from_table_extended_in_current_slot() { + let current_slot = 0; + let addresses: Vec<_> = (0..2).map(|_| Pubkey::new_unique()).collect(); + let lookup_table = AddressLookupTable { + meta: LookupTableMeta { + last_extended_slot: current_slot, + last_extended_slot_start_index: 1, + ..LookupTableMeta::default() + }, + addresses: Cow::Owned(addresses.clone()), + }; + + assert_eq!( + lookup_table.lookup(current_slot, &[0]), + Ok(vec![addresses[0]]) + ); + assert_eq!( + lookup_table.lookup(current_slot, &[1]), + Err(AddressLookupError::InvalidLookupIndex), + ); + } + + #[test] + fn test_lookup_from_table_extended_in_previous_slot() { + let current_slot = 1; + let addresses: Vec<_> = (0..10).map(|_| Pubkey::new_unique()).collect(); + let lookup_table = AddressLookupTable { + meta: LookupTableMeta { + last_extended_slot: current_slot - 1, + last_extended_slot_start_index: 1, + ..LookupTableMeta::default() + }, + addresses: Cow::Owned(addresses.clone()), + }; + + assert_eq!( + lookup_table.lookup(current_slot, &[0, 3, 1, 5]), + Ok(vec![addresses[0], addresses[3], addresses[1], addresses[5]]) + ); + assert_eq!( + lookup_table.lookup(current_slot, &[10]), + Err(AddressLookupError::InvalidLookupIndex), + ); + } +}