diff --git a/Cargo.lock b/Cargo.lock index 58d28a75..7938dc23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "ahash" version = "0.7.6" @@ -22,6 +28,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "anchor-attribute-access-control" version = "0.26.0" @@ -201,6 +222,37 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + +[[package]] +name = "async-compression" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" +dependencies = [ + "brotli", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -327,6 +379,27 @@ dependencies = [ "syn", ] +[[package]] +name = "brotli" +version = "3.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bs58" version = "0.3.1" @@ -381,6 +454,12 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" + [[package]] name = "cc" version = "1.0.78" @@ -396,6 +475,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -431,6 +520,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-channel" version = "0.5.6" @@ -514,6 +612,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "derivation-path" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5c37193a1db1d8ed868c03ec7b152175f26160a5b740e5e484143877e0adf0" + [[package]] name = "digest" version = "0.9.0" @@ -534,18 +638,174 @@ dependencies = [ "subtle", ] +[[package]] +name = "eager" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe71d579d1812060163dff96056261deb5bf6729b100fa2e36a68b9649ba3d3" + +[[package]] +name = "ed25519" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "ed25519-dalek-bip32" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d2be62a4061b872c8c0873ee4fc6f101ce7b889d039f019c5fa2af471a59908" +dependencies = [ + "derivation-path", + "ed25519-dalek", + "hmac 0.12.1", + "sha2 0.10.6", +] + [[package]] name = "either" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enum-iterator" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2953d1df47ac0eb70086ccabf0275aa8da8591a28bd358ee2b52bd9f9e3ff9e9" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8958699f9359f0b04e691a13850d48b7de329138023876d07cbd024c2c820598" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + [[package]] name = "feature-probe" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" + +[[package]] +name = "futures-io" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" + +[[package]] +name = "futures-sink" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" + +[[package]] +name = "futures-task" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" + +[[package]] +name = "futures-util" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +dependencies = [ + "futures-core", + "futures-io", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "generic-array" version = "0.14.6" @@ -557,6 +817,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -583,6 +853,25 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "h2" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.11.2" @@ -610,6 +899,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hermit-abi" version = "0.2.6" @@ -629,6 +927,15 @@ dependencies = [ "digest 0.9.0", ] +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + [[package]] name = "hmac-drbg" version = "0.3.0" @@ -637,7 +944,94 @@ checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ "digest 0.9.0", "generic-array", - "hmac", + "hmac 0.8.1", +] + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +dependencies = [ + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", ] [[package]] @@ -656,6 +1050,22 @@ dependencies = [ "version_check", ] +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "ipnet" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11b0d96e660696543b251e58030cf9787df56da39dab19ad60eae7353040917e" + [[package]] name = "itertools" version = "0.10.5" @@ -710,6 +1120,16 @@ version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + [[package]] name = "libsecp256k1" version = "0.6.0" @@ -810,11 +1230,39 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + [[package]] name = "multisig" version = "0.1.0" dependencies = [ "anchor-lang", + "solana-address-lookup-table-program", ] [[package]] @@ -829,21 +1277,31 @@ dependencies = [ ] [[package]] -name = "num-traits" -version = "0.2.15" +name = "num-integer" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", + "num-traits", ] [[package]] -name = "num_cpus" -version = "1.15.0" +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] @@ -891,6 +1349,33 @@ dependencies = [ "crypto-mac", ] +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -928,6 +1413,15 @@ dependencies = [ "yansi", ] +[[package]] +name = "qstring" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" +dependencies = [ + "percent-encoding", +] + [[package]] name = "quote" version = "1.0.23" @@ -1044,6 +1538,62 @@ version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +[[package]] +name = "reqwest" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c" +dependencies = [ + "async-compression", + "base64 0.13.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-rustls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -1059,6 +1609,27 @@ dependencies = [ "semver", ] +[[package]] +name = "rustls" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +dependencies = [ + "base64 0.13.1", +] + [[package]] name = "rustversion" version = "1.0.11" @@ -1077,6 +1648,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "semver" version = "1.0.16" @@ -1123,6 +1704,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha2" version = "0.9.9" @@ -1157,6 +1750,12 @@ dependencies = [ "keccak", ] +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + [[package]] name = "sized-chunks" version = "0.6.5" @@ -1167,12 +1766,52 @@ dependencies = [ "typenum", ] +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "solana-address-lookup-table-program" +version = "1.14.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03f403a837de4e5d6135bb8100b7aa982a1e5ecc166386258ce3583cd12e2d7c" +dependencies = [ + "bincode", + "bytemuck", + "log", + "num-derive", + "num-traits", + "rustc_version", + "serde", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-program", + "solana-program-runtime", + "solana-sdk", + "thiserror", +] + [[package]] name = "solana-frozen-abi" version = "1.14.11" @@ -1219,6 +1858,41 @@ dependencies = [ "syn", ] +[[package]] +name = "solana-logger" +version = "1.14.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2bcbaba2c683e7bf80ff4f3a3cdcdaabdb0b21333e8d89aed06be136193d39" +dependencies = [ + "env_logger", + "lazy_static", + "log", +] + +[[package]] +name = "solana-measure" +version = "1.14.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33bbb0e7ee37cdfd18f2636e687cfafcc2e85a7768e283941fd08da022bd0f66" +dependencies = [ + "log", + "solana-sdk", +] + +[[package]] +name = "solana-metrics" +version = "1.14.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f77f7044d57975f001a2c8f3756e4a04f10ca886c69eb8ce0b1786aad52c663d" +dependencies = [ + "crossbeam-channel", + "gethostname", + "lazy_static", + "log", + "reqwest", + "solana-sdk", +] + [[package]] name = "solana-program" version = "1.14.11" @@ -1268,6 +1942,84 @@ dependencies = [ "zeroize", ] +[[package]] +name = "solana-program-runtime" +version = "1.14.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb4a1b61c005eb9c0767b215e428c51adfa6e0023691d37f05653a4cd29bce2b" +dependencies = [ + "base64 0.13.1", + "bincode", + "eager", + "enum-iterator", + "itertools", + "libc", + "libloading", + "log", + "num-derive", + "num-traits", + "rand", + "rustc_version", + "serde", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-measure", + "solana-metrics", + "solana-sdk", + "thiserror", +] + +[[package]] +name = "solana-sdk" +version = "1.14.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a46085d2548bb943e7210b28b09378e361350577b391a94457ad78af1a9f75ef" +dependencies = [ + "assert_matches", + "base64 0.13.1", + "bincode", + "bitflags", + "borsh", + "bs58 0.4.0", + "bytemuck", + "byteorder", + "chrono", + "derivation-path", + "digest 0.10.6", + "ed25519-dalek", + "ed25519-dalek-bip32", + "generic-array", + "hmac 0.12.1", + "itertools", + "js-sys", + "lazy_static", + "libsecp256k1", + "log", + "memmap2", + "num-derive", + "num-traits", + "pbkdf2 0.11.0", + "qstring", + "rand", + "rand_chacha", + "rustc_version", + "rustversion", + "serde", + "serde_bytes", + "serde_derive", + "serde_json", + "sha2 0.10.6", + "sha3", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-logger", + "solana-program", + "solana-sdk-macro", + "thiserror", + "uriparse", + "wasm-bindgen", +] + [[package]] name = "solana-sdk-macro" version = "1.14.11" @@ -1281,6 +2033,12 @@ dependencies = [ "syn", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "subtle" version = "2.4.1" @@ -1310,6 +2068,15 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.38" @@ -1337,9 +2104,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" dependencies = [ "anyhow", - "hmac", + "hmac 0.8.1", "once_cell", - "pbkdf2", + "pbkdf2 0.4.0", "rand", "rustc-hash", "sha2 0.9.9", @@ -1364,6 +2131,48 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +[[package]] +name = "tokio" +version = "1.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "windows-sys", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-util" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + [[package]] name = "toml" version = "0.5.10" @@ -1373,12 +2182,50 @@ dependencies = [ "serde", ] +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + [[package]] name = "typenum" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + [[package]] name = "unicode-ident" version = "1.0.6" @@ -1406,12 +2253,49 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "uriparse" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0200d0fc04d809396c2ad43f3c95da3582a2556eba8d453c1087f4120ee352ff" +dependencies = [ + "fnv", + "lazy_static", +] + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -1449,6 +2333,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.83" @@ -1488,6 +2384,56 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" version = "0.42.0" @@ -1545,6 +2491,15 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + [[package]] name = "yansi" version = "0.5.1" diff --git a/programs/multisig/Cargo.toml b/programs/multisig/Cargo.toml index 3e7be4c0..97fef6ae 100644 --- a/programs/multisig/Cargo.toml +++ b/programs/multisig/Cargo.toml @@ -17,3 +17,4 @@ default = [] [dependencies] anchor-lang = "=0.26.0" +solana-address-lookup-table-program = "=1.14.11" diff --git a/programs/multisig/src/errors.rs b/programs/multisig/src/errors.rs index cdbd58f9..14c1b0c7 100644 --- a/programs/multisig/src/errors.rs +++ b/programs/multisig/src/errors.rs @@ -20,12 +20,16 @@ pub enum MultisigError { NotAMember, #[msg("TransactionMessage is malformed.")] InvalidTransactionMessage, - #[msg("Transaction is stale.")] + #[msg("Transaction is stale")] StaleTransaction, - #[msg("Invalid transaction status.")] + #[msg("Invalid transaction status")] InvalidTransactionStatus, - #[msg("Transaction does not belong to the multisig.")] + #[msg("Transaction does not belong to the multisig")] TransactionNotForMultisig, - #[msg("Member already approved the transaction.")] + #[msg("Member already approved the transaction")] AlreadyApproved, + #[msg("Wrong number of accounts provided")] + InvalidNumberOfAccounts, + #[msg("Invalid account provided")] + InvalidAccount, } diff --git a/programs/multisig/src/events.rs b/programs/multisig/src/events.rs index 765d239c..f7c6988e 100644 --- a/programs/multisig/src/events.rs +++ b/programs/multisig/src/events.rs @@ -2,7 +2,7 @@ use anchor_lang::prelude::*; /// New multisig account is created. #[event] -pub struct MultisigCreatedEvent { +pub struct MultisigCreated { /// The multisig account. pub multisig: Pubkey, #[index] @@ -22,7 +22,7 @@ pub enum ConfigUpdateType { /// Multisig config is updated. #[event] -pub struct MultisigConfigUpdatedEvent { +pub struct MultisigConfigUpdated { /// The multisig account. pub multisig: Pubkey, #[index] @@ -35,7 +35,7 @@ pub struct MultisigConfigUpdatedEvent { /// New multisig transaction account is created. #[event] -pub struct TransactionCreatedEvent { +pub struct TransactionCreated { /// The multisig account. pub multisig: Pubkey, /// The transaction account. @@ -47,7 +47,7 @@ pub struct TransactionCreatedEvent { /// New multisig transaction account is created. #[event] -pub struct TransactionApprovedEvent { +pub struct TransactionApproved { /// The multisig account. pub multisig: Pubkey, /// The transaction account. @@ -56,3 +56,12 @@ pub struct TransactionApprovedEvent { /// Memo that was added by the creator. pub memo: Option, } + +/// New multisig transaction account is created. +#[event] +pub struct TransactionExecuted { + /// The multisig account. + pub multisig: Pubkey, + /// The transaction account. + pub transaction: Pubkey, +} diff --git a/programs/multisig/src/instructions/mod.rs b/programs/multisig/src/instructions/mod.rs index 4b84b4cc..236466d5 100644 --- a/programs/multisig/src/instructions/mod.rs +++ b/programs/multisig/src/instructions/mod.rs @@ -1,9 +1,11 @@ pub use multisig_config::*; pub use multisig_create::*; pub use transaction_create::*; +pub use transaction_execute::*; pub use transaction_vote::*; mod multisig_config; mod multisig_create; mod transaction_create; +mod transaction_execute; mod transaction_vote; diff --git a/programs/multisig/src/instructions/multisig_create.rs b/programs/multisig/src/instructions/multisig_create.rs index 87d9cee4..cf8af489 100644 --- a/programs/multisig/src/instructions/multisig_create.rs +++ b/programs/multisig/src/instructions/multisig_create.rs @@ -76,7 +76,7 @@ impl MultisigCreate<'_> { multisig.create_key = args.create_key; multisig.bump = *ctx.bumps.get("multisig").unwrap(); - emit!(MultisigCreatedEvent { + emit!(MultisigCreated { multisig: ctx.accounts.multisig.to_account_info().key.clone(), memo: args.memo, }); diff --git a/programs/multisig/src/instructions/transaction_create.rs b/programs/multisig/src/instructions/transaction_create.rs index 3494bea5..84b340b7 100644 --- a/programs/multisig/src/instructions/transaction_create.rs +++ b/programs/multisig/src/instructions/transaction_create.rs @@ -84,7 +84,7 @@ impl TransactionCreate<'_> { // Updated last transaction index in the multisig account. multisig.transaction_index = transaction_index; - emit!(TransactionCreatedEvent { + emit!(TransactionCreated { multisig: multisig_key, transaction: transaction.key(), memo: args.memo, diff --git a/programs/multisig/src/instructions/transaction_execute.rs b/programs/multisig/src/instructions/transaction_execute.rs new file mode 100644 index 00000000..19cc6a55 --- /dev/null +++ b/programs/multisig/src/instructions/transaction_execute.rs @@ -0,0 +1,96 @@ +use anchor_lang::prelude::*; +use anchor_lang::solana_program::program::invoke_signed; + +use crate::errors::*; +use crate::events::*; +use crate::state::*; +use crate::utils::*; + +#[derive(Accounts)] +pub struct TransactionExecute<'info> { + #[account( + mut, + seeds = [SEED_PREFIX, multisig.create_key.as_ref(), SEED_MULTISIG], + bump = multisig.bump, + )] + pub multisig: Box>, + + #[account( + mut, + seeds = [ + SEED_PREFIX, + multisig.key().as_ref(), + &transaction.transaction_index.to_le_bytes(), + SEED_TRANSACTION + ], + bump = transaction.bump, + constraint = transaction.status == TransactionStatus::ExecuteReady @ MultisigError::InvalidTransactionStatus, + constraint = transaction.multisig == multisig.key() @ MultisigError::TransactionNotForMultisig + )] + pub transaction: Account<'info, MultisigTransaction>, + + #[account( + mut, + constraint = multisig.is_member(member.key()).is_some() || multisig.allow_external_execute @ MultisigError::NotAMember, + constraint = multisig.member_has_permission(member.key(), Permission::Execute) || multisig.allow_external_execute @ MultisigError::Unauthorized, + )] + pub member: Signer<'info>, + // `remaining_accounts` must include the following accounts in the exact order: + // 1. AddressLookupTable accounts in the order they appear in `message.address_table_lookups`. + // 2. Accounts in the order they appear in `message.account_keys`. + // 3. Accounts in the order they appear in `message.address_table_lookups`. +} + +impl TransactionExecute<'_> { + /// Execute the multisig transaction. + /// The transaction must be `ExecuteReady`. + pub fn transaction_execute(ctx: Context) -> Result<()> { + let multisig = &mut ctx.accounts.multisig; + let transaction = &mut ctx.accounts.transaction; + + let multisig_key = multisig.key(); + let authority_seeds = &[ + SEED_PREFIX, + multisig_key.as_ref(), + &transaction.authority_index.to_le_bytes(), + SEED_AUTHORITY, + &[transaction.authority_bump], + ]; + + let authority_pubkey = + Pubkey::create_program_address(authority_seeds, ctx.program_id).unwrap(); + + let transaction_message = &transaction.message; + let num_lookups = transaction_message.address_table_lookups.len(); + let message_account_infos = ctx + .remaining_accounts + .get(num_lookups..) + .ok_or(MultisigError::InvalidNumberOfAccounts)?; + let address_lookup_table_account_infos = ctx + .remaining_accounts + .get(..num_lookups) + .ok_or(MultisigError::InvalidNumberOfAccounts)?; + + let executable_message = ExecutableTransactionMessage::new_validated( + transaction_message, + message_account_infos, + address_lookup_table_account_infos, + &authority_pubkey, + )?; + + // Execute the transaction instructions one-by-one. + for (ix, account_infos) in executable_message.to_instructions_and_accounts().iter() { + invoke_signed(ix, account_infos, &[authority_seeds])?; + } + + // Mark it as executed + transaction.status = TransactionStatus::Executed; + + emit!(TransactionExecuted { + multisig: multisig_key, + transaction: transaction.key(), + }); + + Ok(()) + } +} diff --git a/programs/multisig/src/instructions/transaction_vote.rs b/programs/multisig/src/instructions/transaction_vote.rs index c93e7469..c227764b 100644 --- a/programs/multisig/src/instructions/transaction_vote.rs +++ b/programs/multisig/src/instructions/transaction_vote.rs @@ -23,7 +23,7 @@ pub struct TransactionVote<'info> { SEED_PREFIX, multisig.key().as_ref(), &transaction.transaction_index.to_le_bytes(), - b"transaction" + SEED_TRANSACTION ], bump = transaction.bump, constraint = transaction.status == TransactionStatus::Active @ MultisigError::InvalidTransactionStatus, @@ -35,7 +35,7 @@ pub struct TransactionVote<'info> { #[account( mut, constraint = multisig.is_member(member.key()).is_some() @ MultisigError::NotAMember, - constraint = multisig.member_has_permission(member.key(), Permission::Vote) @MultisigError::Unauthorized, + constraint = multisig.member_has_permission(member.key(), Permission::Vote) @ MultisigError::Unauthorized, )] pub member: Signer<'info>, } @@ -65,7 +65,7 @@ impl TransactionVote<'_> { transaction.status = TransactionStatus::ExecuteReady; } - emit!(TransactionApprovedEvent { + emit!(TransactionApproved { multisig: multisig.key(), transaction: transaction.key(), memo: args.memo, diff --git a/programs/multisig/src/lib.rs b/programs/multisig/src/lib.rs index 2fcf721b..1b22dc34 100644 --- a/programs/multisig/src/lib.rs +++ b/programs/multisig/src/lib.rs @@ -44,6 +44,12 @@ pub mod multisig { TransactionVote::transaction_approve(ctx, args) } + /// Execute the multisig transaction. + /// The transaction must be `ExecuteReady`. + pub fn transaction_execute(ctx: Context) -> Result<()> { + TransactionExecute::transaction_execute(ctx) + } + // // instruction to remove a member/key from the multisig // pub fn remove_member(ctx: Context, old_member: Pubkey) -> Result<()> { // // if there is only one key in this multisig, reject the removal diff --git a/programs/multisig/src/state.rs b/programs/multisig/src/state.rs index 7411ba95..143bd639 100644 --- a/programs/multisig/src/state.rs +++ b/programs/multisig/src/state.rs @@ -68,7 +68,7 @@ impl Multisig { ) { self.stale_transaction_index = self.transaction_index; - emit!(MultisigConfigUpdatedEvent { + emit!(MultisigConfigUpdated { multisig: multisig_address, update, memo diff --git a/programs/multisig/src/utils/executable_transaction_message.rs b/programs/multisig/src/utils/executable_transaction_message.rs new file mode 100644 index 00000000..25e1606c --- /dev/null +++ b/programs/multisig/src/utils/executable_transaction_message.rs @@ -0,0 +1,230 @@ +use std::collections::HashMap; +use std::convert::From; + +use anchor_lang::prelude::*; +use anchor_lang::solana_program::instruction::Instruction; +use solana_address_lookup_table_program::state::AddressLookupTable; + +use crate::errors::*; +use crate::state::*; + +/// Sanitized and validated combination of a `MsTransactionMessage` and `AccountInfo`s it references. +pub struct ExecutableTransactionMessage<'a, 'info> { + /// Message which loaded a collection of lookup table addresses. + message: &'a MultisigTransactionMessage, + /// Cache that maps `AccountInfo`s mentioned in the message to the indices they are referred by in the message. + account_infos_by_message_index: HashMap>, +} + +impl<'a, 'info> ExecutableTransactionMessage<'a, 'info> { + /// # Arguments + /// `message` - a `MsTransactionMessage`. + /// `message_account_infos` - AccountInfo's that are expected to be mentioned in the message. + /// `address_lookup_table_account_infos` - AccountInfo's that are expected to correspond to the lookup tables mentioned in `message.address_table_lookups`. + /// `authority_pubkey` - The authority PDA that is expected to sign the message. + pub fn new_validated( + message: &'a MultisigTransactionMessage, + message_account_infos: &'a [AccountInfo<'info>], + address_lookup_table_account_infos: &'a [AccountInfo<'info>], + authority_pubkey: &'a Pubkey, + ) -> Result { + // CHECK: `address_lookup_table_account_infos` must be valid `AddressLookupTable`s + // and be the ones mentioned in `message.address_table_lookups`. + require_eq!( + address_lookup_table_account_infos.len(), + message.address_table_lookups.len(), + MultisigError::InvalidNumberOfAccounts + ); + let lookup_tables: HashMap<&Pubkey, &AccountInfo> = address_lookup_table_account_infos + .iter() + .map(|maybe_lookup_table| { + // The lookup table account must be owned by SolanaAddressLookupTableProgram. + require!( + maybe_lookup_table.owner == &solana_address_lookup_table_program::id(), + MultisigError::InvalidAccount + ); + // The lookup table must be mentioned in `message.address_table_lookups`. + require!( + message + .address_table_lookups + .iter() + .any(|lookup| &lookup.account_key == maybe_lookup_table.key), + MultisigError::InvalidAccount + ); + Ok((maybe_lookup_table.key, maybe_lookup_table)) + }) + .collect::>>()?; + + // CHECK: `account_infos` should exactly match the number of accounts mentioned in the message. + require_eq!( + message_account_infos.len(), + message.num_all_account_keys(), + MultisigError::InvalidNumberOfAccounts + ); + + let mut account_infos_by_message_index = HashMap::new(); + + // CHECK: `message.account_keys` should come first in `account_infos` and have modifiers expected by the message. + for (i, account_key) in message.account_keys.iter().enumerate() { + let account_info = &message_account_infos[i]; + require_keys_eq!( + *account_info.key, + *account_key, + MultisigError::InvalidAccount + ); + require_eq!( + account_info.is_writable, + message.is_static_writable_index(i), + MultisigError::InvalidAccount + ); + // For authority `is_signer` might differ because it's always false in the passed account infos. + if account_info.key != authority_pubkey { + require_eq!( + account_info.is_signer, + message.is_signer_index(i), + MultisigError::InvalidAccount + ); + } + account_infos_by_message_index.insert(i, account_info); + } + + // CHECK: `message_account_infos` loaded with lookup tables should come after `message.account_keys`, + // in the same order and with the same modifiers as listed in lookups. + // Track where we are in the message account indexes. Start after `message.account_keys`. + let mut message_indexes_cursor = message.account_keys.len(); + for lookup in message.address_table_lookups.iter() { + // This is cheap deserialization, it doesn't allocate/clone space for addresses. + let lookup_table_data = &lookup_tables + .get(&lookup.account_key) + .unwrap() + .data + .borrow()[..]; + let lookup_table = AddressLookupTable::deserialize(lookup_table_data) + .map_err(|_| MultisigError::InvalidAccount)?; + + // Accounts listed as writable in lookup, should be loaded as writable and non-signers. + for (i, index_in_lookup_table) in lookup.writable_indexes.iter().enumerate() { + // Check the modifiers. + let index = message_indexes_cursor + i; + let loaded_account_info = &message_account_infos + .get(index) + .ok_or(MultisigError::InvalidNumberOfAccounts)?; + require_eq!( + loaded_account_info.is_writable, + true, + MultisigError::InvalidAccount + ); + require_eq!( + loaded_account_info.is_signer, + false, + MultisigError::InvalidAccount + ); + // Check that the pubkey matches the one from the actual lookup table. + let pubkey_from_lookup_table = lookup_table + .addresses + .get(usize::from(*index_in_lookup_table)) + .ok_or(MultisigError::InvalidAccount)?; + require_keys_eq!( + *loaded_account_info.key, + *pubkey_from_lookup_table, + MultisigError::InvalidAccount + ); + + account_infos_by_message_index.insert(index, loaded_account_info); + } + message_indexes_cursor += lookup.writable_indexes.len(); + + // Accounts listed as readonly in lookup, should be loaded as readonly and non-signers. + for (i, index_in_lookup_table) in lookup.readonly_indexes.iter().enumerate() { + // Check the modifiers. + let index = message_indexes_cursor + i; + let loaded_account_info = &message_account_infos + .get(index) + .ok_or(MultisigError::InvalidNumberOfAccounts)?; + require_eq!( + loaded_account_info.is_writable, + false, + MultisigError::InvalidAccount + ); + require_eq!( + loaded_account_info.is_signer, + false, + MultisigError::InvalidAccount + ); + // Check that the pubkey matches the one from the actual lookup table. + let pubkey_from_lookup_table = lookup_table + .addresses + .get(usize::from(*index_in_lookup_table)) + .ok_or(MultisigError::InvalidAccount)?; + require_keys_eq!( + *loaded_account_info.key, + *pubkey_from_lookup_table, + MultisigError::InvalidAccount + ); + + account_infos_by_message_index.insert(index, loaded_account_info); + } + message_indexes_cursor += lookup.readonly_indexes.len(); + } + + Ok(Self { + message, + account_infos_by_message_index, + }) + } + + pub fn to_instructions_and_accounts(&self) -> Vec<(Instruction, Vec>)> { + let mut executable_instructions = vec![]; + + for ms_compiled_instruction in self.message.instructions.iter() { + let ix_accounts: Vec<(AccountInfo<'info>, AccountMeta)> = ms_compiled_instruction + .account_indexes + .iter() + .map(|account_index| { + let account_info = *self + .account_infos_by_message_index + .get(&usize::from(*account_index)) + .unwrap(); + + // `is_signer` cannot just be taken from the account info, because for `authority` + // it's always false in the passed account infos, but might be true in the actual instructions. + let is_signer = self.message.is_signer_index(usize::from(*account_index)); + + let account_meta = if account_info.is_writable { + AccountMeta::new(*account_info.key, is_signer) + } else { + AccountMeta::new_readonly(*account_info.key, is_signer) + }; + + (account_info.clone(), account_meta) + }) + .collect(); + + // Program ID should always be in the static accounts list. + let ix_program_account_info = *self + .account_infos_by_message_index + .get(&usize::from(ms_compiled_instruction.program_id_index)) + .unwrap(); + + let ix = Instruction { + program_id: *ix_program_account_info.key, + accounts: ix_accounts + .iter() + .map(|(_, account_meta)| account_meta.clone()) + .collect(), + data: ms_compiled_instruction.data.clone(), + }; + + let mut account_infos: Vec = ix_accounts + .into_iter() + .map(|(account_info, _)| account_info) + .collect(); + // Add Program ID + account_infos.push(ix_program_account_info.clone()); + + executable_instructions.push((ix, account_infos)); + } + + executable_instructions + } +} diff --git a/programs/multisig/src/utils/mod.rs b/programs/multisig/src/utils/mod.rs index 18ede3ca..c69f480a 100644 --- a/programs/multisig/src/utils/mod.rs +++ b/programs/multisig/src/utils/mod.rs @@ -1,3 +1,5 @@ +mod executable_transaction_message; mod small_vec; +pub use executable_transaction_message::*; pub use small_vec::*; diff --git a/sdk/multisig/idl/multisig.json b/sdk/multisig/idl/multisig.json index 589c5485..ca7c2c25 100644 --- a/sdk/multisig/idl/multisig.json +++ b/sdk/multisig/idl/multisig.json @@ -137,6 +137,31 @@ } } ] + }, + { + "name": "transactionExecute", + "docs": [ + "Execute the multisig transaction.", + "The transaction must be `ExecuteReady`." + ], + "accounts": [ + { + "name": "multisig", + "isMut": true, + "isSigner": false + }, + { + "name": "transaction", + "isMut": true, + "isSigner": false + }, + { + "name": "member", + "isMut": true, + "isSigner": true + } + ], + "args": [] } ], "accounts": [ @@ -664,7 +689,7 @@ ], "events": [ { - "name": "MultisigCreatedEvent", + "name": "MultisigCreated", "fields": [ { "name": "multisig", @@ -681,7 +706,7 @@ ] }, { - "name": "MultisigConfigUpdatedEvent", + "name": "MultisigConfigUpdated", "fields": [ { "name": "multisig", @@ -705,7 +730,7 @@ ] }, { - "name": "TransactionCreatedEvent", + "name": "TransactionCreated", "fields": [ { "name": "multisig", @@ -727,7 +752,7 @@ ] }, { - "name": "TransactionApprovedEvent", + "name": "TransactionApproved", "fields": [ { "name": "multisig", @@ -747,6 +772,21 @@ "index": true } ] + }, + { + "name": "TransactionExecuted", + "fields": [ + { + "name": "multisig", + "type": "publicKey", + "index": false + }, + { + "name": "transaction", + "type": "publicKey", + "index": false + } + ] } ], "errors": [ @@ -798,22 +838,32 @@ { "code": 6009, "name": "StaleTransaction", - "msg": "Transaction is stale." + "msg": "Transaction is stale" }, { "code": 6010, "name": "InvalidTransactionStatus", - "msg": "Invalid transaction status." + "msg": "Invalid transaction status" }, { "code": 6011, "name": "TransactionNotForMultisig", - "msg": "Transaction does not belong to the multisig." + "msg": "Transaction does not belong to the multisig" }, { "code": 6012, "name": "AlreadyApproved", - "msg": "Member already approved the transaction." + "msg": "Member already approved the transaction" + }, + { + "code": 6013, + "name": "InvalidNumberOfAccounts", + "msg": "Wrong number of accounts provided" + }, + { + "code": 6014, + "name": "InvalidAccount", + "msg": "Invalid account provided" } ], "metadata": { diff --git a/sdk/multisig/src/generated/errors/index.ts b/sdk/multisig/src/generated/errors/index.ts index b6f1ef47..6d48c6ff 100644 --- a/sdk/multisig/src/generated/errors/index.ts +++ b/sdk/multisig/src/generated/errors/index.ts @@ -210,7 +210,7 @@ createErrorFromNameLookup.set( ) /** - * StaleTransaction: 'Transaction is stale.' + * StaleTransaction: 'Transaction is stale' * * @category Errors * @category generated @@ -219,7 +219,7 @@ export class StaleTransactionError extends Error { readonly code: number = 0x1779 readonly name: string = 'StaleTransaction' constructor() { - super('Transaction is stale.') + super('Transaction is stale') if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, StaleTransactionError) } @@ -233,7 +233,7 @@ createErrorFromNameLookup.set( ) /** - * InvalidTransactionStatus: 'Invalid transaction status.' + * InvalidTransactionStatus: 'Invalid transaction status' * * @category Errors * @category generated @@ -242,7 +242,7 @@ export class InvalidTransactionStatusError extends Error { readonly code: number = 0x177a readonly name: string = 'InvalidTransactionStatus' constructor() { - super('Invalid transaction status.') + super('Invalid transaction status') if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, InvalidTransactionStatusError) } @@ -256,7 +256,7 @@ createErrorFromNameLookup.set( ) /** - * TransactionNotForMultisig: 'Transaction does not belong to the multisig.' + * TransactionNotForMultisig: 'Transaction does not belong to the multisig' * * @category Errors * @category generated @@ -265,7 +265,7 @@ export class TransactionNotForMultisigError extends Error { readonly code: number = 0x177b readonly name: string = 'TransactionNotForMultisig' constructor() { - super('Transaction does not belong to the multisig.') + super('Transaction does not belong to the multisig') if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, TransactionNotForMultisigError) } @@ -282,7 +282,7 @@ createErrorFromNameLookup.set( ) /** - * AlreadyApproved: 'Member already approved the transaction.' + * AlreadyApproved: 'Member already approved the transaction' * * @category Errors * @category generated @@ -291,7 +291,7 @@ export class AlreadyApprovedError extends Error { readonly code: number = 0x177c readonly name: string = 'AlreadyApproved' constructor() { - super('Member already approved the transaction.') + super('Member already approved the transaction') if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, AlreadyApprovedError) } @@ -304,6 +304,49 @@ createErrorFromNameLookup.set( () => new AlreadyApprovedError() ) +/** + * InvalidNumberOfAccounts: 'Wrong number of accounts provided' + * + * @category Errors + * @category generated + */ +export class InvalidNumberOfAccountsError extends Error { + readonly code: number = 0x177d + readonly name: string = 'InvalidNumberOfAccounts' + constructor() { + super('Wrong number of accounts provided') + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, InvalidNumberOfAccountsError) + } + } +} + +createErrorFromCodeLookup.set(0x177d, () => new InvalidNumberOfAccountsError()) +createErrorFromNameLookup.set( + 'InvalidNumberOfAccounts', + () => new InvalidNumberOfAccountsError() +) + +/** + * InvalidAccount: 'Invalid account provided' + * + * @category Errors + * @category generated + */ +export class InvalidAccountError extends Error { + readonly code: number = 0x177e + readonly name: string = 'InvalidAccount' + constructor() { + super('Invalid account provided') + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, InvalidAccountError) + } + } +} + +createErrorFromCodeLookup.set(0x177e, () => new InvalidAccountError()) +createErrorFromNameLookup.set('InvalidAccount', () => new InvalidAccountError()) + /** * Attempts to resolve a custom program error from the provided error code. * @category Errors diff --git a/sdk/multisig/src/generated/instructions/index.ts b/sdk/multisig/src/generated/instructions/index.ts index 15e386c6..1462942f 100644 --- a/sdk/multisig/src/generated/instructions/index.ts +++ b/sdk/multisig/src/generated/instructions/index.ts @@ -2,3 +2,4 @@ export * from './multisigAddMember' export * from './multisigCreate' export * from './transactionApprove' export * from './transactionCreate' +export * from './transactionExecute' diff --git a/sdk/multisig/src/generated/instructions/transactionExecute.ts b/sdk/multisig/src/generated/instructions/transactionExecute.ts new file mode 100644 index 00000000..1c08a517 --- /dev/null +++ b/sdk/multisig/src/generated/instructions/transactionExecute.ts @@ -0,0 +1,88 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +import * as web3 from '@solana/web3.js' + +/** + * @category Instructions + * @category TransactionExecute + * @category generated + */ +export const transactionExecuteStruct = new beet.BeetArgsStruct<{ + instructionDiscriminator: number[] /* size: 8 */ +}>( + [['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)]], + 'TransactionExecuteInstructionArgs' +) +/** + * Accounts required by the _transactionExecute_ instruction + * + * @property [_writable_] multisig + * @property [_writable_] transaction + * @property [_writable_, **signer**] member + * @category Instructions + * @category TransactionExecute + * @category generated + */ +export type TransactionExecuteInstructionAccounts = { + multisig: web3.PublicKey + transaction: web3.PublicKey + member: web3.PublicKey + anchorRemainingAccounts?: web3.AccountMeta[] +} + +export const transactionExecuteInstructionDiscriminator = [ + 93, 171, 78, 134, 252, 84, 186, 189, +] + +/** + * Creates a _TransactionExecute_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @category Instructions + * @category TransactionExecute + * @category generated + */ +export function createTransactionExecuteInstruction( + accounts: TransactionExecuteInstructionAccounts, + programId = new web3.PublicKey('Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS') +) { + const [data] = transactionExecuteStruct.serialize({ + instructionDiscriminator: transactionExecuteInstructionDiscriminator, + }) + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.multisig, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.transaction, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.member, + isWritable: true, + isSigner: true, + }, + ] + + if (accounts.anchorRemainingAccounts != null) { + for (const acc of accounts.anchorRemainingAccounts) { + keys.push(acc) + } + } + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }) + return ix +}