diff --git a/.github/workflows/publish-rust-lazer-publisher-sdk.yml b/.github/workflows/publish-rust-lazer-publisher-sdk.yml index 453dfbf0e5..0bc62c5459 100644 --- a/.github/workflows/publish-rust-lazer-publisher-sdk.yml +++ b/.github/workflows/publish-rust-lazer-publisher-sdk.yml @@ -12,6 +12,10 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v2 + - name: Install Protoc + uses: arduino/setup-protoc@v3 + with: + version: "30.2" - run: ./publish.sh env: diff --git a/lazer/Cargo.lock b/lazer/Cargo.lock index a72b243fa0..94731d0909 100644 --- a/lazer/Cargo.lock +++ b/lazer/Cargo.lock @@ -3864,7 +3864,7 @@ dependencies = [ [[package]] name = "pyth-lazer-publisher-sdk" -version = "0.1.1" +version = "0.1.2" dependencies = [ "protobuf", "protobuf-codegen", diff --git a/lazer/publisher_sdk/proto/publisher_update.proto b/lazer/publisher_sdk/proto/publisher_update.proto index e47f7cfa0c..916837dedc 100644 --- a/lazer/publisher_sdk/proto/publisher_update.proto +++ b/lazer/publisher_sdk/proto/publisher_update.proto @@ -1,59 +1,62 @@ syntax = "proto3"; +package pyth_lazer_transaction; import "google/protobuf/timestamp.proto"; -package pyth_lazer_transaction; +// if any fields marked as [required] are missing, feed/publisher update will be rejected -// PublisherUpdate contains an array of individual updates and a timestamp +// Publisher update included in transaction +// +// Each publisher update contains a batch of feed updates from publisher. +// The publisher uses Pyth Agent on their side that handles batching. +// Each feed update specifies a single update type (price, funding rate, etc.) message PublisherUpdate { - // Array of updates, each of which target a single feed - repeated FeedUpdate updates = 1; - - // ID of the Publisher that is sending the update - // Should match ID stored in Pyth Lazer - optional uint32 publisher_id = 2; + // [required] array of feed updates, each of which target a single feed + // order of updates are preserved between encoding/decoding + repeated FeedUpdate updates = 1; - // Timestamp when this message was created - optional google.protobuf.Timestamp publisher_timestamp = 3; + // [required] timestamp when batch of feed updates was collected + optional google.protobuf.Timestamp publisher_timestamp = 2; } -// Update to a feed. May contain different types of data depending on what kind of update it is +// A single feed update containing one type of update message FeedUpdate { - // Feed which the update should be applied to - // Should match a feed id recognized by PythLazer - optional uint32 feed_id = 1; - - // Timestamp when this data was first acquired or generated - optional google.protobuf.Timestamp source_timestamp = 2; - - // one of the valid updates allowed by publishers for a lazer feed - oneof update { - PriceUpdate price_update = 3; - FundingRateUpdate funding_rate_update = 4; - }; + // [required] id of the lazer feed to be updated + // should match the ids of feeds recognized by pyth lazer + optional uint32 feed_id = 1; + + // [required] timestamp when this data was first acquired or generated + optional google.protobuf.Timestamp source_timestamp = 2; + + // [required] one type of update containing specific data + oneof update { + PriceUpdate price_update = 3; + FundingRateUpdate funding_rate_update = 4; + }; } +// feed update containing data for the core price, bid, and ask prices message PriceUpdate { - // Price for the symbol as an integer - // Should be produced with a matching exponent to the configured exponent value in PythLazer - // May be missing if no price data is available - optional int64 price = 1; - - // Best Bid Price for the symbol as an integer - // Should be produced with a matching exponent to the configured exponent value in PythLazer - // May be missing if no data is available - optional int64 best_bid_price = 2; - - // Best Ask Price for the symbol as an integer - // Should be produced with a matching exponent to the configured exponent value in PythLazer - // May be missing if no data is available - optional int64 best_ask_price = 3; + // [optional] price for the feed as an integer + // should be produced with a matching exponent to the configured exponent value in pyth lazer + optional int64 price = 1; + + // [optional] best bid price for the feed as an integer + // should be produced with a matching exponent to the configured exponent value in pyth lazer + // may be missing if no data is available + optional int64 best_bid_price = 2; + + // [optional] best ask price for the feed as an integer + // should be produced with a matching exponent to the configured exponent value in pyth lazer + // may be missing if no data is available + optional int64 best_ask_price = 3; } +// feed update containing data relating to funding rate message FundingRateUpdate { - // Price for which the funding rate applies to - optional int64 price = 1; + // [optional] price for which the funding rate applies to + optional int64 price = 1; - // Perpetual Future funding rate - optional int64 rate = 2; + // [optional] perpetual future funding rate + optional int64 rate = 2; } diff --git a/lazer/publisher_sdk/proto/pyth_lazer_transaction.proto b/lazer/publisher_sdk/proto/pyth_lazer_transaction.proto index 203df35076..2d5b337912 100644 --- a/lazer/publisher_sdk/proto/pyth_lazer_transaction.proto +++ b/lazer/publisher_sdk/proto/pyth_lazer_transaction.proto @@ -1,34 +1,39 @@ syntax = "proto3"; - package pyth_lazer_transaction; import "publisher_update.proto"; -// Types of Signatures allowed for signing Lazer Transactions -enum TransactionSignatureType { - // signature is 64 bytes long - ed25519 = 0; -} +// if any fields marked as [required] are missing, transaction will be rejected +// if signature does not match payload bytes, transaction will be rejected -// Signed lazer transaction payload -// This is what Pyth Lazer expects as input to the system +// Signed transaction for lazer +// Payload should be created on the publisher side and encoded as bytes. +// Resulting bytes should then be signed with the signature scheme specified. +// The signed lazer transaction is encoded as bytes and sent to Pyth Lazer Relayer. message SignedLazerTransaction { - // Type and signature should match - optional TransactionSignatureType signature_type = 1; + // [required] specifies the type of signature used to sign the payload + optional TransactionSignatureType signature_type = 1; - // Signature derived by signing payload with private key - optional bytes signature = 2; + // [required] signature derived from signing payload bytes + optional bytes signature = 2; - // a LazerTransaction message which is already encoded with protobuf as bytes - // The encoded bytes are what should be signed - optional bytes payload = 3; + // [required] lazer transaction encoded as bytes through protobuf + optional bytes payload = 3; + + // TODO: Add public key +} + +// Types of signatures supported by Pyth Lazer +enum TransactionSignatureType { + // signature is 64 bytes long + ed25519 = 0; } // Transaction contianing one of the valid Lazer Transactions message LazerTransaction { - oneof payload { - // Expected transaction sent by Publishers - // May contain many individual updates to various feeds - PublisherUpdate publisher_update = 1; - } + // [required] valid transaction types supported by pyth lazer + oneof payload { + // updates to feeds, sent by authorized publishers + PublisherUpdate publisher_update = 1; + } } diff --git a/lazer/publisher_sdk/proto/transaction_envelope.proto b/lazer/publisher_sdk/proto/transaction_envelope.proto new file mode 100644 index 0000000000..bd89e9758e --- /dev/null +++ b/lazer/publisher_sdk/proto/transaction_envelope.proto @@ -0,0 +1,67 @@ +syntax = "proto3"; +package pyth_lazer_transaction; + +import "google/protobuf/timestamp.proto"; +import "pyth_lazer_transaction.proto"; + +// Envelope containing signed transaction and context attached by Pyth Lazer. +// Created by Pyth Lazer Relayers, which also generate and attach the context. +message TransactionEnvelope { + // [required] signed transaction encoded with protobuf + optional SignedLazerTransaction signed_transaction = 1; + + // [required] context attached by pyth lazer relayer + optional PayloadContext payload_context = 2; +} + +// Context attached by Pyth Lazer Relayer containing information necessary for processing transaction. +// Has different context data depending on the type of transaction. +// Submitted over Message Queue to be read by rest of Pyth Lazer service. +message PayloadContext { + // [required] timestamp wwhen relayer received the signed transaction + optional google.protobuf.Timestamp relayer_receive_timestamp = 1; + + // [required] context set based on type of transaction + oneof context { + PublisherUpdateContext publisher_update_context = 2; + } +} + +// Context contains status of each feed update found in transaction +message PublisherUpdateContext { + // [required] ID of publisher based on the access token used to connect + optional uint32 publisher_id = 1; + + // [required] context for each feed update + // must exactly match length and order of feed updates + // order of updates are preserved between encoding/decoding + repeated FeedUpdateContext feed_update_context = 2; +} + +// State for each feed update. +// Each feed update is validated and may be marked as rejected by Relayer. +message FeedUpdateContext { + // [required] status of feed update + oneof status { + Accepted accepted = 1; + Rejected rejected = 2; + } +} + +// Accepted publisher update +message Accepted {} + +// Rejected publisher update and its reason for being rejected +message Rejected { + // [required] reason for rejection + RejectReason reject_reason = 1; +} + +// The reasons that a publisher update might be rejected for +enum RejectReason { + InvalidTimestamp = 0; + PriceDeviation = 1; + PriceOverflow = 2; + InvalidFeedId = 3; + MissingFields = 4; +} diff --git a/lazer/publisher_sdk/rust/Cargo.toml b/lazer/publisher_sdk/rust/Cargo.toml index 55e5f205d4..74e763be09 100644 --- a/lazer/publisher_sdk/rust/Cargo.toml +++ b/lazer/publisher_sdk/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pyth-lazer-publisher-sdk" -version = "0.1.1" +version = "0.1.2" edition = "2021" description = "Pyth Lazer Publisher SDK types." license = "Apache-2.0" diff --git a/lazer/publisher_sdk/rust/build.rs b/lazer/publisher_sdk/rust/build.rs index b8d1c542d5..b0bd56ade2 100644 --- a/lazer/publisher_sdk/rust/build.rs +++ b/lazer/publisher_sdk/rust/build.rs @@ -8,10 +8,12 @@ fn main() -> Result<()> { println!("cargo:rerun-if-changed=../proto/"); protobuf_codegen::Codegen::new() - .pure() + .protoc() + .protoc_extra_arg("--include_source_info") .include("../proto") .input("../proto/publisher_update.proto") .input("../proto/pyth_lazer_transaction.proto") + .input("../proto/transaction_envelope.proto") .cargo_out_dir("protobuf") .run_from_script(); diff --git a/lazer/publisher_sdk/rust/src/lib.rs b/lazer/publisher_sdk/rust/src/lib.rs index 3cdb201cfe..e1629e99be 100644 --- a/lazer/publisher_sdk/rust/src/lib.rs +++ b/lazer/publisher_sdk/rust/src/lib.rs @@ -1,3 +1,7 @@ +pub mod transaction_envelope { + pub use crate::protobuf::transaction_envelope::*; +} + pub mod transaction { pub use crate::protobuf::pyth_lazer_transaction::*; }