Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/publish-rust-lazer-publisher-sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion lazer/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

85 changes: 44 additions & 41 deletions lazer/publisher_sdk/proto/publisher_update.proto
Original file line number Diff line number Diff line change
@@ -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;
}
43 changes: 23 additions & 20 deletions lazer/publisher_sdk/proto/pyth_lazer_transaction.proto
Original file line number Diff line number Diff line change
@@ -1,34 +1,37 @@
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;
}

// 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;
}
}
67 changes: 67 additions & 0 deletions lazer/publisher_sdk/proto/transaction_envelope.proto
Original file line number Diff line number Diff line change
@@ -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] ID of publisher based on the access token used to connect
optional uint32 publisher_id = 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The next option we will likely add to PayloadContext.context is a governance update context. That one doesn't have a publisher_id, so I think it makes more sense to move this publisher_id field to PublisherUpdateContext message.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't imagine that we would have a permissionless endpoint on Relayer. If permissioned, we need some way for governance to authn/z with Relayer, at which point we could read their ID. We may not necessarily want to. Maybe the ID takes another form.

Just curious what you had in mind. For now, I don't mind moving it into publisher context. I had a comment on your PR adding governance on how we determine the governance source.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed in Slack. If we add publisher's public key to the payload, I guess we won't need publisher id in the context at all.


// [required] timestamp wwhen relayer received the signed transaction
optional google.protobuf.Timestamp relayer_receive_timestamp = 2;

// [required] context set based on type of transaction
oneof context {
PublisherUpdateContext publisher_update_context = 3;
}
}

// Context contains status of each feed update found in transaction
message PublisherUpdateContext {
// [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 = 1;
}

// 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;
}
2 changes: 1 addition & 1 deletion lazer/publisher_sdk/rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
4 changes: 3 additions & 1 deletion lazer/publisher_sdk/rust/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
4 changes: 4 additions & 0 deletions lazer/publisher_sdk/rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
pub mod transaction_envelope {
pub use crate::protobuf::transaction_envelope::*;
}

pub mod transaction {
pub use crate::protobuf::pyth_lazer_transaction::*;
}
Expand Down
Loading