diff --git a/Cargo.lock b/Cargo.lock index d71cb1e..fae5064 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,6 +69,25 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "flatbuffers" +version = "25.9.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b6620799e7340ebd9968d2e0708eb82cf1971e9a16821e2091b6d6e475eed5" +dependencies = [ + "bitflags", + "rustc_version", +] + +[[package]] +name = "flatc-rust" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57e61227926ef5b237af48bee74394cc4a5a221ebd10c5147a98e612f207851d" +dependencies = [ + "log", +] + [[package]] name = "getrandom" version = "0.3.4" @@ -256,6 +275,8 @@ dependencies = [ name = "protosol" version = "2.0.0" dependencies = [ + "flatbuffers", + "flatc-rust", "prost", "prost-build", ] @@ -304,6 +325,15 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.44" @@ -330,6 +360,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + [[package]] name = "syn" version = "1.0.109" diff --git a/Cargo.toml b/Cargo.toml index 987bdff..b70502e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,8 @@ links = "protosol" [build-dependencies] prost-build = "0.11.9" +flatc-rust = "0.2.0" [dependencies] prost = "0.11.9" +flatbuffers = "25.9.23" diff --git a/build.rs b/build.rs index b3bb799..60fff15 100644 --- a/build.rs +++ b/build.rs @@ -1,4 +1,8 @@ -use std::{env, fs, path::{Path, PathBuf}}; +extern crate flatc_rust; +use std::{ + env, fs, + path::{Path, PathBuf}, +}; /// Normalize path only on Windows, else just return unchanged. fn maybe_normalize_windows_path(path: &Path) -> PathBuf { @@ -18,34 +22,74 @@ fn maybe_normalize_windows_path(path: &Path) -> PathBuf { } } -fn main() -> Result<(), Box> { - let proto_dir = PathBuf::from("proto"); +fn monitor_and_get_files( + dir: &PathBuf, + env_var: &str, + extension: &str, +) -> Result<(Vec, PathBuf), Box> { let abs = env::current_dir() .expect("cwd") - .join(&proto_dir) + .join(dir) .canonicalize() .map(|p| maybe_normalize_windows_path(&p)) - .expect("canonicalize proto dir"); + .expect("canonicalize dir"); println!("cargo:rerun-if-changed={}", abs.display()); - println!("cargo:PROTO_DIR={}", abs.display()); + println!("cargo:{}={}", env_var, abs.display()); - let mut proto_files = vec![]; + let mut files = vec![]; for entry in fs::read_dir(&abs)? { let path = entry?.path(); - if path.extension().and_then(|e| e.to_str()) == Some("proto") { + if path.extension().and_then(|e| e.to_str()) == Some(extension) { println!("cargo:rerun-if-changed={}", path.display()); - proto_files.push(path); + files.push(path); } } + Ok((files, abs)) +} + +fn compile_protos() -> Result<(), Box> { + let proto_dir = PathBuf::from("proto"); + let (proto_files, abs) = monitor_and_get_files(&proto_dir, "PROTO_DIR", "proto")?; + let out_dir = PathBuf::from(env::var("OUT_DIR")?); let mut config = prost_build::Config::new(); config.out_dir(&out_dir); config.compile_protos( - &proto_files.iter().map(|p| p.display().to_string()).collect::>(), + &proto_files + .iter() + .map(|p| p.display().to_string()) + .collect::>(), &[abs], )?; Ok(()) } + +fn compile_flatbuffers() -> Result<(), Box> { + let flatbuffers_dir = PathBuf::from("flatbuffers"); + let (flatbuffers_files, abs) = + monitor_and_get_files(&flatbuffers_dir, "FLATBUFFERS_DIR", "fbs")?; + + let out_dir = PathBuf::from(env::var("OUT_DIR")?); + flatc_rust::run(flatc_rust::Args { + lang: "rust", + inputs: flatbuffers_files + .iter() + .map(|p| p.as_path()) + .collect::>() + .as_slice(), + out_dir: out_dir.as_path(), + includes: &[abs.as_path()], + ..Default::default() + })?; + + Ok(()) +} + +fn main() -> Result<(), Box> { + compile_protos()?; + compile_flatbuffers()?; + Ok(()) +} diff --git a/flatbuffers/block.fbs b/flatbuffers/block.fbs new file mode 100644 index 0000000..eaa33da --- /dev/null +++ b/flatbuffers/block.fbs @@ -0,0 +1,84 @@ +// Generated from block.proto + +include "metadata.fbs"; +include "context.fbs"; +include "txn.fbs"; + +namespace org.solana.sealevel.v2; + +table CostTracker { + block_cost:ulong; + vote_cost:ulong; +} + +table Inflation { + initial:double; + terminal:double; + taper:double; + foundation:double; + foundation_term:double; +} + +table FeeRateGovernor { + target_lamports_per_signature:ulong; + target_signatures_per_slot:ulong; + min_lamports_per_signature:ulong; + max_lamports_per_signature:ulong; + burn_percent:ubyte; +} + +table BankFields { + block_height:ulong; + poh:Hash (required); + parent_bank_hash:Hash (required); + parent_lthash:LtHash (required); + current_slot:ulong; + parent_slot:ulong; + prev_lps:ulong; + fee_rate_governor:FeeRateGovernor (required); + parent_signature_count:ulong; + hashes_per_tick:ulong; + ticks_per_slot:ulong; + slots_per_year:double; + inflation:Inflation (required); + genesis_creation_time:ulong; + prev_epoch_capitalization:ulong; + vote_accounts_t_1:[VoteAccount] (required); + vote_accounts_t_2:[VoteAccount] (required); +} + +struct LeaderScheduleHash { + hash:[ubyte:16]; +} + +table LeaderScheduleEffects { + leaders_epoch:ulong; + leaders_slot0:ulong; + leaders_slot_cnt:ulong; + leader_pub_cnt:ulong; + leaders_sched_cnt:ulong; + leader_schedule_hash:LeaderScheduleHash (required); +} + +table BlockContext { + transactions:[TransactionMessage] (required); + account_states:[Account] (required); + blockhash_queue:[Hash] (required); + bank_fields:BankFields (required); + features:FeatureSet; +} + +table BlockEffects { + has_err:bool; + slot_capitalization:ulong; + bank_hash:Hash; + cost_tracker:CostTracker; + leader_schedule:LeaderScheduleEffects; +} + +table BlockFixture { + metadata:FixtureMetadata (required); + input:BlockContext (required); + output:BlockEffects (required); +} + diff --git a/flatbuffers/context.fbs b/flatbuffers/context.fbs new file mode 100644 index 0000000..8c61227 --- /dev/null +++ b/flatbuffers/context.fbs @@ -0,0 +1,36 @@ +// Generated from context.proto + +namespace org.solana.sealevel.v2; + +struct Pubkey { + address:[ubyte:32]; +} + +struct Signature { + signature:[ubyte:64]; +} + +struct Hash { + hash:[ubyte:32]; +} + +struct LtHash { + hash:[ushort:1024]; +} + +table FeatureSet { + features:[ulong]; +} + +table Account { + address:Pubkey (required); + lamports:ulong; + data:[ubyte] (required); + executable:bool; + owner:Pubkey (required); +} + +table VoteAccount { + vote_account:Account (required); + stake:ulong; +} diff --git a/flatbuffers/elf.fbs b/flatbuffers/elf.fbs new file mode 100644 index 0000000..7e36ee9 --- /dev/null +++ b/flatbuffers/elf.fbs @@ -0,0 +1,28 @@ +// Generated from elf.proto + +include "metadata.fbs"; +include "context.fbs"; + +namespace org.solana.sealevel.v2; + +table ELFLoaderCtx { + elf_data:[ubyte] (required); + features:FeatureSet (required); + deploy_checks:bool; +} + +table ELFLoaderEffects { + err_code:ubyte; + rodata_hash:ulong; + text_cnt:ulong; + text_off:ulong; + entry_pc:ulong; + calldests_hash:ulong; +} + +table ELFLoaderFixture { + metadata:FixtureMetadata (required); + input:ELFLoaderCtx (required); + output:ELFLoaderEffects (required); +} + diff --git a/flatbuffers/instr.fbs b/flatbuffers/instr.fbs new file mode 100644 index 0000000..ab999b9 --- /dev/null +++ b/flatbuffers/instr.fbs @@ -0,0 +1,36 @@ +// Generated from invoke.proto + +include "metadata.fbs"; +include "context.fbs"; + +namespace org.solana.sealevel.v2; + +table InstrAccount { + index:ubyte; + is_writable:bool; + is_signer:bool; +} + +table InstrContext { + program_id:Pubkey (required); + account_states:[Account] (required); + instr_accounts:[InstrAccount] (required); + instr_data:[ubyte] (required); + cu_avail:ulong; + features:FeatureSet; +} + +table InstrEffects { + err_code:ubyte; + custom_err_code:uint; + modified_accounts:[Account]; + cu_remaining:ulong; + return_data:[ubyte]; +} + +table InstrFixture { + metadata:FixtureMetadata (required); + input:InstrContext (required); + output:InstrEffects (required); +} + diff --git a/flatbuffers/metadata.fbs b/flatbuffers/metadata.fbs new file mode 100644 index 0000000..92450aa --- /dev/null +++ b/flatbuffers/metadata.fbs @@ -0,0 +1,8 @@ +// Generated from metadata.proto + +namespace org.solana.sealevel.v2; + +table FixtureMetadata { + fn_entrypoint:string (required); +} + diff --git a/flatbuffers/txn.fbs b/flatbuffers/txn.fbs new file mode 100644 index 0000000..8ef15bb --- /dev/null +++ b/flatbuffers/txn.fbs @@ -0,0 +1,66 @@ +// Generated from txn.proto + +include "metadata.fbs"; +include "context.fbs"; + +namespace org.solana.sealevel.v2; + +table MessageHeader { + num_required_signatures:ubyte; + num_readonly_signed_accounts:ubyte; + num_readonly_unsigned_accounts:ubyte; +} + +table CompiledInstruction { + program_id_index:ubyte; + accounts:[ubyte] (required); + data:[ubyte] (required); +} + +table AddressLookupTable { + account_key:Pubkey (required); + writable_indexes:[ubyte] (required); + readonly_indexes:[ubyte] (required); +} + +table TransactionMessage { + is_legacy:bool; + header:MessageHeader (required); + account_keys:[Pubkey] (required); + recent_blockhash:Hash (required); + instructions:[CompiledInstruction] (required); + address_lookup_tables:[AddressLookupTable] (required); + message_hash:Hash (required); + signatures:[Signature] (required); +} + +table TxnContext { + txn_message:TransactionMessage (required); + account_states:[Account] (required); + blockhash_queue:[Hash] (required); + features:FeatureSet; +} + +table FeeDetails { + transaction_fee:ulong; + prioritization_fee:ulong; +} + +table TxnEffects { + txn_err_code:ubyte; + instr_err_code:ubyte; + instr_err_idx:ubyte; + custom_err_code:uint; + modified_accounts:[Account]; + return_data:[ubyte]; + executed_units:ulong; + fee_details:FeeDetails; + loaded_accounts_data_size:uint; +} + +table TxnFixture { + metadata:FixtureMetadata (required); + input:TxnContext (required); + output:TxnEffects (required); +} + diff --git a/flatbuffers/vm.fbs b/flatbuffers/vm.fbs new file mode 100644 index 0000000..2824bfd --- /dev/null +++ b/flatbuffers/vm.fbs @@ -0,0 +1,90 @@ +// Generated from vm.proto + +include "metadata.fbs"; +include "context.fbs"; +include "instr.fbs"; + +namespace org.solana.sealevel.v2; + +enum ErrKind : ubyte { + UNSPECIFIED = 0, + EBPF = 1, + SYSCALL = 2, + INSTRUCTION = 3, +} + +table InputDataRegion { + offset:ulong; + content:[ubyte] (required); + is_writable:bool; +} + +table VmContext { + heap_max:ulong; + rodata:[ubyte] (required); + rodata_text_section_offset:ulong; + rodata_text_section_length:ulong; + r0:ulong; + r1:ulong; + r2:ulong; + r3:ulong; + r4:ulong; + r5:ulong; + r6:ulong; + r7:ulong; + r8:ulong; + r9:ulong; + r10:ulong; + r11:ulong; + entry_pc:ulong; + calldests:[ubyte] (required); + return_data:ReturnData; + sbpf_version:ubyte; +} + +table ReturnData { + program_id:Pubkey (required); + data:[ubyte] (required); +} + +table SyscallInvocation { + function_name:string (required); + heap_prefix:[ubyte] (required); + stack_prefix:[ubyte] (required); +} + +table SyscallContext { + vm_ctx:VmContext (required); + instr_ctx:InstrContext (required); + syscall_invocation:SyscallInvocation (required); +} + +table SyscallEffects { + err_code:byte; + err_kind:ErrKind; + cu_avail:ulong; + heap:[ubyte]; + stack:[ubyte]; + input_data_regions:[InputDataRegion]; + frame_count:ulong; + log:string; + rodata:[ubyte]; + pc:ulong; + r0:ulong; + r1:ulong; + r2:ulong; + r3:ulong; + r4:ulong; + r5:ulong; + r6:ulong; + r7:ulong; + r8:ulong; + r9:ulong; + r10:ulong; +} + +table SyscallFixture { + metadata:FixtureMetadata (required); + input:SyscallContext (required); + output:SyscallEffects (required); +}