Skip to content

Commit d522ff4

Browse files
authored
Borsch traits using bindgen (#196)
* changed c return value of update price to indicate aggregation (#190) * changed c return value of update price to indicate aggregation * forgot to add bindings * removed the need for casting * fixed update_no_fail * removed unnecessary docker packages * timeless coments * added comment clarifying that 0 is success * a convinient way to derive traits * silenced unused bindings warnings * removed dead code * added a comment explaining the structure * changed importing style * called fmt on bindings output * added register_traits method * removed unnecessary match * fixed formatting * made it easier to add borsch to more types by not using vectors
1 parent 4d37ff5 commit d522ff4

File tree

6 files changed

+91
-4
lines changed

6 files changed

+91
-4
lines changed

program/rust/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,13 @@ edition = "2021"
77
license = "Apache 2.0"
88
publish = false
99

10+
[build-dependencies]
11+
bindgen = "0.60.1"
12+
1013
[dependencies]
1114
solana-program = "=1.10.29"
15+
borsh = "0.9"
16+
1217

1318
[lib]
1419
crate-type = ["cdylib", "lib"]

program/rust/build.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,26 @@
1+
mod build_utils;
2+
use bindgen::Builder;
3+
use std::vec::Vec;
4+
15
fn main() {
26
println!("cargo:rustc-link-search=../c/target");
7+
8+
let borsh_derives = ["BorshSerialize".to_string(), "BorshDeserialize".to_string()];
9+
10+
//make a parser and to it type, traits pairs
11+
let mut parser = build_utils::DeriveAdderParserCallback::new();
12+
parser.register_traits("cmd_hdr", borsh_derives.to_vec());
13+
parser.register_traits("pc_acc", borsh_derives.to_vec());
14+
15+
16+
//generate and write bindings
17+
let bindings = Builder::default()
18+
.header("./src/bindings.h")
19+
.parse_callbacks(Box::new(parser))
20+
.rustfmt_bindings(true)
21+
.generate()
22+
.expect("Unable to generate bindings");
23+
bindings
24+
.write_to_file("./bindings.rs")
25+
.expect("Couldn't write bindings!");
326
}

program/rust/build_utils.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use bindgen::callbacks::ParseCallbacks;
2+
use std::collections::HashMap;
3+
use std::panic::UnwindSafe;
4+
///This type stores a hashmap from structnames
5+
///to vectors of trait names, and ensures
6+
///that the traits of each struct are added to its
7+
///definition when an instance of this struct
8+
///is provided as a ParseCallback for bindgen
9+
#[derive(Debug, Default)]
10+
pub struct DeriveAdderParserCallback<'a> {
11+
pub types_to_traits: HashMap<&'a str, Vec<String>>,
12+
}
13+
14+
impl<'a> DeriveAdderParserCallback<'a> {
15+
///create a parser that does not add any traits
16+
pub fn new() -> Self {
17+
Default::default()
18+
}
19+
//add pairs of types and their desired traits
20+
pub fn register_traits(&mut self, type_name: &'a str, traits: Vec<String>) {
21+
self.types_to_traits.insert(&type_name, traits);
22+
}
23+
}
24+
25+
//this is required to implement the callback trait
26+
impl UnwindSafe for DeriveAdderParserCallback<'_> {}
27+
28+
impl ParseCallbacks for DeriveAdderParserCallback<'_> {
29+
fn add_derives(&self, _name: &str) -> Vec<String> {
30+
self.types_to_traits
31+
.get(_name)
32+
.unwrap_or(&Vec::<String>::new())
33+
.to_vec()
34+
}
35+
}

program/rust/src/c_oracle_header.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#![allow(non_upper_case_globals)]
2+
#![allow(non_camel_case_types)]
3+
#![allow(non_snake_case)]
4+
//we do not use all the variables in oracle.h, so this helps with the warnings
5+
#![allow(unused_variables)]
6+
#![allow(dead_code)]
7+
//All the custom trait imports should go here
8+
use borsh::{BorshDeserialize, BorshSerialize};
9+
//bindings.rs is generated by build.rs to include
10+
//things defined in bindings.h
11+
include!("../bindings.rs");

program/rust/src/lib.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,23 @@
1-
//c_oracle_header is auto generated by build_bpf.sh
2-
//to reflect the current status of oracle.h
31
mod c_oracle_header;
42
mod time_machine_types;
53

4+
//Below is a high lever description of the rust/c setup.
5+
6+
//As we migrate from C to Rust, our Rust code needs to be able to interract with C
7+
//build-bpf.sh is set up to compile the C code into a bpf archive file, that build.rs
8+
//is set up to link the rust targets to. This enables to interact with the c_entrypoint
9+
//as well as similarly declare other C functions in Rust and call them
10+
11+
//We also generate bindings for the types and constants in oracle.h (as well as other things
12+
//included in bindings.h), these bindings can be accessed through c_oracle_header.rs
13+
//Bindings allow us to access type definitions, function definitions and constants. In order to
14+
//add traits to the bindings, we use the parser in build.rs. The traits must be defined/included
15+
//at the the top of c_oracle_headers.rs. One of the most important traits we deal are the Borsh
16+
//serialization traits.
17+
18+
//the only limitation of our set up is that we can not unit test in rust, anything that calls
19+
//a c function. Though we can test functions that use constants/types defined in oracle.h
20+
621
//do not link with C during unit tests (which are built in native architecture, unlike libpyth.o)
722
#[cfg(target_arch = "bpf")]
823
#[link(name = "cpyth")]

scripts/build-bpf.sh

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ rm ./target/*-keypair.json
4040

4141
#build Rust and link it with C
4242
cd "${RUST_DIR}"
43-
cargo install bindgen
44-
bindgen ./src/bindings.h -o ./src/c_oracle_header.rs
4543
cargo clean
4644
cargo test
4745
cargo clean

0 commit comments

Comments
 (0)