diff --git a/Cargo.lock b/Cargo.lock index eabb1b8145..43cb9ded98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -152,9 +152,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.89" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95" dependencies = [ "backtrace", ] @@ -200,7 +200,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -418,6 +418,15 @@ dependencies = [ "constant_time_eq", ] +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -427,6 +436,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2", +] + [[package]] name = "borsh" version = "1.5.1" @@ -447,7 +465,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", "syn_derive", ] @@ -533,7 +551,7 @@ checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -598,15 +616,21 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.30" +version = "1.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" dependencies = [ "jobserver", "libc", "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.0" @@ -723,7 +747,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -774,6 +798,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "connect_disconnect_client" version = "1.0.0-rc1" @@ -796,6 +830,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "const-oid" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6f2aa4d0537bcc1c74df8755072bd31c1ef1a3a1b85a68e8404a8c353b7b8b" + [[package]] name = "constant_time_eq" version = "0.3.1" @@ -827,6 +867,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -1094,6 +1144,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83bd3bb4314701c568e340cd8cf78c975aa0ca79e03d3f6d1677d5b0c9c0c03" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -1104,6 +1166,16 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "cursive" version = "0.20.0" @@ -1184,7 +1256,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -1195,7 +1267,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -1241,6 +1313,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "der" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79b71cca7d95d7681a4b3b9cdf63c8dbc3730d0584c2c74e31416d64a90493f4" +dependencies = [ + "const-oid", +] + [[package]] name = "deranged" version = "0.3.11" @@ -1261,7 +1342,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -1270,13 +1351,22 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", "crypto-common", "subtle", ] @@ -1335,6 +1425,18 @@ dependencies = [ "shared_child", ] +[[package]] +name = "ecdsa" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43ee23aa5b4f68c7a092b5c3beb25f50c406adc75e2363634f242f28ab255372" +dependencies = [ + "der", + "elliptic-curve", + "hmac 0.11.0", + "signature", +] + [[package]] name = "educe" version = "0.4.23" @@ -1353,6 +1455,22 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "elliptic-curve" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beca177dcb8eb540133e7680baff45e7cc4d93bf22002676cec549f82343721b" +dependencies = [ + "crypto-bigint", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "email_address" version = "0.2.9" @@ -1404,7 +1522,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -1424,7 +1542,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -1437,7 +1555,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -1458,7 +1576,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -1554,6 +1672,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ff" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f40b2dcd8bc322217a5f6559ae5f9e9d1de202a2ecee2e9eafcbece7562a4f" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "filetime" version = "0.2.25" @@ -1705,7 +1833,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -1804,6 +1932,17 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "group" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c363a5301b8f153d80747126a04b3c82073b9fe3130571a9d170cacdeaf7912" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "h2" version = "0.4.6" @@ -1937,13 +2076,23 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac", + "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", + "digest 0.10.7", ] [[package]] @@ -2039,23 +2188,6 @@ dependencies = [ "want", ] -[[package]] -name = "hyper-rustls" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" -dependencies = [ - "futures-util", - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", -] - [[package]] name = "hyper-tls" version = "0.6.0" @@ -2109,7 +2241,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -2340,6 +2472,28 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "jobserver" version = "0.1.32" @@ -2358,6 +2512,26 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonwebkey" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c57c852b14147e2bd58c14fde40398864453403ef632b1101db130282ee6e2cc" +dependencies = [ + "base64 0.13.1", + "bitflags 1.3.2", + "generic-array", + "jsonwebtoken", + "num-bigint", + "p256", + "rand 0.8.5", + "serde", + "serde_json", + "thiserror", + "yasna", + "zeroize", +] + [[package]] name = "jsonwebtoken" version = "8.3.0" @@ -2366,7 +2540,7 @@ checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ "base64 0.21.7", "pem", - "ring 0.16.20", + "ring", "serde", "serde_json", "simple_asn1", @@ -2395,9 +2569,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.160" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0b21006cd1874ae9e650973c565615676dc4a274c965bb0a73796dac838ce4f" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "libm" @@ -2526,7 +2700,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ "cfg-if", - "digest", + "digest 0.10.7", ] [[package]] @@ -2618,6 +2792,12 @@ dependencies = [ "tempfile", ] +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + [[package]] name = "nibble_vec" version = "0.1.0" @@ -2767,6 +2947,40 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.6.0", + "block2", + "libc", + "objc2", +] + [[package]] name = "object" version = "0.36.5" @@ -2791,6 +3005,12 @@ version = "11.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "openssl" version = "0.10.68" @@ -2814,7 +3034,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -2888,6 +3108,17 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +[[package]] +name = "p256" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d053368e1bae4c8a672953397bd1bd7183dde1c72b0b7612a15719173148d186" +dependencies = [ + "ecdsa", + "elliptic-curve", + "sha2 0.9.9", +] + [[package]] name = "papergrid" version = "0.10.0" @@ -3027,6 +3258,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee3ef9b64d26bad0536099c816c6734379e45bbd5f14798def6809e5cc350447" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.31" @@ -3095,7 +3336,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -3108,11 +3349,11 @@ dependencies = [ "byteorder", "bytes", "fallible-iterator 0.2.0", - "hmac", + "hmac 0.12.1", "md-5", "memchr", "rand 0.8.5", - "sha2", + "sha2 0.10.8", "stringprep", ] @@ -3568,9 +3809,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.8" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" +checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" dependencies = [ "base64 0.22.1", "bytes", @@ -3582,7 +3823,6 @@ dependencies = [ "http-body", "http-body-util", "hyper", - "hyper-rustls", "hyper-tls", "hyper-util", "ipnet", @@ -3597,7 +3837,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.1", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-native-tls", @@ -3608,7 +3848,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "windows-registry", + "winreg", ] [[package]] @@ -3629,27 +3869,12 @@ dependencies = [ "cc", "libc", "once_cell", - "spin 0.5.2", - "untrusted 0.7.1", + "spin", + "untrusted", "web-sys", "winapi", ] -[[package]] -name = "ring" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" -dependencies = [ - "cc", - "cfg-if", - "getrandom", - "libc", - "spin 0.9.8", - "untrusted 0.9.0", - "windows-sys 0.52.0", -] - [[package]] name = "rkyv" version = "0.7.45" @@ -3765,19 +3990,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rustls" -version = "0.23.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" -dependencies = [ - "once_cell", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - [[package]] name = "rustls-pemfile" version = "2.2.0" @@ -3793,17 +4005,6 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" -[[package]] -name = "rustls-webpki" -version = "0.102.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" -dependencies = [ - "ring 0.17.8", - "rustls-pki-types", - "untrusted 0.9.0", -] - [[package]] name = "rustversion" version = "1.0.18" @@ -3920,7 +4121,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags 2.6.0", - "core-foundation", + "core-foundation 0.9.4", "core-foundation-sys", "libc", "security-framework-sys", @@ -3962,14 +4163,14 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "indexmap 2.6.0", "itoa", @@ -4036,7 +4237,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -4061,7 +4262,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -4072,7 +4273,20 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", ] [[package]] @@ -4083,7 +4297,7 @@ checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] [[package]] @@ -4092,7 +4306,7 @@ version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest", + "digest 0.10.7", "keccak", ] @@ -4151,6 +4365,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2807892cfa58e081aa1f1111391c7a0649d4fa127a4ffbe34bcbfb35a1171a4" +dependencies = [ + "digest 0.9.0", + "rand_core 0.6.4", +] + [[package]] name = "simdutf8" version = "0.1.5" @@ -4316,7 +4540,7 @@ dependencies = [ "quote", "spacetimedb-primitives", "spacetimedb-sql-parser", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -4378,6 +4602,7 @@ dependencies = [ "toml 0.8.19", "wasmbin", "wasmtime", + "webbrowser", ] [[package]] @@ -4388,6 +4613,7 @@ dependencies = [ "async-trait", "axum", "axum-extra", + "blake3", "bytes", "bytestring", "chrono", @@ -4399,6 +4625,8 @@ dependencies = [ "hyper", "hyper-util", "itoa", + "jsonwebkey", + "jsonwebtoken", "lazy_static", "log", "mime", @@ -4418,6 +4646,7 @@ dependencies = [ "tokio-stream", "tokio-tungstenite", "tracing", + "uuid", ] [[package]] @@ -4882,10 +5111,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] -name = "spin" -version = "0.9.8" +name = "spki" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "5c01a0c15da1b0b0e1494112e7af814a678fec9bd157881b49beac661e9b6f32" +dependencies = [ + "der", +] [[package]] name = "sptr" @@ -5058,7 +5290,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -5073,9 +5305,9 @@ dependencies = [ [[package]] name = "subtle" -version = "2.6.1" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" @@ -5090,9 +5322,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.79" +version = "2.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021" dependencies = [ "proc-macro2", "quote", @@ -5108,7 +5340,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -5122,9 +5354,6 @@ name = "sync_wrapper" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" -dependencies = [ - "futures-core", -] [[package]] name = "synstructure" @@ -5146,7 +5375,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -5173,20 +5402,20 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.6.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ - "bitflags 2.6.0", - "core-foundation", + "bitflags 1.3.2", + "core-foundation 0.9.4", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.6.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" dependencies = [ "core-foundation-sys", "libc", @@ -5239,6 +5468,12 @@ version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" +[[package]] +name = "target-triple" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078" + [[package]] name = "tempdir" version = "0.3.7" @@ -5329,7 +5564,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -5435,7 +5670,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -5474,17 +5709,6 @@ dependencies = [ "whoami", ] -[[package]] -name = "tokio-rustls" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" -dependencies = [ - "rustls", - "rustls-pki-types", - "tokio", -] - [[package]] name = "tokio-stream" version = "0.1.16" @@ -5644,7 +5868,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -5747,14 +5971,15 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trybuild" -version = "1.0.100" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8923cde76a6329058a86f04d033f0945a2c6df8b94093512e4ab188b3e3a8950" +checksum = "8dcd332a5496c026f1e14b7f3d2b7bd98e509660c04239c58b0ba38a12daded4" dependencies = [ "glob", "serde", "serde_derive", "serde_json", + "target-triple", "termcolor", "toml 0.8.19", ] @@ -5848,12 +6073,6 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - [[package]] name = "upgrade-version" version = "0.1.0" @@ -6015,7 +6234,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", "wasm-bindgen-shared", ] @@ -6049,7 +6268,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6103,7 +6322,7 @@ checksum = "3961bf864c790b5a06939f8f36d2a1a6be5bf0f926ddc25fb159b1766f2874db" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", "synstructure 0.13.1", "thiserror", ] @@ -6200,7 +6419,7 @@ dependencies = [ "rustix", "serde", "serde_derive", - "sha2", + "sha2 0.10.8", "toml 0.8.19", "windows-sys 0.52.0", "zstd", @@ -6215,7 +6434,7 @@ dependencies = [ "anyhow", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", "wasmtime-component-util", "wasmtime-wit-bindgen", "wit-parser", @@ -6317,7 +6536,7 @@ checksum = "a4b0c1f76891f778db9602ee3fbb4eb7e9a3f511847d1fb1b69eddbcea28303c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -6342,6 +6561,24 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webbrowser" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5f07fb9bc8de2ddfe6b24a71a75430673fd679e568c48b52716cef1cfae923" +dependencies = [ + "block2", + "core-foundation 0.10.0", + "home", + "jni", + "log", + "ndk-context", + "objc2", + "objc2-foundation", + "url", + "web-sys", +] + [[package]] name = "which" version = "4.4.2" @@ -6436,7 +6673,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -6447,18 +6684,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", -] - -[[package]] -name = "windows-registry" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" -dependencies = [ - "windows-result", - "windows-strings", - "windows-targets 0.52.6", + "syn 2.0.82", ] [[package]] @@ -6480,6 +6706,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -6507,6 +6742,21 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -6538,6 +6788,12 @@ dependencies = [ "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -6550,6 +6806,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -6562,6 +6824,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -6580,6 +6848,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -6592,6 +6866,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -6604,6 +6884,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -6616,6 +6902,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -6637,6 +6929,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "wit-parser" version = "0.217.0" @@ -6696,6 +6998,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +[[package]] +name = "yasna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e262a29d0e61ccf2b6190d7050d4b237535fc76ce4c1210d9caa316f71dffa75" +dependencies = [ + "num-bigint", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -6714,14 +7025,28 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "d68d9dcec5f9b43a30d38c49f91dfedfaac384cb8f085faca366c26207dd1619" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] [[package]] name = "zstd" diff --git a/Cargo.toml b/Cargo.toml index 0295922e05..960dec6ff1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -248,6 +248,7 @@ urlencoding = "2.1.2" uuid = { version = "1.2.1", features = ["v4"] } walkdir = "2.2.5" wasmbin = "0.6" +webbrowser = "1.0.2" # Vendor the openssl we rely on, rather than depend on a # potentially very old system version. diff --git a/crates/bindings-csharp/Runtime/Internal/Autogen/RawScheduleDefV9.cs b/crates/bindings-csharp/Runtime/Internal/Autogen/RawScheduleDefV9.cs index e361088054..568a13b03c 100644 --- a/crates/bindings-csharp/Runtime/Internal/Autogen/RawScheduleDefV9.cs +++ b/crates/bindings-csharp/Runtime/Internal/Autogen/RawScheduleDefV9.cs @@ -19,14 +19,18 @@ public partial class RawScheduleDefV9 public string Name; [DataMember(Name = "reducer_name")] public string ReducerName; + [DataMember(Name = "scheduled_at_column")] + public ushort ScheduledAtColumn; public RawScheduleDefV9( string Name, - string ReducerName + string ReducerName, + ushort ScheduledAtColumn ) { this.Name = Name; this.ReducerName = ReducerName; + this.ScheduledAtColumn = ScheduledAtColumn; } public RawScheduleDefV9() diff --git a/crates/bindings-csharp/Runtime/Internal/Autogen/RawTableDefV9.cs b/crates/bindings-csharp/Runtime/Internal/Autogen/RawTableDefV9.cs index 2c243f5c56..83e69a585a 100644 --- a/crates/bindings-csharp/Runtime/Internal/Autogen/RawTableDefV9.cs +++ b/crates/bindings-csharp/Runtime/Internal/Autogen/RawTableDefV9.cs @@ -20,7 +20,7 @@ public partial class RawTableDefV9 [DataMember(Name = "product_type_ref")] public uint ProductTypeRef; [DataMember(Name = "primary_key")] - public ushort? PrimaryKey; + public System.Collections.Generic.List PrimaryKey; [DataMember(Name = "indexes")] public System.Collections.Generic.List Indexes; [DataMember(Name = "constraints")] @@ -37,7 +37,7 @@ public partial class RawTableDefV9 public RawTableDefV9( string Name, uint ProductTypeRef, - ushort? PrimaryKey, + System.Collections.Generic.List PrimaryKey, System.Collections.Generic.List Indexes, System.Collections.Generic.List Constraints, System.Collections.Generic.List Sequences, @@ -60,6 +60,7 @@ SpacetimeDB.Internal.TableAccess TableAccess public RawTableDefV9() { this.Name = ""; + this.PrimaryKey = new(); this.Indexes = new(); this.Constraints = new(); this.Sequences = new(); diff --git a/crates/bindings-macro/src/lib.rs b/crates/bindings-macro/src/lib.rs index bc4ba11a2f..0adca26ca0 100644 --- a/crates/bindings-macro/src/lib.rs +++ b/crates/bindings-macro/src/lib.rs @@ -1030,7 +1030,19 @@ fn table_impl(mut args: TableArgs, mut item: MutItem) -> syn:: let unique_col_ids = unique_columns.iter().map(|col| col.index); let primary_col_id = primary_key_column.iter().map(|col| col.index); let sequence_col_ids = sequenced_columns.iter().map(|col| col.index); - let scheduled_reducer_ident = args.scheduled.iter(); + + let schedule = args + .scheduled + .as_ref() + .map(|reducer| { + // scheduled_at was inserted as the last field + let scheduled_at_id = (fields.len() - 1) as u16; + quote!(spacetimedb::table::ScheduleDesc { + reducer_name: <#reducer as spacetimedb::rt::ReducerInfo>::NAME, + scheduled_at_column: #scheduled_at_id, + }) + }) + .into_iter(); let unique_err = if !unique_columns.is_empty() { quote!(spacetimedb::UniqueConstraintViolation) @@ -1063,7 +1075,7 @@ fn table_impl(mut args: TableArgs, mut item: MutItem) -> syn:: const INDEXES: &'static [spacetimedb::table::IndexDesc<'static>] = &[#(#index_descs),*]; #(const PRIMARY_KEY: Option = Some(#primary_col_id);)* const SEQUENCES: &'static [u16] = &[#(#sequence_col_ids),*]; - #(const SCHEDULED_REDUCER_NAME: Option<&'static str> = Some(<#scheduled_reducer_ident as spacetimedb::rt::ReducerInfo>::NAME);)* + #(const SCHEDULE: Option> = Some(#schedule);)* #table_id_from_name_func } diff --git a/crates/bindings/src/lib.rs b/crates/bindings/src/lib.rs index 8a14cd760d..0c674e1d71 100644 --- a/crates/bindings/src/lib.rs +++ b/crates/bindings/src/lib.rs @@ -35,6 +35,7 @@ pub use spacetimedb_lib::ser::Serialize; pub use spacetimedb_lib::Address; pub use spacetimedb_lib::AlgebraicValue; pub use spacetimedb_lib::Identity; +pub use spacetimedb_lib::ScheduleAt; pub use spacetimedb_primitives::TableId; pub use sys::Errno; pub use table::{AutoIncOverflow, BTreeIndex, Table, TryInsertError, UniqueColumn, UniqueConstraintViolation}; diff --git a/crates/bindings/src/rt.rs b/crates/bindings/src/rt.rs index cd1232cde4..1d9d53111a 100644 --- a/crates/bindings/src/rt.rs +++ b/crates/bindings/src/rt.rs @@ -165,6 +165,20 @@ pub trait TableColumn { } impl TableColumn for T {} +/// Assert that the primary_key column of a scheduled table is a u64. +pub const fn assert_scheduled_table_primary_key() {} + +mod sealed { + pub trait Sealed {} +} +#[diagnostic::on_unimplemented( + message = "scheduled table primary key must be a `u64`", + label = "should be `u64`, not `{Self}`" +)] +pub trait ScheduledTablePrimaryKey: sealed::Sealed {} +impl sealed::Sealed for u64 {} +impl ScheduledTablePrimaryKey for u64 {} + /// Used in the last type parameter of `Reducer` to indicate that the /// context argument *should* be passed to the reducer logic. pub struct ContextArg; @@ -331,8 +345,8 @@ pub fn register_table() { for &col in T::SEQUENCES { table = table.with_column_sequence(col, None); } - if let Some(scheduled_reducer) = T::SCHEDULED_REDUCER_NAME { - table = table.with_schedule(scheduled_reducer, None); + if let Some(schedule) = T::SCHEDULE { + table = table.with_schedule(schedule.reducer_name, schedule.scheduled_at_column, None); } table.finish(); diff --git a/crates/bindings/src/table.rs b/crates/bindings/src/table.rs index fbf2261d9d..5a3e2b6fb0 100644 --- a/crates/bindings/src/table.rs +++ b/crates/bindings/src/table.rs @@ -124,7 +124,7 @@ pub trait TableInternal: Sized { const INDEXES: &'static [IndexDesc<'static>]; const PRIMARY_KEY: Option = None; const SEQUENCES: &'static [u16]; - const SCHEDULED_REDUCER_NAME: Option<&'static str> = None; + const SCHEDULE: Option> = None; /// Returns the ID of this table. fn table_id() -> TableId; @@ -143,6 +143,11 @@ pub enum IndexAlgo<'a> { BTree { columns: &'a [u16] }, } +pub struct ScheduleDesc<'a> { + pub reducer_name: &'a str, + pub scheduled_at_column: u16, +} + #[doc(hidden)] pub trait __MapRowTypeToTable { type Table: Table; diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 9ed96517d4..646e6fadf1 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -64,6 +64,7 @@ tokio-tungstenite.workspace = true toml.workspace = true wasmbin.workspace = true wasmtime.workspace = true +webbrowser.workspace = true [dev-dependencies] insta.workspace = true diff --git a/crates/cli/src/config.rs b/crates/cli/src/config.rs index a21e8b17c8..7dcf2c108c 100644 --- a/crates/cli/src/config.rs +++ b/crates/cli/src/config.rs @@ -104,6 +104,7 @@ pub struct RawConfig { identity_configs: Vec, #[serde(default, skip_serializing_if = "Vec::is_empty")] server_configs: Vec, + login_token: Option, } #[derive(Debug, Clone)] @@ -154,6 +155,7 @@ impl RawConfig { default_server: local.nickname.clone(), identity_configs: Vec::new(), server_configs: vec![local, testnet], + login_token: None, } } @@ -578,6 +580,10 @@ Fetch the server's fingerprint with: cfg.ecdsa_public_key = None; Ok(()) } + + pub fn set_login_token(&mut self, token: String) { + self.login_token = Some(token); + } } impl Config { @@ -1045,4 +1051,8 @@ Update the server's fingerprint with: self.home.delete_default_server_fingerprint() } } + + pub fn set_login_token(&mut self, token: String) { + self.home.set_login_token(token); + } } diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 52f7a0bb20..9cbde94750 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -31,6 +31,7 @@ pub fn get_subcommands() -> Vec { dns::cli(), generate::cli(), list::cli(), + login::cli(), init::cli(), build::cli(), server::cli(), @@ -61,6 +62,7 @@ pub async fn exec_subcommand(config: Config, cmd: &str, args: &ArgMatches) -> Re "subscribe" => subscribe::exec(config, args).await, #[cfg(feature = "standalone")] "start" => start::exec(args).await, + "login" => login::exec(config, args).await, "upgrade" => upgrade::exec(config, args).await, unknown => Err(anyhow::anyhow!("Invalid subcommand: {}", unknown)), } diff --git a/crates/cli/src/subcommands/login.rs b/crates/cli/src/subcommands/login.rs new file mode 100644 index 0000000000..aa9937d861 --- /dev/null +++ b/crates/cli/src/subcommands/login.rs @@ -0,0 +1,78 @@ +use crate::Config; +use clap::{Arg, ArgMatches, Command}; +use reqwest::Url; +use serde::Deserialize; +use webbrowser; + +pub fn cli() -> Command { + Command::new("login") + .arg( + Arg::new("host") + .long("host") + .default_value("https://spacetimedb.com") + .help("Fetch login token from a different host"), + ) + .about("Login the CLI in to SpacetimeDB") +} + +#[derive(Deserialize)] +struct TokenResponse { + token: String, +} + +#[derive(Deserialize)] +struct LoginTokenResponse { + approved: bool, + session: Option, +} + +#[derive(Deserialize)] +struct LoginTokenResponseSession { + token: String, +} + +pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> { + let remote: &String = args.get_one("host").unwrap(); + // Users like to provide URLs with trailing slashes, which can cause issues due to double-slashes in the routes below. + let remote = remote.trim_end_matches('/'); + + let route = |path| format!("{}{}", remote, path); + + let client = reqwest::Client::new(); + + let response: TokenResponse = client + .get(route("/api/auth/cli/request-login-token")) + .send() + .await? + .json() + .await?; + let temp_token = response.token.as_str(); + + let browser_url = Url::parse_with_params(route("/login/cli").as_str(), vec![("token", temp_token)])?; + if webbrowser::open(browser_url.as_str()).is_err() { + println!("Please open the following URL in your browser: {}", browser_url); + } + + println!("Waiting to hear response from the server..."); + loop { + let response: LoginTokenResponse = client + .get(Url::parse_with_params( + route("/api/auth/cli/status").as_str(), + vec![("token", temp_token)], + )?) + .send() + .await? + .json() + .await?; + if response.approved { + config.set_login_token(response.session.unwrap().token); + println!("Login successful!"); + break; + } + + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + } + // TODO: test that Ctrl-C returns non-zero, rather than falling through to the Ok(()) here + + Ok(()) +} diff --git a/crates/cli/src/subcommands/mod.rs b/crates/cli/src/subcommands/mod.rs index 40aafdee8b..08b7f1fd4c 100644 --- a/crates/cli/src/subcommands/mod.rs +++ b/crates/cli/src/subcommands/mod.rs @@ -8,6 +8,7 @@ pub mod generate; pub mod identity; pub mod init; pub mod list; +pub mod login; pub mod logs; pub mod publish; pub mod repl; diff --git a/crates/client-api/Cargo.toml b/crates/client-api/Cargo.toml index 649feb2489..9b30ad4edb 100644 --- a/crates/client-api/Cargo.toml +++ b/crates/client-api/Cargo.toml @@ -40,4 +40,11 @@ bytestring = "1" tokio-tungstenite.workspace = true itoa = "1.0.9" derive_more = "0.99.17" +uuid.workspace = true +blake3.workspace = true +jsonwebtoken.workspace = true scopeguard.workspace = true + +[dev-dependencies] +jsonwebkey = { version = "0.3.5", features = ["generate","jwt-convert"] } +jsonwebtoken.workspace = true diff --git a/crates/client-api/src/auth.rs b/crates/client-api/src/auth.rs index 7e8c8d8b29..8fe911f7d7 100644 --- a/crates/client-api/src/auth.rs +++ b/crates/client-api/src/auth.rs @@ -1,4 +1,4 @@ -use std::time::Duration; +use std::time::{Duration, SystemTime}; use axum::extract::{Query, Request, State}; use axum::middleware::Next; @@ -6,13 +6,14 @@ use axum::response::IntoResponse; use axum_extra::typed_header::TypedHeader; use headers::{authorization, HeaderMapExt}; use http::{request, HeaderValue, StatusCode}; -use rand::Rng; use serde::Deserialize; +use spacetimedb::auth::identity::SpacetimeIdentityClaims2; use spacetimedb::auth::identity::{ decode_token, encode_token, DecodingKey, EncodingKey, JwtError, JwtErrorKind, SpacetimeIdentityClaims, }; use spacetimedb::energy::EnergyQuanta; use spacetimedb::identity::Identity; +use uuid::Uuid; use crate::{log_and_500, ControlStateDelegate, NodeDelegate}; @@ -33,6 +34,7 @@ pub struct SpacetimeCreds { token: String, } +pub const LOCALHOST: &str = "localhost"; const TOKEN_USERNAME: &str = "token"; impl authorization::Credentials for SpacetimeCreds { const SCHEME: &'static str = authorization::Basic::SCHEME; @@ -58,6 +60,9 @@ impl SpacetimeCreds { pub fn decode_token(&self, public_key: &DecodingKey) -> Result { decode_token(public_key, self.token()).map(|x| x.claims) } + fn from_signed_token(token: String) -> Self { + Self { token } + } /// Mint a new credentials JWT for an identity. pub fn encode_token(private_key: &EncodingKey, identity: Identity) -> Result { let token = encode_token(private_key, identity)?; @@ -94,19 +99,67 @@ pub struct SpacetimeAuth { pub identity: Identity, } +use jsonwebtoken; + +struct TokenClaims { + pub issuer: String, + pub subject: String, + pub audience: Vec, +} +use blake3; + +impl TokenClaims { + // Compute the id from the issuer and subject. + fn id(&self) -> Identity { + let input = format!("{}|{}", self.issuer, self.subject); + let first_hash = blake3::hash(input.as_bytes()); + let id_hash = &first_hash.as_bytes()[..26]; + let mut checksum_input = [0u8; 28]; + // TODO: double check this gets the right number... + checksum_input[2..].copy_from_slice(id_hash); + checksum_input[0] = 0xc2; + checksum_input[1] = 0x00; + let checksum_hash = &blake3::hash(&checksum_input); + + let mut final_bytes = [0u8; 32]; + final_bytes[0] = 0xc2; + final_bytes[1] = 0x00; + final_bytes[2..6].copy_from_slice(&checksum_hash.as_bytes()[..4]); + final_bytes[6..].copy_from_slice(id_hash); + Identity::from_byte_array(final_bytes) + } + + fn encode_and_sign(&self, private_key: &EncodingKey) -> Result { + let claims = SpacetimeIdentityClaims2 { + identity: self.id(), + subject: self.subject.clone(), + issuer: self.issuer.clone(), + audience: self.audience.clone(), + iat: SystemTime::now(), + exp: None, + }; + let header = jsonwebtoken::Header::new(jsonwebtoken::Algorithm::ES256); + jsonwebtoken::encode(&header, &claims, private_key) + } +} + impl SpacetimeAuth { /// Allocate a new identity, and mint a new token for it. pub async fn alloc(ctx: &(impl NodeDelegate + ControlStateDelegate + ?Sized)) -> axum::response::Result { - // TODO: I'm just sticking in a random string until we change how identities are generated. - let identity = { - let mut rng = rand::thread_rng(); - let mut random_bytes = [0u8; 16]; // Example: 16 random bytes - rng.fill(&mut random_bytes); - - let preimg = [b"clockworklabs:", &random_bytes[..]].concat(); - Identity::from_hashing_bytes(preimg) + // Generate claims with a random subject. + let claims = TokenClaims { + issuer: ctx.local_issuer(), + subject: Uuid::new_v4().to_string(), + // Placeholder audience. + audience: vec!["spacetimedb".to_string()], }; - let creds = SpacetimeCreds::encode_token(ctx.private_key(), identity).map_err(log_and_500)?; + + let identity = claims.id(); + let creds = { + let token = claims.encode_and_sign(ctx.private_key()).map_err(log_and_500)?; + SpacetimeCreds::from_signed_token(token) + }; + Ok(Self { creds, identity }) } @@ -120,6 +173,35 @@ impl SpacetimeAuth { } } +#[cfg(test)] +mod tests { + use crate::auth::TokenClaims; + use anyhow::Ok; + // use jsonwebkey as jwk; + use jsonwebkey as jwk; + use spacetimedb::auth::identity; + + // Make sure that when we encode TokenClaims, we can decode to get the expected identity. + #[test] + fn decode_encoded_token() -> Result<(), anyhow::Error> { + let mut my_jwk = jwk::JsonWebKey::new(jwk::Key::generate_p256()); + my_jwk.set_algorithm(jwk::Algorithm::ES256)?; + let encoding_key = my_jwk.key.to_encoding_key(); + + let claims = TokenClaims { + issuer: "localhost".to_string(), + subject: "test-subject".to_string(), + audience: vec!["spacetimedb".to_string()], + }; + let id = claims.id(); + let token = claims.encode_and_sign(&encoding_key)?; + + let decoded = identity::decode_token(&my_jwk.key.to_decoding_key(), &token)?; + assert_eq!(decoded.claims.identity, id); + Ok(()) + } +} + pub struct SpacetimeAuthHeader { auth: Option, } diff --git a/crates/client-api/src/lib.rs b/crates/client-api/src/lib.rs index 826cefcf1a..37527f9a95 100644 --- a/crates/client-api/src/lib.rs +++ b/crates/client-api/src/lib.rs @@ -30,6 +30,9 @@ pub trait NodeDelegate: Send + Sync { /// Return a JWT decoding key for verifying credentials. fn public_key(&self) -> &DecodingKey; + // The issuer to use when signing JWTs. + fn local_issuer(&self) -> String; + /// Return the public key used to verify JWTs, as the bytes of a PEM public key file. /// /// The `/identity/public-key` route calls this method to return the public key to callers. @@ -231,6 +234,10 @@ impl NodeDelegate for Arc { (**self).gather_metrics() } + fn local_issuer(&self) -> String { + (**self).local_issuer() + } + fn host_controller(&self) -> &HostController { (**self).host_controller() } diff --git a/crates/core/src/auth/identity.rs b/crates/core/src/auth/identity.rs index 1d8ecb4eef..287d68a106 100644 --- a/crates/core/src/auth/identity.rs +++ b/crates/core/src/auth/identity.rs @@ -20,6 +20,26 @@ pub struct SpacetimeIdentityClaims { pub exp: Option, } +// The new token format that we are sending out. +#[serde_with::serde_as] +#[derive(Debug, Serialize, Deserialize)] +pub struct SpacetimeIdentityClaims2 { + #[serde(rename = "hex_identity")] + pub identity: Identity, + #[serde(rename = "sub")] + pub subject: String, + #[serde(rename = "iss")] + pub issuer: String, + #[serde(rename = "aud")] + pub audience: Vec, + + /// The unix timestamp the token was issued at + #[serde_as(as = "serde_with::TimestampSeconds")] + pub iat: SystemTime, + #[serde_as(as = "Option")] + pub exp: Option, +} + /// Encode a JWT token using a private_key and an identity. Expiry is set in absolute seconds, /// the function will calculate a proper duration since unix epoch pub fn encode_token(private_key: &EncodingKey, identity: Identity) -> Result { diff --git a/crates/core/src/db/datastore/locking_tx_datastore/datastore.rs b/crates/core/src/db/datastore/locking_tx_datastore/datastore.rs index 267482376b..7c332eff14 100644 --- a/crates/core/src/db/datastore/locking_tx_datastore/datastore.rs +++ b/crates/core/src/db/datastore/locking_tx_datastore/datastore.rs @@ -1391,6 +1391,7 @@ mod tests { ColRow { table: ST_SCHEDULED_ID.into(), pos: 1, name: "table_id", ty: TableId::get_type() }, ColRow { table: ST_SCHEDULED_ID.into(), pos: 2, name: "reducer_name", ty: AlgebraicType::String }, ColRow { table: ST_SCHEDULED_ID.into(), pos: 3, name: "schedule_name", ty: AlgebraicType::String }, + ColRow { table: ST_SCHEDULED_ID.into(), pos: 4, name: "at_column", ty: AlgebraicType::U16 }, ColRow { table: ST_ROW_LEVEL_SECURITY_ID.into(), pos: 0, name: "table_id", ty: TableId::get_type() }, ColRow { table: ST_ROW_LEVEL_SECURITY_ID.into(), pos: 1, name: "sql", ty: AlgebraicType::String }, diff --git a/crates/core/src/db/datastore/locking_tx_datastore/mut_tx.rs b/crates/core/src/db/datastore/locking_tx_datastore/mut_tx.rs index dc9dc5e4ef..22cacebc9d 100644 --- a/crates/core/src/db/datastore/locking_tx_datastore/mut_tx.rs +++ b/crates/core/src/db/datastore/locking_tx_datastore/mut_tx.rs @@ -156,6 +156,7 @@ impl MutTxId { schedule_id: ScheduleId::SENTINEL, schedule_name: schedule.schedule_name, reducer_name: schedule.reducer_name, + at_column: schedule.at_column, }; let (generated, ..) = self.insert(ST_SCHEDULED_ID, &mut row.into(), database_address)?; let id = generated.as_u32(); diff --git a/crates/core/src/db/datastore/system_tables.rs b/crates/core/src/db/datastore/system_tables.rs index 94b2b2a719..173a97eed2 100644 --- a/crates/core/src/db/datastore/system_tables.rs +++ b/crates/core/src/db/datastore/system_tables.rs @@ -266,6 +266,7 @@ st_fields_enum!(enum StScheduledFields { "table_id", TableId = 1, "reducer_name", ReducerName = 2, "schedule_name", ScheduleName = 3, + "at_column", AtColumn = 4, }); /// Helper method to check that a system table has the correct fields. @@ -1285,6 +1286,7 @@ pub struct StScheduledRow { pub(crate) table_id: TableId, pub(crate) reducer_name: Box, pub(crate) schedule_name: Box, + pub(crate) at_column: ColId, } impl TryFrom> for StScheduledRow { @@ -1307,6 +1309,7 @@ impl From for ScheduleSchema { reducer_name: row.reducer_name, schedule_id: row.schedule_id, schedule_name: row.schedule_name, + at_column: row.at_column, } } } diff --git a/crates/core/src/db/relational_db.rs b/crates/core/src/db/relational_db.rs index 58e431f11d..bfb0b33ef2 100644 --- a/crates/core/src/db/relational_db.rs +++ b/crates/core/src/db/relational_db.rs @@ -18,7 +18,7 @@ use crate::error::{DBError, DatabaseError, TableError}; use crate::execution_context::ExecutionContext; use crate::messages::control_db::HostType; use crate::util::spawn_rayon; -use anyhow::anyhow; +use anyhow::{anyhow, Context}; use fs2::FileExt; use futures::channel::mpsc; use futures::StreamExt; @@ -527,14 +527,18 @@ impl RelationalDB { .get_all_tables_tx(&ExecutionContext::internal(self.address), tx) } - pub fn is_scheduled_table( + pub fn table_scheduled_id_and_at( &self, ctx: &ExecutionContext, - tx: &mut MutTx, + tx: &impl StateView, table_id: TableId, - ) -> Result { - tx.schema_for_table(ctx, table_id) - .map(|schema| schema.schedule.is_some()) + ) -> Result, DBError> { + let schema = tx.schema_for_table(ctx, table_id)?; + let Some(sched) = &schema.schedule else { return Ok(None) }; + let primary_key = schema + .primary_key + .context("scheduled table doesn't have a primary key?")?; + Ok(Some((primary_key, sched.at_column))) } pub fn decode_column( diff --git a/crates/core/src/host/instance_env.rs b/crates/core/src/host/instance_env.rs index 38435ad5a6..e01b334c7a 100644 --- a/crates/core/src/host/instance_env.rs +++ b/crates/core/src/host/instance_env.rs @@ -128,14 +128,14 @@ impl InstanceEnv { } })?; - if stdb.is_scheduled_table(ctx, tx, table_id)? { + if let Some((id_column, at_column)) = stdb.table_scheduled_id_and_at(ctx, tx, table_id)? { let row_ref = tx.get(table_id, row_ptr)?.unwrap(); - let (schedule_id, schedule_at) = get_schedule_from_row(tx, stdb, table_id, &row_ref) + let (schedule_id, schedule_at) = get_schedule_from_row(&row_ref, id_column, at_column) // NOTE(centril): Should never happen, // as we successfully inserted and thus `ret` is verified against the table schema. .map_err(|e| NodesError::ScheduleError(ScheduleError::DecodingError(e)))?; self.scheduler - .schedule(table_id, schedule_id, schedule_at) + .schedule(table_id, schedule_id, schedule_at, id_column, at_column) .map_err(NodesError::ScheduleError)?; } diff --git a/crates/core/src/host/scheduler.rs b/crates/core/src/host/scheduler.rs index 41f9f0b7d6..57cf8ed5a4 100644 --- a/crates/core/src/host/scheduler.rs +++ b/crates/core/src/host/scheduler.rs @@ -8,15 +8,13 @@ use spacetimedb_client_api_messages::energy::EnergyQuanta; use spacetimedb_client_api_messages::timestamp::Timestamp; use spacetimedb_lib::scheduler::ScheduleAt; use spacetimedb_lib::Address; -use spacetimedb_primitives::TableId; +use spacetimedb_primitives::{ColId, TableId}; use spacetimedb_sats::{bsatn::ToBsatn as _, AlgebraicValue}; -use spacetimedb_schema::schema::TableSchema; use spacetimedb_table::table::RowRef; use tokio::sync::mpsc; use tokio_util::time::delay_queue::Expired; use tokio_util::time::{delay_queue, DelayQueue}; -use crate::db::datastore::locking_tx_datastore::tx::TxId; use crate::db::datastore::locking_tx_datastore::MutTxId; use crate::db::datastore::system_tables::{StFields, StScheduledFields, ST_SCHEDULED_ID}; use crate::db::datastore::traits::IsolationLevel; @@ -36,8 +34,17 @@ pub struct ScheduledReducerId { table_id: TableId, /// The particular schedule row in the reducer scheduling table referred to by `self.table_id`. schedule_id: u64, + // These may seem redundant, but they're actually free - they fit in the struct padding. + // `scheduled_id: u64, table_id: u32, id_column: u16, at_column: u16` == 16 bytes, same as + // (`scheduled_id: u64, table_id: u32` == 12 bytes).pad_to_align() == 16 bytes + /// The column that the primary key (`scheduled_id`) is in. + id_column: ColId, + /// The column that the `ScheduleAt` value is in. + at_column: ColId, } +spacetimedb_table::static_assert_size!(ScheduledReducerId, 16); + enum MsgOrExit { Msg(T), Exit, @@ -75,9 +82,6 @@ impl Scheduler { } } -const SCHEDULED_AT_FIELD: [&str; 2] = ["scheduled_at", "ScheduledAt"]; -const SCHEDULED_ID_FIELD: [&str; 2] = ["scheduled_id", "ScheduledId"]; - impl SchedulerStarter { // TODO(cloutiertyler): This whole start dance is scuffed, but I don't have // time to make it better right now. @@ -98,14 +102,25 @@ impl SchedulerStarter { // Find all Scheduled tables for st_scheduled_row in self.db.iter(ctx, &tx, ST_SCHEDULED_ID)? { let table_id = st_scheduled_row.read_col(StScheduledFields::TableId)?; + let (id_column, at_column) = self + .db + .table_scheduled_id_and_at(ctx, &tx, table_id)? + .ok_or_else(|| anyhow!("scheduled table {table_id} doesn't have valid columns"))?; // Insert each entry (row) in the scheduled table into `queue`. for scheduled_row in self.db.iter(ctx, &tx, table_id)? { - let schedule_id = get_schedule_id(&tx, &self.db, table_id, &scheduled_row)?; - let schedule_at = get_schedule_at(&tx, &self.db, table_id, &scheduled_row)?; + let (schedule_id, schedule_at) = get_schedule_from_row(&scheduled_row, id_column, at_column)?; // calculate duration left to call the scheduled reducer let duration = schedule_at.to_duration_from_now(); - queue.insert(QueueItem::Id(ScheduledReducerId { table_id, schedule_id }), duration); + queue.insert( + QueueItem::Id(ScheduledReducerId { + table_id, + schedule_id, + id_column, + at_column, + }), + duration, + ); } } @@ -163,7 +178,14 @@ pub enum ScheduleError { } impl Scheduler { - pub fn schedule(&self, table_id: TableId, schedule_id: u64, schedule_at: ScheduleAt) -> Result<(), ScheduleError> { + pub(super) fn schedule( + &self, + table_id: TableId, + schedule_id: u64, + schedule_at: ScheduleAt, + id_column: ColId, + at_column: ColId, + ) -> Result<(), ScheduleError> { // Check that `at` is within `tokio_utils::time::DelayQueue`'s accepted time-range. // // `DelayQueue` uses a sliding window, @@ -194,7 +216,12 @@ impl Scheduler { // if the actor has exited, it's fine to ignore; it means that the host actor calling // schedule will exit soon as well, and it'll be scheduled to run when the module host restarts let _ = self.tx.send(MsgOrExit::Msg(SchedulerMessage::Schedule { - id: ScheduledReducerId { table_id, schedule_id }, + id: ScheduledReducerId { + table_id, + schedule_id, + id_column, + at_column, + }, at: schedule_at, })); @@ -311,8 +338,7 @@ impl SchedulerActor { return Ok(None); }; - let ScheduledReducer { reducer, bsatn_args } = - proccess_schedule(&ctx, tx, &db, id.table_id, &schedule_row)?; + let ScheduledReducer { reducer, bsatn_args } = process_schedule(&ctx, tx, &db, id.table_id, &schedule_row)?; let (reducer_seed, reducer_id) = module_info .reducer_seed_and_id(&reducer[..]) @@ -361,12 +387,10 @@ impl SchedulerActor { /// return true if it is repeated schedule fn handle_repeated_schedule( &mut self, - tx: &MutTxId, - db: &RelationalDB, id: ScheduledReducerId, schedule_row: &RowRef<'_>, ) -> Result { - let schedule_at = get_schedule_at_mut(tx, db, id.table_id, schedule_row)?; + let schedule_at = read_schedule_at(schedule_row, id.at_column)?; if let ScheduleAt::Interval(dur) = schedule_at { let key = self.queue.insert(QueueItem::Id(id), Duration::from_micros(dur)); @@ -388,7 +412,7 @@ impl SchedulerActor { match get_schedule_row_mut(ctx, &tx, db, id) { Ok(schedule_row) => { - if let Ok(is_repeated) = self.handle_repeated_schedule(&tx, db, id, &schedule_row) { + if let Ok(is_repeated) = self.handle_repeated_schedule(id, &schedule_row) { if is_repeated { return; // Do not delete entry for repeated reducer } @@ -436,7 +460,7 @@ fn commit_and_broadcast_deletion_event(ctx: &ExecutionContext, tx: MutTxId, modu } /// Generate `ScheduledReducer` for given `ScheduledReducerId` -fn proccess_schedule( +fn process_schedule( ctx: &ExecutionContext, tx: &MutTxId, db: &RelationalDB, @@ -463,24 +487,6 @@ fn proccess_schedule( }) } -/// Helper to get schedule_id from schedule_row with `TxId` -fn get_schedule_id(tx: &TxId, db: &RelationalDB, table_id: TableId, schedule_row: &RowRef<'_>) -> anyhow::Result { - let schema = db.schema_for_table(tx, table_id)?; - let schedule_id_pos = schema - .get_column_id_by_name(SCHEDULED_ID_FIELD[0]) - .or_else(|| schema.get_column_id_by_name(SCHEDULED_ID_FIELD[1])) - .ok_or_else(|| anyhow!("Column '{}' not found in table {}", SCHEDULED_ID_FIELD[0], table_id))?; - - schedule_row.read_col::(schedule_id_pos).map_err(|e| { - anyhow!( - "Error reading column '{}' from schedule table id:{}, row: {} ", - SCHEDULED_ID_FIELD[0], - table_id, - e - ) - }) -} - /// Helper to get schedule_row with `MutTxId` fn get_schedule_row_mut<'a>( ctx: &'a ExecutionContext, @@ -488,84 +494,24 @@ fn get_schedule_row_mut<'a>( db: &'a RelationalDB, id: ScheduledReducerId, ) -> anyhow::Result> { - let ScheduledReducerId { schedule_id, table_id } = id; - let schema = db.schema_for_table_mut(tx, table_id)?; - let scheduled_id_pos = schema - .get_column_id_by_name(SCHEDULED_ID_FIELD[0]) - .or_else(|| schema.get_column_id_by_name(SCHEDULED_ID_FIELD[1])) - .ok_or_else(|| anyhow!("Column '{}' not found in table {}", SCHEDULED_ID_FIELD[0], table_id))?; - - db.iter_by_col_eq_mut(ctx, tx, table_id, scheduled_id_pos, &schedule_id.into())? + db.iter_by_col_eq_mut(ctx, tx, id.table_id, id.id_column, &id.schedule_id.into())? .next() - .ok_or_else(|| anyhow!("Schedule with ID {} not found in table {}", schedule_id, table_id)) + .ok_or_else(|| anyhow!("Schedule with ID {} not found in table {}", id.schedule_id, id.table_id)) } /// Helper to get schedule_id and schedule_at from schedule_row product value pub fn get_schedule_from_row( - tx: &MutTxId, - db: &RelationalDB, - table_id: TableId, row: &RowRef<'_>, + id_column: ColId, + at_column: ColId, ) -> anyhow::Result<(u64, ScheduleAt)> { - let row_ty = db.row_schema_for_table(tx, table_id)?; - - let col_pos = |field_name: &str| -> anyhow::Result { - row_ty - .index_of_field_name(field_name) - .ok_or_else(|| anyhow!("Column '{}' not found in row schema for table {}", field_name, table_id)) - }; - - let schedule_id_col_pos = col_pos(SCHEDULED_ID_FIELD[0]).or_else(|_| col_pos(SCHEDULED_ID_FIELD[1]))?; - let schedule_at_col_pos = col_pos(SCHEDULED_AT_FIELD[0]).or_else(|_| col_pos(SCHEDULED_AT_FIELD[1]))?; - - let schedule_id: u64 = row.read_col(schedule_id_col_pos)?; - let schedule_at_av: AlgebraicValue = row.read_col(schedule_at_col_pos)?; - let schedule_at = ScheduleAt::try_from(schedule_at_av.clone()).map_err(|e| { - anyhow!( - "Failed to convert field '{}' to ScheduleAt: {:?}", - SCHEDULED_AT_FIELD[0], - e - ) - })?; + let schedule_id: u64 = row.read_col(id_column)?; + let schedule_at = read_schedule_at(row, at_column)?; Ok((schedule_id, schedule_at)) } -/// Helper to get schedule_at from schedule_row with `TxId` -fn get_schedule_at( - tx: &TxId, - db: &RelationalDB, - table_id: TableId, - schedule_row: &RowRef<'_>, -) -> anyhow::Result { - let schema = db.schema_for_table(tx, table_id)?; - get_schedule_at_from_schema(table_id, schema, schedule_row) -} - -/// Helper to get schedule_at from schedule_row with `MutTxId` -fn get_schedule_at_mut( - tx: &MutTxId, - db: &RelationalDB, - table_id: TableId, - schedule_row: &RowRef<'_>, -) -> anyhow::Result { - let schema = db.schema_for_table_mut(tx, table_id)?; - get_schedule_at_from_schema(table_id, schema, schedule_row) -} - -/// Helper to get schedule_at from schedule_row -fn get_schedule_at_from_schema( - table_id: TableId, - table_schema: Arc, - schedule_row: &RowRef<'_>, -) -> anyhow::Result { - let schedule_at_pos = table_schema - .get_column_id_by_name(SCHEDULED_AT_FIELD[0]) - .or_else(|| table_schema.get_column_id_by_name(SCHEDULED_AT_FIELD[1])) - .ok_or_else(|| anyhow!("Column '{}' not found in table {}", SCHEDULED_AT_FIELD[0], table_id))?; - - schedule_row - .read_col::(schedule_at_pos)? - .try_into() - .map_err(|_| anyhow!("Failed to convert column '{}' to ScheduleAt", SCHEDULED_AT_FIELD[0],)) +fn read_schedule_at(row: &RowRef<'_>, at_column: ColId) -> anyhow::Result { + let schedule_at_av: AlgebraicValue = row.read_col(at_column)?; + ScheduleAt::try_from(schedule_at_av).map_err(|e| anyhow!("Failed to convert 'scheduled_at' to ScheduleAt: {e:?}")) } diff --git a/crates/lib/src/db/raw_def/v9.rs b/crates/lib/src/db/raw_def/v9.rs index 1be6f034d5..7aed1e8325 100644 --- a/crates/lib/src/db/raw_def/v9.rs +++ b/crates/lib/src/db/raw_def/v9.rs @@ -122,7 +122,9 @@ pub struct RawTableDefV9 { /// Eventually, we may remove the requirement for an index. /// /// The database engine does not actually care about this, but client code generation does. - pub primary_key: Option, + /// + /// A list of length 0 means no primary key. Currently, a list of length >1 is not supported. + pub primary_key: ColList, /// The indices of the table. pub indexes: Vec, @@ -303,6 +305,9 @@ pub struct RawScheduleDefV9 { /// The name of the reducer to call. pub reducer_name: RawIdentifier, + + /// The column of the `scheduled_at` field of this scheduled table. + pub scheduled_at_column: ColId, } /// A constraint definition attached to a table. @@ -471,7 +476,7 @@ impl RawModuleDefV9Builder { constraints: vec![], sequences: vec![], schedule: None, - primary_key: None, + primary_key: ColList::empty(), table_type: TableType::User, table_access: TableAccess::Public, }, @@ -662,7 +667,7 @@ impl<'a> RawTableDefBuilder<'a> { /// Adds a primary key to the table. /// You must also add a unique constraint on the primary key column. pub fn with_primary_key(mut self, column: impl Into) -> Self { - self.table.primary_key = Some(column.into()); + self.table.primary_key = ColList::new(column.into()); self } @@ -712,10 +717,20 @@ impl<'a> RawTableDefBuilder<'a> { /// Adds a schedule definition to the table. /// /// The table must have the appropriate columns for a scheduled table. - pub fn with_schedule(mut self, reducer_name: impl Into, name: Option) -> Self { + pub fn with_schedule( + mut self, + reducer_name: impl Into, + scheduled_at_column: impl Into, + name: Option, + ) -> Self { let reducer_name = reducer_name.into(); let name = name.unwrap_or_else(|| self.generate_schedule_name()); - self.table.schedule = Some(RawScheduleDefV9 { name, reducer_name }); + let scheduled_at_column = scheduled_at_column.into(); + self.table.schedule = Some(RawScheduleDefV9 { + name, + reducer_name, + scheduled_at_column, + }); self } diff --git a/crates/schema/src/auto_migrate.rs b/crates/schema/src/auto_migrate.rs index e3fec7026d..8de6e6493b 100644 --- a/crates/schema/src/auto_migrate.rs +++ b/crates/schema/src/auto_migrate.rs @@ -493,7 +493,8 @@ mod tests { ]), true, ) - .with_schedule("check_deliveries", None) + .with_auto_inc_primary_key(0) + .with_schedule("check_deliveries", 1, None) .finish(); old_builder.add_reducer( "check_deliveries", @@ -510,6 +511,7 @@ mod tests { ]), true, ) + .with_auto_inc_primary_key(0) .finish(); old_builder.add_row_level_security("SELECT * FROM Apples"); @@ -577,6 +579,7 @@ mod tests { ]), true, ) + .with_auto_inc_primary_key(0) // remove schedule def .finish(); @@ -595,8 +598,9 @@ mod tests { ]), true, ) + .with_auto_inc_primary_key(0) // add schedule def - .with_schedule("perform_inspection", None) + .with_schedule("perform_inspection", 1, None) .finish(); // add reducer. diff --git a/crates/schema/src/def.rs b/crates/schema/src/def.rs index d68760c948..1a31284c53 100644 --- a/crates/schema/src/def.rs +++ b/crates/schema/src/def.rs @@ -467,7 +467,7 @@ impl From for RawTableDefV9 { RawTableDefV9 { name: name.into(), product_type_ref, - primary_key, + primary_key: ColList::from_iter(primary_key), indexes: to_raw(indexes, |index: &RawIndexDefV9| &index.name), constraints: to_raw(constraints, |constraint: &RawConstraintDefV9| &constraint.name), sequences: to_raw(sequences, |sequence: &RawSequenceDefV9| &sequence.name), @@ -740,6 +740,7 @@ impl From for RawScheduleDefV9 { RawScheduleDefV9 { name: val.name.into(), reducer_name: val.reducer_name.into(), + scheduled_at_column: val.at_column, } } } diff --git a/crates/schema/src/def/validate/v8.rs b/crates/schema/src/def/validate/v8.rs index 29e6a8fb9a..9355e1c554 100644 --- a/crates/schema/src/def/validate/v8.rs +++ b/crates/schema/src/def/validate/v8.rs @@ -11,7 +11,7 @@ use spacetimedb_lib::{ TableDesc as RawTableDescV8, TypeAlias as RawTypeAliasV8, }; -use spacetimedb_primitives::ColId; +use spacetimedb_primitives::{ColId, ColList}; use spacetimedb_sats::{AlgebraicTypeRef, Typespace, WithTypespace}; use crate::def::{validate::Result, ModuleDef}; @@ -89,12 +89,16 @@ fn upgrade_table( } = table; // Check all column defs, then discard them. + let scheduled_at_col = columns + .iter() + .position(|x| &*x.col_name == "scheduled_at") + .map(|i| i as u16); check_all_column_defs(product_type_ref, columns, &table_name, typespace, extra_errors); // Now we're ready to go through the various definitions and upgrade them. let indexes = convert_all(indexes, upgrade_index); let sequences = convert_all(sequences.into_iter().chain(generated_sequences), upgrade_sequence); - let schedule = upgrade_schedule(scheduled, &table_name); + let schedule = upgrade_schedule(scheduled, &table_name, scheduled_at_col); // Constraints are pretty hairy, which is why we're getting rid of v8. let mut primary_key = None; @@ -110,7 +114,7 @@ fn upgrade_table( RawTableDefV9 { name: table_name, product_type_ref, - primary_key, + primary_key: ColList::from_iter(primary_key), indexes, constraints: unique_constraints, sequences, @@ -269,10 +273,16 @@ fn upgrade_constraint( } } -fn upgrade_schedule(schedule: Option, table_name: &RawIdentifier) -> Option { +fn upgrade_schedule( + schedule: Option, + table_name: &RawIdentifier, + scheduled_at_col: Option, +) -> Option { + let scheduled_at_col = scheduled_at_col?; schedule.map(|reducer_name| RawScheduleDefV9 { name: format!("{table_name}_schedule").into(), reducer_name, + scheduled_at_column: scheduled_at_col.into(), }) } @@ -396,6 +406,7 @@ mod tests { ("scheduled_id", AlgebraicType::U64), ]), ) + .with_column_constraint(Constraints::primary_key_auto(), 2) .with_scheduled(Some("check_deliveries".into())), ); @@ -464,7 +475,7 @@ mod tests { &delivery_def.schedule.as_ref().unwrap().reducer_name[..], "check_deliveries" ); - assert_eq!(delivery_def.primary_key, None); + assert_eq!(delivery_def.primary_key, Some(ColId(2))); assert_eq!(def.typespace.get(product_type_ref), Some(&product_type)); assert_eq!(def.typespace.get(sum_type_ref), Some(&sum_type)); diff --git a/crates/schema/src/def/validate/v9.rs b/crates/schema/src/def/validate/v9.rs index cd9d90ac87..15d1b5bb8c 100644 --- a/crates/schema/src/def/validate/v9.rs +++ b/crates/schema/src/def/validate/v9.rs @@ -186,6 +186,7 @@ impl ModuleValidator<'_> { .collect_all_errors(); // We can't validate the primary key without validating the unique constraints first. + let primary_key_head = primary_key.head(); let constraints_primary_key = constraints .into_iter() .map(|constraint| { @@ -208,7 +209,7 @@ impl ModuleValidator<'_> { .collect_all_errors(); let schedule = schedule - .map(|schedule| table_in_progress.validate_schedule_def(schedule)) + .map(|schedule| table_in_progress.validate_schedule_def(schedule, primary_key_head)) .transpose(); let name = table_in_progress @@ -455,9 +456,16 @@ impl TableValidator<'_, '_> { fn validate_primary_key( &mut self, validated_constraints: IdentifierMap, - primary_key: Option, + primary_key: ColList, ) -> Result<(IdentifierMap, Option)> { + if primary_key.len() > 1 { + return Err(ValidationError::RepeatedPrimaryKey { + table: self.raw_name.clone(), + } + .into()); + } let pk = primary_key + .head() .map(|pk| -> Result { let pk = self.validate_col_id(&self.raw_name, pk)?; let pk_col_list = ColSet::from(pk); @@ -590,18 +598,25 @@ impl TableValidator<'_, '_> { } /// Validate a schedule definition. - fn validate_schedule_def(&mut self, schedule: RawScheduleDefV9) -> Result { - let RawScheduleDefV9 { name, reducer_name } = schedule; + fn validate_schedule_def(&mut self, schedule: RawScheduleDefV9, primary_key: Option) -> Result { + let RawScheduleDefV9 { + name, + reducer_name, + scheduled_at_column, + } = schedule; // Find the appropriate columns. let at_column = self .product_type .elements - .iter() - .enumerate() - .find(|(_, element)| element.name() == Some("scheduled_at")); - let id_column = self.product_type.elements.iter().enumerate().find(|(_, element)| { - element.name() == Some("scheduled_id") && element.algebraic_type == AlgebraicType::U64 + .get(scheduled_at_column.idx()) + .is_some_and(|ty| ty.algebraic_type.is_schedule_at()) + .then_some(scheduled_at_column); + let id_column = primary_key.filter(|pk| { + self.product_type + .elements + .get(pk.idx()) + .is_some_and(|ty| ty.algebraic_type == AlgebraicType::U64) }); // Error if either column is missing. @@ -618,9 +633,6 @@ impl TableValidator<'_, '_> { let (name, (at_column, id_column), reducer_name) = (name, at_id, reducer_name).combine_errors()?; - let at_column = at_column.0.into(); - let id_column = id_column.0.into(); - Ok(ScheduleDef { name, at_column, @@ -845,7 +857,8 @@ mod tests { ]), true, ) - .with_schedule("check_deliveries", Some("check_deliveries_schedule".into())) + .with_auto_inc_primary_key(2) + .with_schedule("check_deliveries", 1, Some("check_deliveries_schedule".into())) .with_type(TableType::System) .finish(); @@ -969,7 +982,7 @@ mod tests { &delivery_def.schedule.as_ref().unwrap().reducer_name[..], "check_deliveries" ); - assert_eq!(delivery_def.primary_key, None); + assert_eq!(delivery_def.primary_key, Some(ColId(2))); assert_eq!(def.typespace.get(product_type_ref), Some(&product_type)); assert_eq!(def.typespace.get(sum_type_ref), Some(&sum_type)); @@ -1386,7 +1399,8 @@ mod tests { ]), true, ) - .with_schedule("check_deliveries", Some("check_deliveries_schedule".into())) + .with_auto_inc_primary_key(2) + .with_schedule("check_deliveries", 1, Some("check_deliveries_schedule".into())) .with_type(TableType::System) .finish(); let result: Result = builder.finish().try_into(); @@ -1411,7 +1425,8 @@ mod tests { ]), true, ) - .with_schedule("check_deliveries", Some("check_deliveries_schedule".into())) + .with_auto_inc_primary_key(2) + .with_schedule("check_deliveries", 1, Some("check_deliveries_schedule".into())) .with_type(TableType::System) .finish(); builder.add_reducer("check_deliveries", ProductType::from([("a", AlgebraicType::U64)]), None); diff --git a/crates/schema/src/schema.rs b/crates/schema/src/schema.rs index 816540add7..3ca1b5b09a 100644 --- a/crates/schema/src/schema.rs +++ b/crates/schema/src/schema.rs @@ -874,8 +874,11 @@ pub struct ScheduleSchema { /// The name of the schedule. pub schedule_name: Box, - // The name of the reducer to call. + /// The name of the reducer to call. pub reducer_name: Box, + + /// The column containing the `ScheduleAt` enum. + pub at_column: ColId, } impl Schema for ScheduleSchema { @@ -893,6 +896,7 @@ impl Schema for ScheduleSchema { schedule_id: id, schedule_name: (*def.name).into(), reducer_name: (*def.reducer_name).into(), + at_column: def.at_column, // Ignore def.at_column and id_column. Those are recovered at runtime. } } diff --git a/crates/standalone/src/lib.rs b/crates/standalone/src/lib.rs index b45fcdcbf4..26876ec552 100644 --- a/crates/standalone/src/lib.rs +++ b/crates/standalone/src/lib.rs @@ -24,6 +24,7 @@ use spacetimedb::identity::Identity; use spacetimedb::messages::control_db::{Database, Node, Replica}; use spacetimedb::stdb_path; use spacetimedb::worker_metrics::WORKER_METRICS; +use spacetimedb_client_api::auth::LOCALHOST; use spacetimedb_client_api_messages::name::{DomainName, InsertDomainResult, RegisterTldResult, Tld}; use std::fs::File; use std::io::Write; @@ -164,6 +165,10 @@ impl spacetimedb_client_api::NodeDelegate for StandaloneEnv { &self.public_key } + fn local_issuer(&self) -> String { + LOCALHOST.to_owned() + } + fn public_key_bytes(&self) -> &[u8] { &self.public_key_bytes }