Skip to content

Commit 63e6356

Browse files
authored
Extend upd_aggregate and add/del_publisher to support 64 price components on pythnet (#384)
* program: Add a #define-based C feature for choice of PC_COMP_SIZE * program/rust: fix price.rs typo * Fix bindgen build for pythnet, test_sizes: add PC_COMP_SIZE asserts * program/rust: New `check` feature skips make-build in formatting CI * fix build.rs formatting * .pre-commit-config.yaml: Also check under `pythnet` feature * oracle.h: Remove cmd_upd_test_t * build.rs: Print OUT_DIR, PC_PYTHNET make variable instead of -D flag * program/c: dynamic header for features, dynamic c_upd_aggregate name * program: rename COMP_SIZE to NUM_COMP in C and Rust * makefile, build.rs: Clarify comments * upd_price.rs: remove stray log statement * upd_price.rs: missed another one
1 parent 6deffc0 commit 63e6356

File tree

14 files changed

+186
-73
lines changed

14 files changed

+186
-73
lines changed

.pre-commit-config.yaml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,13 @@ repos:
1414
language: "rust"
1515
entry: cargo +nightly-2023-03-01 fmt
1616
pass_filenames: false
17-
- id: cargo-clippy
18-
name: Cargo clippy
17+
- id: cargo-clippy-solana
18+
name: Cargo Clippy Solana
1919
language: "rust"
20-
entry : cargo +nightly-2023-03-01 clippy --tests -- -D warnings
20+
entry : cargo +nightly-2023-03-01 clippy --tests --features check -- -D warnings
21+
pass_filenames : false
22+
- id: cargo-clippy-pythnet
23+
name: Cargo Clippy Pythnet
24+
language: "rust"
25+
entry : cargo +nightly-2023-03-01 clippy --tests --features pythnet,check -- -D warnings
2126
pass_filenames : false

pcapps/pyth_csv.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ void csv_print::print_header()
8383
<< ",prev_slot"
8484
<< ",prev_price"
8585
<< ",prev_conf";
86-
for(unsigned i=0; i != PC_COMP_SIZE; ++i ) {
86+
for(unsigned i=0; i != PC_NUM_COMP; ++i ) {
8787
std::cout << ",comp_status_" << i
8888
<< ",comp_price_" << i
8989
<< ",comp_conf_" << i
@@ -156,7 +156,7 @@ void csv_print::parse_price( replay& rep )
156156
<< ',' << cptr->agg_.conf_
157157
<< ',' << cptr->agg_.pub_slot_;
158158
}
159-
for( unsigned i=ptr->num_; i != PC_COMP_SIZE; ++i ) {
159+
for( unsigned i=ptr->num_; i != PC_NUM_COMP; ++i ) {
160160
std::cout << ",,,,";
161161
}
162162
std::cout << std::endl;

program/c/makefile

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,25 @@ else
1111
include $(SOLANA)/sdk/sbf/c/sbf.mk
1212
endif
1313

14+
# Propagate the PC_PYTHNET feature by conditionally defining it in a
15+
# features.h header. The makefile included from Solana SDK does not
16+
# have an easy way to pass extra C flags which motivates this approach.
17+
ifdef PC_PYTHNET
18+
FEATURES_H_BODY:="\#pragma once\n\#define PC_PYTHNET 1"
19+
else
20+
FEATURES_H_BODY:="\#pragma once"
21+
endif
22+
23+
24+
.PHONY: features.h # Putting this in .PHONY makes sure the header is always regenerated
25+
features.h:
26+
echo $(FEATURES_H_BODY) > src/oracle/features.h
27+
1428

1529
# Bundle C code compiled to bpf for use by rust
1630
# The all target is defined by the solana makefile included above and generates the needed .o file.
1731
.PHONY: cpyth-bpf
18-
cpyth-bpf: all
32+
cpyth-bpf: features.h all
1933
bash -c "ar rc $(OUT_DIR)/libcpyth-bpf.a $(OUT_DIR)/oracle/*.o"
2034

2135

@@ -24,14 +38,14 @@ cpyth-bpf: all
2438
# 1) Compile C code to system architecture for use by rust's cargo test
2539
# 2) Bundle C code compiled to system architecture for use by rust's cargo test
2640
.PHONY: cpyth-native
27-
cpyth-native:
41+
cpyth-native: features.h
2842
gcc -c ./src/oracle/native/upd_aggregate.c -o $(OUT_DIR)/cpyth-native.o -fPIC
2943
ar rcs $(OUT_DIR)/libcpyth-native.a $(OUT_DIR)/cpyth-native.o
3044

3145

3246
# Note: there's probably a smart way to do this with wildcards but I (jayant) can't figure it out
3347
.PHONY: test
34-
test:
48+
test: features.h
3549
mkdir -p $(OUT_DIR)/test/
3650
gcc -c ./src/oracle/model/test_price_model.c -o $(OUT_DIR)/test/test_price_model.o -fPIC
3751
gcc -c ./src/oracle/sort/test_sort_stable.c -o $(OUT_DIR)/test/test_sort_stable.o -fPIC

program/c/src/oracle/native/upd_aggregate.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,13 @@ char heap_start[8192];
77
#define static_assert _Static_assert
88

99
#include "../upd_aggregate.h"
10+
#include "features.h"
1011

11-
extern bool c_upd_aggregate( pc_price_t *ptr, uint64_t slot, int64_t timestamp ){
12+
#ifdef PC_PYTHNET
13+
extern bool c_upd_aggregate_pythnet( pc_price_t *ptr, uint64_t slot, int64_t timestamp ){
14+
#else
15+
extern bool c_upd_aggregate_solana( pc_price_t *ptr, uint64_t slot, int64_t timestamp ){
16+
#endif
1217
return upd_aggregate(ptr, slot, timestamp );
1318
}
1419

program/c/src/oracle/oracle.h

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <stdbool.h>
44
#include "util/compat_stdint.h"
5+
#include "features.h"
56

67
#ifdef __cplusplus
78
extern "C" {
@@ -20,8 +21,18 @@ extern "C" {
2021
#define PC_PUBKEY_SIZE 32
2122
#define PC_PUBKEY_SIZE_64 (PC_PUBKEY_SIZE/sizeof(uint64_t))
2223
#define PC_MAP_TABLE_SIZE 640
23-
#define PC_COMP_SIZE 32
24-
#define PC_COMP_SIZE_V2 128
24+
25+
// Total price component slots available
26+
#define PC_NUM_COMP_SOLANA 32
27+
#define PC_NUM_COMP_PYTHNET 128
28+
29+
// PC_NUM_COMP - number of price components in use
30+
#ifdef PC_PYTHNET
31+
// Not whole PC_NUM_COMP_PYTHNET because of stack issues appearing in upd_aggregate()
32+
#define PC_NUM_COMP 64
33+
#else
34+
#define PC_NUM_COMP PC_NUM_COMP_SOLANA
35+
#endif
2536

2637
#define PC_PROD_ACC_SIZE 512
2738
#define PC_EXP_DECAY -9
@@ -194,10 +205,23 @@ typedef struct pc_price
194205
uint64_t prev_conf_; // confidence interval of previous aggregate with TRADING status
195206
int64_t prev_timestamp_; // unix timestamp of previous aggregate with TRADING status
196207
pc_price_info_t agg_; // aggregate price information
197-
pc_price_comp_t comp_[PC_COMP_SIZE];// component prices
208+
pc_price_comp_t comp_[PC_NUM_COMP];// component prices
198209
} pc_price_t;
199210

211+
#ifdef PC_PYTHNET
212+
213+
// Replace Solana component size's contribution with Pythnet's
214+
#define PC_EXPECTED_PRICE_T_SIZE_PYTHNET (3312 \
215+
- PC_NUM_COMP_SOLANA * sizeof(pc_price_comp_t) \
216+
+ PC_NUM_COMP * sizeof(pc_price_comp_t) \
217+
)
218+
219+
static_assert( sizeof( pc_price_t ) == PC_EXPECTED_PRICE_T_SIZE_PYTHNET, "" );
220+
#undef PC_EXPECTED_PRICE_SIZE_PYTHNET
221+
222+
#else
200223
static_assert( sizeof( pc_price_t ) == 3312, "" );
224+
#endif
201225

202226
// This constant needs to be an upper bound of the price account size, it is used within pythd for ztsd.
203227
// It is set tighly to the current price account + 96 component prices + 48 bytes for cumulative sums
@@ -383,19 +407,6 @@ typedef struct cmd_upd_price
383407

384408
static_assert( sizeof( cmd_upd_price_t ) == 40, "" );
385409

386-
typedef struct cmd_upd_test
387-
{
388-
uint32_t ver_;
389-
int32_t cmd_;
390-
uint32_t num_;
391-
int32_t expo_;
392-
int8_t slot_diff_[PC_COMP_SIZE];
393-
int64_t price_[PC_COMP_SIZE];
394-
uint64_t conf_[PC_COMP_SIZE];
395-
} cmd_upd_test_t;
396-
397-
static_assert( sizeof( cmd_upd_test_t ) == 560, "" );
398-
399410
// structure of clock sysvar account
400411
typedef struct sysvar_clock
401412
{

program/c/src/oracle/upd_aggregate.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,14 @@
44
#include <solana_sdk.h>
55
#include "oracle.h"
66
#include "upd_aggregate.h"
7+
#include "features.h"
78

8-
extern bool c_upd_aggregate( pc_price_t *ptr, uint64_t slot, int64_t timestamp ){
9+
// Dynamically deciding the name prevents linking the wrong C binary flavor
10+
#ifdef PC_PYTHNET
11+
extern bool c_upd_aggregate_pythnet( pc_price_t *ptr, uint64_t slot, int64_t timestamp ){
12+
#else
13+
extern bool c_upd_aggregate_solana( pc_price_t *ptr, uint64_t slot, int64_t timestamp ){
14+
#endif
915
return upd_aggregate(ptr, slot, timestamp );
1016
}
1117

program/c/src/oracle/upd_aggregate.h

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,16 @@
55
#include "model/price_model.c" /* FIXME: HACK TO DEAL WITH DOCKER LINKAGE ISSUES */
66
#include "pd.h"
77

8-
98
#ifdef __cplusplus
109
extern "C" {
1110
#endif
1211

1312
typedef struct pc_qset
1413
{
15-
pd_t iprice_[PC_COMP_SIZE];
16-
pd_t uprice_[PC_COMP_SIZE];
17-
pd_t lprice_[PC_COMP_SIZE];
18-
pd_t weight_[PC_COMP_SIZE];
14+
pd_t iprice_[PC_NUM_COMP];
15+
pd_t uprice_[PC_NUM_COMP];
16+
pd_t lprice_[PC_NUM_COMP];
17+
pd_t weight_[PC_NUM_COMP];
1918
int64_t decay_[1+PC_MAX_SEND_LATENCY];
2019
int64_t fact_[PC_FACTOR_SIZE];
2120
} pc_qset_t;
@@ -163,7 +162,7 @@ static inline bool upd_aggregate( pc_price_t *ptr, uint64_t slot, int64_t timest
163162
{
164163
uint32_t numv = 0;
165164
uint32_t nprcs = (uint32_t)0;
166-
int64_t prcs[ PC_COMP_SIZE * 3 ]; // ~0.75KiB for current PC_COMP_SIZE (FIXME: DOUBLE CHECK THIS FITS INTO STACK FRAME LIMIT)
165+
int64_t prcs[ PC_NUM_COMP * 3 ]; // ~0.75KiB for current PC_NUM_COMP (FIXME: DOUBLE CHECK THIS FITS INTO STACK FRAME LIMIT)
167166
for ( uint32_t i = 0; i != ptr->num_; ++i ) {
168167
pc_price_comp_t *iptr = &ptr->comp_[i];
169168
// copy contributing price to aggregate snapshot
@@ -195,7 +194,7 @@ static inline bool upd_aggregate( pc_price_t *ptr, uint64_t slot, int64_t timest
195194
// note: numv>0 and nprcs = 3*numv at this point
196195
int64_t agg_p25;
197196
int64_t agg_p75;
198-
int64_t scratch[ PC_COMP_SIZE * 3 ]; // ~0.75KiB for current PC_COMP_SIZE (FIXME: DOUBLE CHECK THIS FITS INTO STACK FRAME LIMIT)
197+
int64_t scratch[ PC_NUM_COMP * 3 ]; // ~0.75KiB for current PC_NUM_COMP (FIXME: DOUBLE CHECK THIS FITS INTO STACK FRAME LIMIT)
199198
price_model_core( (uint64_t)nprcs, prcs, &agg_p25, &agg_price, &agg_p75, scratch );
200199

201200
// get the left and right confidences

program/rust/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ lazy_static = "1.4.0"
4343
# to cargo-build-bpf at that point - we manually capture them at
4444
# compile-time and pass on to the child process.
4545
[features]
46+
check = [] # Skips make build in build.rs, use with cargo-clippy and cargo-check
4647
debug = []
4748
library = []
4849
pythnet = [] # logic-affecting features start with this one

program/rust/build.rs

Lines changed: 64 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,68 @@
11
use {
22
bindgen::Builder,
33
std::{
4-
path::PathBuf,
4+
path::{
5+
Path,
6+
PathBuf,
7+
},
58
process::Command,
69
},
710
};
811

912
fn main() {
1013
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
1114

15+
let has_feat_pythnet = std::env::var("CARGO_FEATURE_PYTHNET").is_ok();
16+
let has_feat_check = std::env::var("CARGO_FEATURE_CHECK").is_ok();
17+
1218
// OUT_DIR is the path cargo provides to a build directory under `target/` specifically for
1319
// isolated build artifacts. We use this to build the C program and then link against the
1420
// resulting static library. This allows building the program when used as a dependency of
1521
// another crate.
1622
let out_dir = std::env::var("OUT_DIR").unwrap();
23+
24+
// Useful for C binary debugging, not printed without -vv cargo flag
25+
eprintln!("OUT_DIR is {}", out_dir);
1726
let out_dir = PathBuf::from(out_dir);
1827

28+
let mut make_extra_flags = vec![];
29+
let mut clang_extra_flags = vec![];
30+
31+
if has_feat_pythnet {
32+
// Define PC_PYTHNET for the C binary build
33+
make_extra_flags.push("PC_PYTHNET=1");
34+
35+
// Define PC_PYTHNET for the bindings build
36+
clang_extra_flags.push("-DPC_PYTHNET=1");
37+
}
38+
1939
let mut make_targets = vec![];
2040
if target_arch == "bpf" {
2141
make_targets.push("cpyth-bpf");
2242
} else {
2343
make_targets.push("cpyth-native");
2444
}
25-
make_targets.push("test");
2645

27-
// We must forward OUT_DIR as an env variable to the make script otherwise it will output
28-
// its artifacts to the wrong place.
29-
std::process::Command::new("make")
30-
.env("VERBOSE", "1")
31-
.env("OUT_DIR", out_dir.display().to_string())
32-
.current_dir("../c")
33-
.args(make_targets)
34-
.status()
35-
.expect("Failed to build C program");
46+
make_targets.push("test");
3647

37-
// Link against the right library for the architecture
38-
if target_arch == "bpf" {
39-
println!("cargo:rustc-link-lib=static=cpyth-bpf");
48+
// When the `check` feature is active, we skip the make
49+
// build. This is used in pre-commit checks to avoid requiring
50+
// Solana in its GitHub Action.
51+
if has_feat_check {
52+
eprintln!("WARNING: `check` feature active, make build is skipped");
4053
} else {
41-
println!("cargo:rustc-link-lib=static=cpyth-native");
54+
do_make_build(make_extra_flags, make_targets, &out_dir);
55+
56+
// Link against the right library for the architecture
57+
if target_arch == "bpf" {
58+
println!("cargo:rustc-link-lib=static=cpyth-bpf");
59+
} else {
60+
println!("cargo:rustc-link-lib=static=cpyth-native");
61+
}
62+
63+
println!("cargo:rustc-link-lib=static=cpyth-test");
64+
println!("cargo:rustc-link-search={}", out_dir.display());
4265
}
43-
println!("cargo:rustc-link-lib=static=cpyth-test");
44-
println!("cargo:rustc-link-search={}", out_dir.display());
4566

4667
std::fs::create_dir("./codegen").unwrap_or_else(|e| {
4768
eprintln!(
@@ -53,16 +74,41 @@ fn main() {
5374
// Generate and write bindings
5475
let bindings = Builder::default()
5576
.clang_arg(format!("-I{:}", get_solana_inc_path().display()))
77+
.clang_args(clang_extra_flags)
5678
.header("./src/bindings.h")
5779
.rustfmt_bindings(true)
5880
.generate()
5981
.expect("Unable to generate bindings");
82+
6083
bindings
6184
.write_to_file("./codegen/bindings.rs")
6285
.expect("Couldn't write bindings!");
6386

6487
// Rerun the build script if either the rust or C code changes
65-
println!("cargo:rerun-if-changed=../")
88+
println!("cargo:rerun-if-changed=../");
89+
}
90+
91+
fn do_make_build(extra_flags: Vec<&str>, targets: Vec<&str>, out_dir: &Path) {
92+
// We must forward OUT_DIR as an env variable to the make script otherwise it will output
93+
// its artifacts to the wrong place.
94+
let make_output = std::process::Command::new("make")
95+
.env("VERBOSE", "1")
96+
.env("OUT_DIR", out_dir.display().to_string())
97+
.current_dir("../c")
98+
.args(extra_flags)
99+
.args(targets)
100+
.output()
101+
.expect("Failed to run make for C oracle program");
102+
103+
if !make_output.status.success() {
104+
panic!(
105+
"C oracle make build did not exit with 0 (code
106+
({:?}).\n\nstdout:\n{}\n\nstderr:\n{}",
107+
make_output.status.code(),
108+
String::from_utf8(make_output.stdout).unwrap_or("<non-utf8>".to_owned()),
109+
String::from_utf8(make_output.stderr).unwrap_or("<non-utf8>".to_owned())
110+
);
111+
}
66112
}
67113

68114
/// Find the Solana C header bindgen

0 commit comments

Comments
 (0)