Skip to content

Commit 846718b

Browse files
committed
feat(lazer): add governance instructions protocol
1 parent 3c32ca2 commit 846718b

File tree

6 files changed

+339
-3
lines changed

6 files changed

+339
-3
lines changed

lazer/Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
syntax = "proto3";
2+
3+
import "google/protobuf/timestamp.proto";
4+
import "google/protobuf/duration.proto";
5+
6+
package pyth_lazer_transaction;
7+
8+
// A dynamically typed value similar to `google.protobuf.Value`
9+
// but supporting more types.
10+
message DynamicValue {
11+
message List {
12+
repeated DynamicValue items = 1;
13+
}
14+
message MapItem {
15+
// [required] Must be unique.
16+
optional string key = 1;
17+
// [required]
18+
optional DynamicValue value = 2;
19+
}
20+
message Map {
21+
repeated MapItem items = 1;
22+
}
23+
24+
oneof value {
25+
string string_value = 1;
26+
double double_value = 2;
27+
uint64 uint_value = 3;
28+
sint64 int_value = 4;
29+
bool bool_value = 5;
30+
bytes bytes_value = 6;
31+
google.protobuf.Duration duration_value = 7;
32+
google.protobuf.Timestamp timestamp_value = 8;
33+
List list = 9;
34+
Map map = 10;
35+
}
36+
}
Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
syntax = "proto3";
2+
3+
import "google/protobuf/timestamp.proto";
4+
import "google/protobuf/duration.proto";
5+
import "google/protobuf/empty.proto";
6+
7+
import "dynamic_value.proto";
8+
9+
// If any field documented as `[required]` is not present in the instruction,
10+
// the instruction will be rejected.
11+
12+
package pyth_lazer_transaction;
13+
14+
// Representation of a complete governance instruction. This value will be signed
15+
// by a governance source.
16+
message GovernanceInstruction {
17+
// Action requested by this instruction. For the instruction to be accepted, all directives
18+
// must be successfully applied. In case of any failure, the whole instruction is reverted.
19+
// However, note that if the instruction targets multiple (or all) shards, each shard will
20+
// accept or reject the instruction independently of other shards.
21+
repeated GovernanceDirective directives = 1;
22+
// [optional] If specified, the instruction will be rejected if the current timestamp
23+
// is less than the specified value. In case of rejection, the same instruction can be resubmitted
24+
// and executed later once the time requirement is met.
25+
optional google.protobuf.Timestamp min_execution_timestamp = 2;
26+
// [optional] If specified, the instruction will be rejected if the current timestamp
27+
// is greater than the specified value. After `max_execution_timestamp` is in the past,
28+
// it will no longer be possible to execute this instruction.
29+
optional google.protobuf.Timestamp max_execution_timestamp = 3;
30+
// [required] Sequence number of this instruction. It must be greater than 0.
31+
// It must always be increasing, but not required to be
32+
// strictly sequential (i.e. gaps are allowed). Each shard separately keeps track of the last executed
33+
// governance instruction and will reject instructions with the same or smaller sequence no.
34+
// Note that if instructions are received out of order, some of them may become permanently
35+
// rejected (e.g. if instruction #3 has been successfully processed before instruction #2 was observed,
36+
// #2 will always be rejected).
37+
// Sequence numbers are assigned and tracked separately for each governance source.
38+
optional uint32 governance_sequence_no = 4;
39+
}
40+
41+
// Specifies which shards the governance instruction applies to.
42+
message ShardFilter {
43+
// The instruction applies to the specified shards.
44+
message ShardNames {
45+
// Must not be empty.
46+
repeated string shard_names = 1;
47+
}
48+
// The instruction applies to the specified shard groups.
49+
message ShardGroups {
50+
// Must not be empty.
51+
repeated string shard_groups = 1;
52+
}
53+
// [required]
54+
oneof filter {
55+
// The instruction applies to all shards.
56+
google.protobuf.Empty all_shards = 1;
57+
ShardNames shard_names = 2;
58+
ShardGroups shard_groups = 3;
59+
}
60+
}
61+
62+
// An item of a governance instruction.
63+
message GovernanceDirective {
64+
// [required] Specifies which shards the governance instruction applies to.
65+
// The instruction applies to each shard independently of other shards and may apply
66+
// at a different time. The instruction may succeed on some shards and fail on other shards.
67+
// Note that each shard has its own list of governance sources and permissions,
68+
// and a `GovernanceInstruction` is issued by a single source, so multiple instructions
69+
// from different sources may be needed to apply a change to multiple shards or shard groups.
70+
optional ShardFilter shard_filter = 1;
71+
// [required]
72+
oneof action {
73+
AddGovernanceSource add_governance_source = 101;
74+
UpdateGovernanceSource update_governance_source = 102;
75+
SetShardName set_shard_name = 103;
76+
SetShardGroup set_shard_group = 104;
77+
AddPublisher add_publisher = 105;
78+
UpdatePublisher update_publisher = 106;
79+
AddFeed add_feed = 107;
80+
UpdateFeed update_feed = 108;
81+
}
82+
}
83+
84+
// Permissions granted to a governance source.
85+
// bool fields in this message are optional and default to false (no permission).
86+
message Permissions {
87+
// All operations, including operations added in the future.
88+
optional bool all = 1;
89+
optional bool add_governance_source = 2;
90+
// All operations under `UpdateGovernanceSource` (update and delete),
91+
// including operations added in the future.
92+
optional bool update_governance_source_all = 3;
93+
optional bool update_governance_source_permissions = 4;
94+
optional bool remove_governance_source = 5;
95+
optional bool set_shard_name = 6;
96+
optional bool set_shard_group = 7;
97+
optional bool add_publisher = 8;
98+
// All operations under `UpdatePublisher` (update and delete),
99+
// including operations added in the future.
100+
optional bool update_publisher_all = 9;
101+
optional bool set_publisher_name = 10;
102+
optional bool add_publisher_public_keys = 11;
103+
optional bool remove_publisher_public_keys = 12;
104+
optional bool set_publisher_public_keys = 13;
105+
optional bool set_publisher_active = 14;
106+
optional bool remove_publisher = 15;
107+
optional bool add_feed = 16;
108+
// All operations under `UpdateFeed` (update and delete),
109+
// including operations added in the future.
110+
optional bool update_feed_all = 17;
111+
optional bool update_feed_metadata = 18;
112+
optional bool activate_feed = 19;
113+
optional bool deactivate_feed = 20;
114+
optional bool remove_feed = 21;
115+
}
116+
117+
// Specifies the way governance transactions are signed and verified.
118+
message GovernanceSource {
119+
// Governance transactions are signed by a single Ed25519 signature.
120+
// This will generally be used in development and testing environments.
121+
message SingleEd25519 {
122+
// [required] Ed25519 public key that signs governance transactions.
123+
optional bytes public_key = 1;
124+
}
125+
126+
// [required]
127+
oneof source {
128+
SingleEd25519 single_ed25519 = 1;
129+
// TODO: wormhole source goes here.
130+
}
131+
}
132+
133+
message AddGovernanceSource {
134+
// [required] Governance source that should be added.
135+
optional GovernanceSource new_source = 1;
136+
// [required] Permissions granted to this source.
137+
optional Permissions permissions = 2;
138+
}
139+
140+
message UpdateGovernanceSource {
141+
// [required] Governance source that should be updated. Rejects if there is no such source.
142+
optional GovernanceSource source = 1;
143+
// [required]
144+
oneof action {
145+
SetGovernanceSourcePermissions set_governance_source_permissions = 2;
146+
// Removes a governance source. Note that the last sequence number associated with this source
147+
// will be retained in the state to prevent repeated execution of instructions in case
148+
// the same source is re-added later.
149+
google.protobuf.Empty remove_governance_source = 3;
150+
}
151+
}
152+
153+
message SetGovernanceSourcePermissions {
154+
// [required] Permissions granted to this source. Replaces all previous permissions.
155+
optional Permissions permissions = 2;
156+
}
157+
158+
// Set shard name. This action will be rejected if `GovernanceDirective.shard_names` is empty or contains
159+
// more than one item.
160+
message SetShardName {
161+
// [required] New shard name. Must be unique across all shards in all environments.
162+
// (Warning: it's not possible to enforce this rule within a shard!)
163+
optional string shard_name = 1;
164+
}
165+
166+
// Set shard group. This action will be rejected if `GovernanceDirective.shard_names` is empty or contains
167+
// more than one item.
168+
message SetShardGroup {
169+
// [required] Group name, e.g. "production", "staging", "testing", etc.
170+
// Data from shards belonging to the same group can be joined and served to consumers as a whole.
171+
// Active feed names must be unique within a group, but not across all groups.
172+
optional string shard_group = 1;
173+
}
174+
175+
message AddPublisher {
176+
// [required] Publisher ID. Restricted to uint16. Must be different from existing ids.
177+
optional uint32 publisher_id = 1;
178+
// [required] Publisher name (only for debug/monitoring/management purposes).
179+
// Must be different from existing publisher names.
180+
optional string name = 2;
181+
// Public keys used to sign publisher update transactions.
182+
repeated bytes public_keys = 3;
183+
// [required] If true, the publisher is active, i.e. it's allowed to publish updates.
184+
optional bool is_active = 4;
185+
}
186+
187+
message UpdatePublisher {
188+
// [required] ID of the publisher that is being updated. Rejects if there is no such publisher.
189+
optional uint32 publisher_id = 1;
190+
// [required]
191+
oneof action {
192+
SetPublisherName set_publisher_name = 2;
193+
AddPublisherPublicKeys add_publisher_public_keys = 3;
194+
RemovePublisherPublicKeys remove_publisher_public_keys = 4;
195+
SetPublisherPublicKeys set_publisher_public_keys = 5;
196+
SetPublisherActive set_publisher_active = 6;
197+
google.protobuf.Empty remove_publisher = 7;
198+
}
199+
}
200+
201+
message SetPublisherName {
202+
// [required] New name.
203+
optional string name = 1;
204+
}
205+
206+
// Add new keys.
207+
message AddPublisherPublicKeys {
208+
// Must not be empty.
209+
repeated bytes public_keys = 1;
210+
}
211+
212+
// Remove existing keys.
213+
message RemovePublisherPublicKeys {
214+
// Must not be empty.
215+
repeated bytes public_keys = 1;
216+
}
217+
218+
// Remove all existing public keys and add new keys (if specified).
219+
message SetPublisherPublicKeys {
220+
repeated bytes public_keys = 1;
221+
}
222+
223+
message SetPublisherActive {
224+
// [required]
225+
optional bool is_active = 1;
226+
}
227+
228+
// Feed is inactive when added, meaning that it will be available to publishers but not to consumers.
229+
message AddFeed {
230+
// [required] ID of the price feed. Must be unique (within the shard).
231+
optional uint32 price_feed_id = 1;
232+
// [required] Feed metadata. Some properties are required (name, exponent, etc.).
233+
// Known properties must have the expected type.
234+
// Additional arbitrary properties are allowed.
235+
// (TODO: document known metadata properties)
236+
optional DynamicValue.Map metadata = 2;
237+
// IDs of publishers enabled for this feed.
238+
repeated uint32 permissioned_publishers = 3;
239+
}
240+
241+
message UpdateFeed {
242+
// [required] ID of the feed that is being updated. Rejects if there is no such feed.
243+
optional uint32 price_feed_id = 1;
244+
// [required]
245+
oneof action {
246+
UpdateFeedMetadata update_feed_metadata = 2;
247+
ActivateFeed activate_feed = 3;
248+
DeactivateFeed deactivate_feed = 4;
249+
google.protobuf.Empty remove_feed = 5;
250+
}
251+
}
252+
253+
message UpdateFeedMetadata {
254+
// [required] Property name.
255+
optional string name = 1;
256+
// [optional] Property value. If unset, the property will be removed.
257+
optional DynamicValue value = 2;
258+
}
259+
260+
// Set the feed as active or shedule an activation.
261+
// If there was already a pending activation or deactivation, it will be cleared
262+
// when this governance instruction is processed.
263+
// Warning: there must never be two feeds with the same name active at the same time
264+
// within a shard group. This cannot be enforced within a shard. When a feed needs to be
265+
// moved between shards, use `activation_timestamp` and `deactivation_timestamp`
266+
// to deactivate it in the old shard and activate it in the new shard at the same time.
267+
message ActivateFeed {
268+
// [optional] If provided, the feed will activate at the specified timestamp.
269+
// If `activation_timestamp` is already passed or if it's unset,
270+
// the feed will be activated immediately when this
271+
// governance instruction is processed.
272+
optional google.protobuf.Timestamp activation_timestamp = 1;
273+
}
274+
275+
// Set the feed as inactive or shedule a deactivation.
276+
// If there was already a pending activation or deactivation, it will be cleared
277+
// when this governance instruction is processed.
278+
// See also: `ActivateFeed` docs.
279+
message DeactivateFeed {
280+
// [optional] If provided, the feed will deactivate at the specified timestamp.
281+
// If `deactivation_timestamp` is already passed or if it's unset,
282+
// the feed will be deactivated immediately when this
283+
// governance instruction is processed.
284+
optional google.protobuf.Timestamp deactivation_timestamp = 1;
285+
}
286+

