From f2b84ce297e89bb7da3140384a235f7bec812369 Mon Sep 17 00:00:00 2001 From: Mike Rolish Date: Wed, 23 Apr 2025 13:25:05 -0500 Subject: [PATCH 1/3] feat: published signed Lazer transactions --- Cargo.lock | 359 ++++++++++----------------- Cargo.toml | 4 +- src/agent/services/lazer_exporter.rs | 137 ++++++---- 3 files changed, 227 insertions(+), 273 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f6dedc9..2c0c9da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,7 +69,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "once_cell", "version_check", ] @@ -81,7 +81,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "once_cell", "version_check", "zerocopy 0.7.35", @@ -396,9 +396,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a194f9d963d8099596278594b3107448656ba73831c9d8c783e613ce86da64" +checksum = "b37fc50485c4f3f736a4fb14199f6d5f5ba008d7f28fe710306c92780f004c07" dependencies = [ "brotli", "flate2", @@ -631,23 +631,11 @@ dependencies = [ "typenum", ] -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - [[package]] name = "blake3" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389a099b34312839e16420d499a9cad9650541715937ffbdd40d36f49e77eeb3" +checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" dependencies = [ "arrayref", "arrayvec", @@ -797,9 +785,9 @@ dependencies = [ [[package]] name = "brotli" -version = "7.0.0" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +checksum = "cf19e729cdbd51af9a397fb9ef8ac8378007b797f8273cfbfdf45dcaa316167b" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -808,9 +796,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "4.0.2" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -847,28 +835,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bytecheck" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" -dependencies = [ - "bytecheck_derive", - "ptr_meta", - "simdutf8", -] - -[[package]] -name = "bytecheck_derive" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "bytemuck" version = "1.22.0" @@ -1013,9 +979,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.36" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04" +checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" dependencies = [ "clap_builder", "clap_derive", @@ -1023,9 +989,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.36" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5" +checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" dependencies = [ "anstream", "anstyle", @@ -1161,7 +1127,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "once_cell", "tiny-keccak", ] @@ -1429,26 +1395,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "derive_more" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" -dependencies = [ - "derive_more-impl", -] - -[[package]] -name = "derive_more-impl" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "dialoguer" version = "0.10.4" @@ -1726,12 +1672,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - [[package]] name = "futures" version = "0.3.31" @@ -1857,9 +1797,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "js-sys", @@ -1964,9 +1904,6 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] [[package]] name = "hashbrown" @@ -2092,6 +2029,15 @@ dependencies = [ "hmac 0.8.1", ] +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "http" version = "0.2.12" @@ -2536,15 +2482,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.15" @@ -2690,6 +2627,12 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -3601,23 +3544,54 @@ dependencies = [ ] [[package]] -name = "ptr_meta" -version = "0.1.4" +name = "protobuf" +version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4" dependencies = [ - "ptr_meta_derive", + "once_cell", + "protobuf-support", + "thiserror 1.0.69", ] [[package]] -name = "ptr_meta_derive" -version = "0.1.4" +name = "protobuf-codegen" +version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +checksum = "5d3976825c0014bbd2f3b34f0001876604fe87e0c86cd8fa54251530f1544ace" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "anyhow", + "once_cell", + "protobuf", + "protobuf-parse", + "regex", + "tempfile", + "thiserror 1.0.69", +] + +[[package]] +name = "protobuf-parse" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4aeaa1f2460f1d348eeaeed86aea999ce98c1bded6f089ff8514c9d9dbdc973" +dependencies = [ + "anyhow", + "indexmap 2.9.0", + "log", + "protobuf", + "protobuf-support", + "tempfile", + "thiserror 1.0.69", + "which", +] + +[[package]] +name = "protobuf-support" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6" +dependencies = [ + "thiserror 1.0.69", ] [[package]] @@ -3630,8 +3604,9 @@ dependencies = [ "bytemuck", "chrono", "chrono-tz", - "clap 4.5.36", + "clap 4.5.37", "config", + "ed25519-dalek", "futures", "futures-util", "http 1.3.1", @@ -3647,7 +3622,8 @@ dependencies = [ "portpicker", "prometheus-client", "proptest", - "pyth-lazer-protocol", + "protobuf", + "pyth-lazer-publisher-sdk", "pyth-price-store", "pyth-sdk", "pyth-sdk-solana", @@ -3678,19 +3654,13 @@ dependencies = [ ] [[package]] -name = "pyth-lazer-protocol" -version = "0.7.0" +name = "pyth-lazer-publisher-sdk" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "584ecb22ba83ffa922cd635184515607df135dfe454cbf18ce2cba8c09ec476a" +checksum = "13f8de2f7e8ba23b57167b7b150295ac806263aad39d7258d2c3684320ffce74" dependencies = [ - "anyhow", - "base64 0.22.1", - "byteorder", - "derive_more", - "itertools 0.13.0", - "rust_decimal", - "serde", - "serde_json", + "protobuf", + "protobuf-codegen", ] [[package]] @@ -3711,7 +3681,7 @@ checksum = "1e7aeef4d5f0a9c98ff5af2ddd84a8b89919c512188305b497a9eb9afa97a949" dependencies = [ "borsh 0.10.4", "borsh-derive 0.10.4", - "getrandom 0.2.15", + "getrandom 0.2.16", "hex", "schemars", "serde", @@ -3823,12 +3793,6 @@ version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - [[package]] name = "rand" version = "0.7.3" @@ -3855,13 +3819,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", - "zerocopy 0.8.24", ] [[package]] @@ -3909,7 +3872,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", ] [[package]] @@ -4033,15 +3996,6 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" -[[package]] -name = "rend" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" -dependencies = [ - "bytecheck", -] - [[package]] name = "reqwest" version = "0.11.27" @@ -4150,41 +4104,12 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", "untrusted 0.9.0", "windows-sys 0.52.0", ] -[[package]] -name = "rkyv" -version = "0.7.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" -dependencies = [ - "bitvec", - "bytecheck", - "bytes", - "hashbrown 0.12.3", - "ptr_meta", - "rend", - "rkyv_derive", - "seahash", - "tinyvec", - "uuid", -] - -[[package]] -name = "rkyv_derive" -version = "0.7.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "ron" version = "0.8.1" @@ -4199,23 +4124,23 @@ dependencies = [ [[package]] name = "rpassword" -version = "7.3.1" +version = "7.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" +checksum = "66d4c8b64f049c6721ec8ccec37ddfc3d641c4a7fca57e8f2a89de509c73df39" dependencies = [ "libc", "rtoolbox", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] name = "rtoolbox" -version = "0.0.2" +version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" +checksum = "a7cc970b249fbe527d6e02e0a227762c9108b2f49d81094fe357ffc6d14d7f6f" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -4228,22 +4153,6 @@ dependencies = [ "ordered-multimap", ] -[[package]] -name = "rust_decimal" -version = "1.37.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faa7de2ba56ac291bd90c6b9bece784a52ae1411f9506544b3eae36dd2356d50" -dependencies = [ - "arrayvec", - "borsh 1.5.7", - "bytes", - "num-traits", - "rand 0.8.5", - "rkyv", - "serde", - "serde_json", -] - [[package]] name = "rustc-demangle" version = "0.1.24" @@ -4274,6 +4183,19 @@ dependencies = [ "nom", ] +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + [[package]] name = "rustix" version = "1.0.5" @@ -4283,7 +4205,7 @@ dependencies = [ "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.9.4", "windows-sys 0.59.0", ] @@ -4444,12 +4366,6 @@ dependencies = [ "untrusted 0.9.0", ] -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - [[package]] name = "security-framework" version = "2.11.1" @@ -4663,9 +4579,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] @@ -4676,12 +4592,6 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" -[[package]] -name = "simdutf8" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" - [[package]] name = "siphasher" version = "0.3.11" @@ -5144,7 +5054,7 @@ checksum = "9ce496a475e5062ba5de97215ab39d9c358f9c9df4bb7f3a45a1f1a8bd9065ed" dependencies = [ "bincode 1.3.3", "borsh 1.5.7", - "getrandom 0.2.15", + "getrandom 0.2.16", "js-sys", "num-traits", "serde", @@ -5403,7 +5313,7 @@ dependencies = [ "console_error_panic_hook", "console_log", "curve25519-dalek 3.2.1", - "getrandom 0.2.15", + "getrandom 0.2.16", "itertools 0.10.5", "js-sys", "lazy_static", @@ -5448,7 +5358,7 @@ dependencies = [ "bytemuck", "console_error_panic_hook", "console_log", - "getrandom 0.2.15", + "getrandom 0.2.16", "lazy_static", "log", "memoffset 0.9.1", @@ -5608,7 +5518,7 @@ dependencies = [ "bytemuck_derive", "curve25519-dalek 4.1.3", "five8_const", - "getrandom 0.2.15", + "getrandom 0.2.16", "js-sys", "num-traits", "serde", @@ -6617,12 +6527,6 @@ dependencies = [ "libc", ] -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - [[package]] name = "tempfile" version = "3.19.1" @@ -6632,7 +6536,7 @@ dependencies = [ "fastrand", "getrandom 0.3.2", "once_cell", - "rustix", + "rustix 1.0.5", "windows-sys 0.59.0", ] @@ -6918,16 +6822,16 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.14" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", "futures-io", "futures-sink", "futures-util", - "hashbrown 0.14.5", + "hashbrown 0.15.2", "pin-project-lite", "slab", "tokio", @@ -7197,7 +7101,7 @@ dependencies = [ "httparse", "log", "native-tls", - "rand 0.9.0", + "rand 0.9.1", "sha1", "thiserror 2.0.12", "url", @@ -7350,12 +7254,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "uuid" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" - [[package]] name = "valuable" version = "0.1.1" @@ -7566,6 +7464,18 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + [[package]] name = "winapi" version = "0.3.9" @@ -7872,15 +7782,6 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - [[package]] name = "x509-parser" version = "0.14.0" diff --git a/Cargo.toml b/Cargo.toml index 9fb38d8..09a130d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ path = "src/bin/agent.rs" [dependencies] anyhow = "1.0.81" +ed25519-dalek = "1.0.1" serde = { version = "1.0.197", features = ["derive"] } async-trait = "0.1.79" warp = { version = "0.3.6", features = ["websocket"] } @@ -50,12 +51,13 @@ tracing-opentelemetry = "0.24.0" opentelemetry = "0.23.0" opentelemetry_sdk = { version = "0.23.0", features = ["rt-tokio"]} opentelemetry-otlp = { version = "0.16.0" } +protobuf = "3.7.2" pyth-price-store = "0.1.0" bytemuck = "1.13.0" tokio-tungstenite = { version = "0.26.2", features = ["native-tls", "url"] } http = "1.3.1" url = { version = "2.5.4", features = ["serde"] } -pyth-lazer-protocol = "0.7.0" +pyth-lazer-publisher-sdk = "0.1.1" tokio-util = { version = "0.7.14", features = ["full"] } [dev-dependencies] diff --git a/src/agent/services/lazer_exporter.rs b/src/agent/services/lazer_exporter.rs index dcef967..359d9fd 100644 --- a/src/agent/services/lazer_exporter.rs +++ b/src/agent/services/lazer_exporter.rs @@ -13,7 +13,8 @@ use { SinkExt, }, http::HeaderValue, - pyth_lazer_protocol::publisher::PriceFeedDataV1, + protobuf::Message as ProtobufMessage, + pyth_lazer_publisher_sdk::transaction::SignedLazerTransaction, reqwest::Client, serde::Deserialize, std::{ @@ -28,15 +29,11 @@ use { connect_async_with_config, tungstenite::{ client::IntoClientRequest, - Message, + Message as TungsteniteMessage, }, MaybeTlsStream, WebSocketStream, }, - tokio_util::bytes::{ - BufMut, - BytesMut, - }, tracing::{ self, instrument, @@ -48,7 +45,9 @@ use { pub struct Config { pub history_url: Url, pub relayer_urls: Vec, + pub publisher_id: u32, pub authorization_token: String, + pub publisher_signing_key: Vec, #[serde(with = "humantime_serde", default = "default_publish_interval")] pub publish_interval_duration: Duration, } @@ -58,21 +57,18 @@ fn default_publish_interval() -> Duration { } struct RelayerSender { - ws_senders: Vec>, Message>>, + ws_senders: Vec>, TungsteniteMessage>>, } impl RelayerSender { - async fn send_price_update(&mut self, price_feed_data: &PriceFeedDataV1) -> Result<()> { - tracing::debug!("price_update: {:?}", price_feed_data); - let mut buf = BytesMut::new().writer(); - bincode::serde::encode_into_std_write( - price_feed_data, - &mut buf, - bincode::config::legacy(), - )?; - let buf = Message::Binary(buf.into_inner().freeze()); + async fn send_price_update( + &mut self, + signed_lazer_transaction: &SignedLazerTransaction, + ) -> Result<()> { + tracing::debug!("price_update: {:?}", signed_lazer_transaction); + let buf = signed_lazer_transaction.write_to_bytes()?; for sender in self.ws_senders.iter_mut() { - sender.send(buf.clone()).await?; + sender.send(TungsteniteMessage::from(buf.clone())).await?; sender.flush().await?; } Ok(()) @@ -83,7 +79,7 @@ async fn connect_to_relayer( url: &Url, token: &str, ) -> Result<( - SplitSink>, Message>, + SplitSink>, TungsteniteMessage>, SplitStream>>, )> { tracing::info!("connecting to the relayer at {}", url); @@ -168,23 +164,37 @@ mod lazer_exporter { state::local::LocalStore, }, anyhow::bail, + chrono::Utc, + ed25519_dalek::{ + Keypair, + Signer, + }, futures_util::StreamExt, - pyth_lazer_protocol::{ - publisher::PriceFeedDataV1, - router::{ - Price, - PriceFeedId, - TimestampUs, + protobuf::{ + well_known_types::timestamp::Timestamp, + Message, + MessageField, + }, + pyth_lazer_publisher_sdk::{ + publisher_update::{ + feed_update::Update, + FeedUpdate, + PriceUpdate, + PublisherUpdate, + }, + transaction::{ + lazer_transaction::Payload, + LazerTransaction, + SignedLazerTransaction, + TransactionSignatureType, }, }, std::{ collections::HashMap, - num::NonZeroI64, sync::Arc, time::Duration, }, tokio_stream::StreamMap, - tracing::warn, }; pub async fn lazer_exporter(config: Config, state: Arc) @@ -241,32 +251,73 @@ mod lazer_exporter { stream_map.insert(config.relayer_urls[i].clone(), receiver); } + let keypair = Keypair::from_bytes(&config.publisher_signing_key)?; let mut publish_interval = tokio::time::interval(config.publish_interval_duration); loop { tokio::select! { _ = publish_interval.tick() => { + let now = Utc::now().timestamp_micros(); + let publisher_timestamp = MessageField::some(Timestamp { + seconds: now / 1_000_000, + nanos: (now % 1_000_000 * 1000) as i32, + special_fields: Default::default(), + }); + let mut publisher_update = PublisherUpdate { + updates: vec![], + publisher_id: Some(config.publisher_id), + publisher_timestamp, + special_fields: Default::default(), + }; + let updates = &mut publisher_update.updates; + // TODO: This read locks and clones local::Store::prices, which may not meet performance needs. for (identifier, price_info) in state.get_all_price_infos().await { if let Some(symbol) = lazer_symbols.get(&identifier.to_string()) { - let price = if let Ok(price) = NonZeroI64::try_from(price_info.price) { - Some(Price(price)) - } else { - warn!("Zero price in local state identifier: {identifier} price_info: {price_info:?}"); - continue; - }; - if let Err(e) = relayer_sender.send_price_update(&PriceFeedDataV1 { - price, - best_ask_price: None, - best_bid_price: None, - price_feed_id: PriceFeedId(symbol.pyth_lazer_id), - publisher_timestamp_us: TimestampUs::now(), - source_timestamp_us: TimestampUs(price_info.timestamp.and_utc().timestamp_micros() as u64), - }).await { - tracing::error!("Error sending price update to relayer: {e:?}"); - bail!("Failed to send price update to relayer: {e:?}"); - } + let source_timestamp_micros = price_info.timestamp.and_utc().timestamp_micros(); + let source_timestamp = MessageField::some(Timestamp { + seconds: source_timestamp_micros / 1_000_000, + nanos: (source_timestamp_micros % 1_000_000 * 1000) as i32, + special_fields: Default::default(), + }); + updates.push(FeedUpdate { + feed_id: Some(symbol.pyth_lazer_id), + source_timestamp, + update: Some(Update::PriceUpdate(PriceUpdate { + price: Some(price_info.price), + ..PriceUpdate::default() + })), + special_fields: Default::default(), + }) + } + } + + if publisher_update.updates.is_empty() { + // nothing to publish + continue; + } + + let lazer_transaction = LazerTransaction { + payload: Some(Payload::PublisherUpdate(publisher_update)), + special_fields: Default::default(), + }; + let buf = match lazer_transaction.write_to_bytes() { + Ok(buf) => buf, + Err(e) => { + tracing::warn!("Failed to encode Lazer transaction to bytes: {:?}", e); + continue; } + }; + let signature = keypair.sign(&buf); + let signed_lazer_transaction = SignedLazerTransaction { + signature_type: Some(TransactionSignatureType::ed25519.into()), + signature: Some(signature.to_bytes().to_vec()), + payload: Some(buf), + special_fields: Default::default(), + }; + if let Err(e) = relayer_sender.send_price_update(&signed_lazer_transaction).await { + tracing::error!("Error publishing update to Lazer relayer: {e:?}"); + bail!("Failed to publish update to Lazer relayer: {e:?}"); } } // Handle messages from the relayers, such as errors if we send a bad update From 6378d35bbea6bbf2b1bb8b765cf84e916b95de08 Mon Sep 17 00:00:00 2001 From: Mike Rolish Date: Wed, 23 Apr 2025 14:45:35 -0500 Subject: [PATCH 2/3] simplify to Timestamp::now() --- src/agent/services/lazer_exporter.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/agent/services/lazer_exporter.rs b/src/agent/services/lazer_exporter.rs index 359d9fd..8a43392 100644 --- a/src/agent/services/lazer_exporter.rs +++ b/src/agent/services/lazer_exporter.rs @@ -164,7 +164,6 @@ mod lazer_exporter { state::local::LocalStore, }, anyhow::bail, - chrono::Utc, ed25519_dalek::{ Keypair, Signer, @@ -257,12 +256,7 @@ mod lazer_exporter { loop { tokio::select! { _ = publish_interval.tick() => { - let now = Utc::now().timestamp_micros(); - let publisher_timestamp = MessageField::some(Timestamp { - seconds: now / 1_000_000, - nanos: (now % 1_000_000 * 1000) as i32, - special_fields: Default::default(), - }); + let publisher_timestamp = MessageField::some(Timestamp::now()); let mut publisher_update = PublisherUpdate { updates: vec![], publisher_id: Some(config.publisher_id), From 57c8446ef1ba2cb5863ce257034c43306f6aef97 Mon Sep 17 00:00:00 2001 From: Mike Rolish Date: Thu, 24 Apr 2025 02:08:29 -0500 Subject: [PATCH 3/3] Redact publisher keypair from Debug --- src/agent/services/lazer_exporter.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/agent/services/lazer_exporter.rs b/src/agent/services/lazer_exporter.rs index 8a43392..94f8d43 100644 --- a/src/agent/services/lazer_exporter.rs +++ b/src/agent/services/lazer_exporter.rs @@ -47,11 +47,19 @@ pub struct Config { pub relayer_urls: Vec, pub publisher_id: u32, pub authorization_token: String, - pub publisher_signing_key: Vec, + publisher_keypair: PublisherKeypair, #[serde(with = "humantime_serde", default = "default_publish_interval")] pub publish_interval_duration: Duration, } +#[derive(Clone, Deserialize)] +struct PublisherKeypair(Vec); +impl std::fmt::Debug for PublisherKeypair { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "PublisherKeypair(redacted)") + } +} + fn default_publish_interval() -> Duration { Duration::from_millis(10) } @@ -244,13 +252,13 @@ mod lazer_exporter { // Establish relayer connections // Relayer will drop the connection if no data received in 5s - let (mut relayer_sender, relayer_receivers) = connect_to_relayers(&config).await?; + let (mut relayer_sender, relayer_receivers) = connect_to_relayers(config).await?; let mut stream_map = StreamMap::new(); for (i, receiver) in relayer_receivers.into_iter().enumerate() { stream_map.insert(config.relayer_urls[i].clone(), receiver); } - let keypair = Keypair::from_bytes(&config.publisher_signing_key)?; + let keypair = Keypair::from_bytes(&config.publisher_keypair.0)?; let mut publish_interval = tokio::time::interval(config.publish_interval_duration); loop {