lazer/publisher_sdk/proto/pyth_lazer_transaction.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ syntax = "proto3";
33
package pyth_lazer_transaction;
44

55
import "publisher_update.proto";
6+
import "governance_instruction.proto";
67

78
// Types of Signatures allowed for signing Lazer Transactions
89
enum TransactionSignatureType {
@@ -30,5 +31,7 @@ message LazerTransaction {
3031
// Expected transaction sent by Publishers
3132
// May contain many individual updates to various feeds
3233
PublisherUpdate publisher_update = 1;
34+
// Sent by governance.
35+
GovernanceInstruction governance_instruction = 2;
3336
}
3437
}

lazer/publisher_sdk/rust/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ repository = "https://github.com/pyth-network/pyth-crosschain"
1010
protobuf = "3.7.2"
1111

1212
[build-dependencies]
13+
fs-err = "3.1.0"
1314
protobuf-codegen = "3.7.2"

lazer/publisher_sdk/rust/build.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use std::io::Result;
22

3+
use fs_err::read_dir;
4+
35
/// Automatically runs during cargo build.
46
/// Proto files for Lazer are defined in the lazer sdk folder in the proto/ subdirectory.
57
/// Both JS and Rust SDKs read the proto files for generating types.
@@ -8,10 +10,8 @@ fn main() -> Result<()> {
810
println!("cargo:rerun-if-changed=../proto/");
911

1012
protobuf_codegen::Codegen::new()
11-
.pure()
1213
.include("../proto")
13-
.input("../proto/publisher_update.proto")
14-
.input("../proto/pyth_lazer_transaction.proto")
14+
.inputs(read_dir("../proto")?.map(|item| item.unwrap().path()))
1515
.cargo_out_dir("protobuf")
1616
.run_from_script();
1717

0 commit comments

Comments
 (0)