diff --git a/Cargo.lock b/Cargo.lock index aece6af21a..869e47591f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -597,6 +597,9 @@ name = "bitflags" version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +dependencies = [ + "serde", +] [[package]] name = "bitvec" @@ -2039,7 +2042,7 @@ dependencies = [ "rand 0.8.5", "regex", "reopen", - "rocksdb 0.23.0", + "rocksdb 0.24.0", "rust_decimal", "rust_decimal_macros", "serde", @@ -4503,6 +4506,26 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "platform-debug-utils" +version = "0.1.0" +dependencies = [ + "clap", + "dotenvy", + "dpp", + "drive-abci", + "envy", + "hex", + "rocksdb 0.24.0", + "ron", + "serde", + "serde_json", + "tenderdash-abci", + "textwrap", + "tracing", + "tracing-subscriber", +] + [[package]] name = "platform-serialization" version = "2.2.0-dev.1" @@ -5337,6 +5360,20 @@ dependencies = [ "librocksdb-sys", ] +[[package]] +name = "ron" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd490c5b18261893f14449cbd28cb9c0b637aebf161cd77900bfdedaff21ec32" +dependencies = [ + "bitflags 2.9.4", + "once_cell", + "serde", + "serde_derive", + "typeid", + "unicode-ident", +] + [[package]] name = "rpassword" version = "7.4.0" @@ -6188,6 +6225,12 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + [[package]] name = "socket2" version = "0.5.10" @@ -6431,6 +6474,7 @@ dependencies = [ "hex", "lhash", "semver", + "serde_json", "tenderdash-proto", "thiserror 2.0.16", "tokio", @@ -6524,6 +6568,17 @@ dependencies = [ "test-case-core", ] +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -7166,6 +7221,12 @@ dependencies = [ "utf-8", ] +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "typenum" version = "1.18.0" @@ -7193,6 +7254,12 @@ version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + [[package]] name = "unicode-normalization" version = "0.1.24" @@ -7202,6 +7269,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + [[package]] name = "unicode-xid" version = "0.2.6" diff --git a/Cargo.toml b/Cargo.toml index e60a2bd71d..ff47df0daa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ members = [ "packages/rs-dash-event-bus", "packages/rs-platform-wallet", "packages/wasm-sdk", + "packages/platform-debug-utils", ] [workspace.package] diff --git a/packages/platform-debug-utils/Cargo.toml b/packages/platform-debug-utils/Cargo.toml new file mode 100644 index 0000000000..bdae01755e --- /dev/null +++ b/packages/platform-debug-utils/Cargo.toml @@ -0,0 +1,41 @@ +[package] +version = "0.1.0" +rust-version.workspace = true +name = "platform-debug-utils" +edition = "2024" + +default-run = "replay_abci_requests" + +[dependencies] +clap = { version = "4.5.26", features = ["derive"] } +dpp = { path = "../rs-dpp", default-features = false, features = ["abci"] } +drive-abci = { path = "../rs-drive-abci" } +dotenvy = "0.15.7" +envy = "0.4.2" +hex = "0.4.3" +ron = "0.12" +rocksdb = "0.24.0" +serde = { version = "1.0.219", features = ["derive"] } +serde_json = { version = "1.0" } +tenderdash-abci = { git = "https://github.com/dashpay/rs-tenderdash-abci", tag = "v1.5.0-dev.2", features = [ + "grpc", + "serde", +] } +textwrap = "0.16" +tracing-subscriber = { version = "0.3.16", default-features = false, features = [ + "env-filter", + "ansi", + "fmt", + "std", + "registry", + "tracing-log", +] } +tracing = "0.1" + +[[bin]] +name = "rocksdb-dump" +path = "src/bin/rocksdb_dump.rs" + +[[bin]] +name = "replay_abci_requests" +path = "src/bin/replay_abci_requests.rs" diff --git a/packages/platform-debug-utils/README.md b/packages/platform-debug-utils/README.md new file mode 100644 index 0000000000..7240a159f9 --- /dev/null +++ b/packages/platform-debug-utils/README.md @@ -0,0 +1,9 @@ +# platform-debug-utils + +Small utilities that make inspecting Dash Platform state easier. Run any binary with `--help` to +see detailed usage and examples. + +- `rocksdb-dump`: export RocksDB or GroveDB contents into a diff-friendly text file grouped by + column family. +- `replay_abci_requests`: replay serialized RequestPrepareProposal / RequestProcessProposal payloads + against a GroveDB snapshot to inspect resulting app hashes and outcomes; see vectors/ for sample payloads. diff --git a/packages/platform-debug-utils/src/bin/replay_abci_requests.rs b/packages/platform-debug-utils/src/bin/replay_abci_requests.rs new file mode 100644 index 0000000000..a79c07fa3c --- /dev/null +++ b/packages/platform-debug-utils/src/bin/replay_abci_requests.rs @@ -0,0 +1,472 @@ +use clap::{Parser, ValueEnum}; +use dpp::block::extended_block_info::v0::ExtendedBlockInfoV0Getters; +use drive_abci::abci::app::FullAbciApplication; +use drive_abci::config::{FromEnv, PlatformConfig}; +use drive_abci::platform_types::platform::Platform; +use drive_abci::platform_types::platform_state::v0::PlatformStateV0Methods; +use drive_abci::rpc::core::DefaultCoreRPC; +use hex::ToHex; +use serde::de::DeserializeOwned; +use serde_json::{Map as JsonMap, Number as JsonNumber, Value as JsonValue}; +use std::error::Error; +use std::fmt; +use std::fs; +use std::path::{Path, PathBuf}; +use tenderdash_abci::Application; +use tenderdash_abci::proto::abci::{ + Request, RequestPrepareProposal, RequestProcessProposal, request, response_process_proposal, +}; +use textwrap::Options; +use tracing::field::{Field, Visit}; +use tracing::{Event, Subscriber}; +use tracing_subscriber::EnvFilter; +use tracing_subscriber::fmt::FmtContext; +use tracing_subscriber::fmt::format::{FormatEvent, FormatFields, Writer}; +use tracing_subscriber::registry::LookupSpan; + +const TRACE_WRAP_WIDTH: usize = 120; + +/// Replay helper for RequestPrepareProposal dumps. +#[derive(Debug, Parser)] +#[command( + name = "replay_abci_requests", + author, + version, + about = "Replay serialized ABCI requests against an existing GroveDB database.", + long_about = "Feed captured RequestPrepareProposal or RequestProcessProposal payloads (RON or JSON) \ +sequentially into the Drive ABCI application to recompute app hashes, inspect tx outcomes, and debug \ +state mismatches. Request files accept both the outer Request wrapper or the specific request type, \ +and configuration mirrors drive-abci's .env loading so you can point at the same RPC credentials. \ +\n\nExample:\n RUST_LOG=trace replay_abci_requests --db-path /path/to/grovedb --requests dump.ron \ +--config /path/to/.env --request-format ron\n\nUse multiple --requests flags to replay several inputs \ +in chronological order." +)] +struct Cli { + /// Path to the GroveDB database that should be used for execution. + /// You can use a command like `./state_backup.sh export --component abci abci.tar.gz testnet` + /// to dump the GroveDB database from existing Platform node. + #[arg(long, value_hint = clap::ValueHint::DirPath)] + db_path: PathBuf, + + /// Files that contain serialized Request*, RequestPrepareProposal, or RequestProcessProposal payloads. + /// They will be executed sequentially. + /// + /// See vectors/ directory for example request payloads. + #[arg(long, value_hint = clap::ValueHint::FilePath, required = true)] + requests: Vec, + + /// Optional .env file path. Defaults to walking up the filesystem like drive-abci. + /// .env file format is the same as used by drive-abci. + #[arg(short, long, value_hint = clap::ValueHint::FilePath)] + config: Option, + + /// Format of the serialized request payload. + #[arg(long, value_enum, default_value_t = RequestFormat::Ron)] + request_format: RequestFormat, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, ValueEnum)] +enum RequestFormat { + Json, + Ron, +} + +fn main() -> Result<(), Box> { + init_logging(); + let cli = Cli::parse(); + run(cli) +} + +fn run(cli: Cli) -> Result<(), Box> { + load_env(cli.config.as_deref())?; + + let mut config = match PlatformConfig::from_env() { + Ok(config) => config, + Err(drive_abci::error::Error::Configuration(envy::Error::MissingValue(field))) => { + return Err(format!("missing configuration option: {}", field.to_uppercase()).into()); + } + Err(err) => return Err(err.into()), + }; + + config.db_path = cli.db_path.clone(); + + let mut requests = Vec::new(); + for path in &cli.requests { + let loaded = load_request(path, cli.request_format)?; + tracing::info!( + "loaded {} request from {}: {:#?}", + loaded.kind(), + path.display(), + loaded + ); + requests.push((path.clone(), loaded)); + } + + if requests.is_empty() { + return Err("no request files provided".into()); + } + + let core_rpc = DefaultCoreRPC::open( + config.core.consensus_rpc.url().as_str(), + config.core.consensus_rpc.username.clone(), + config.core.consensus_rpc.password.clone(), + )?; + + let platform: Platform = + Platform::open_with_client(&config.db_path, Some(config.clone()), core_rpc, None)?; + log_last_committed_block(&platform); + + let app = FullAbciApplication::new(&platform); + + for (path, request) in requests { + match request { + LoadedRequest::Prepare(request) => { + let height = request.height; + tracing::info!("executing prepare_proposal from {}", path.display()); + let response = app.prepare_proposal(request).map_err(|err| { + format!("prepare_proposal failed for {}: {:?}", path.display(), err) + })?; + tracing::info!( + "prepare_proposal result ({}): height={}, app_hash=0x{}, tx_results={}, tx_records={}", + path.display(), + height, + response.app_hash.encode_hex::(), + response.tx_results.len(), + response.tx_records.len() + ); + } + LoadedRequest::Process(request) => { + tracing::info!("executing process_proposal from {}", path.display()); + let height = request.height; + let response = app.process_proposal(request).map_err(|err| { + format!("process_proposal failed for {}: {:?}", path.display(), err) + })?; + let status = response_process_proposal::ProposalStatus::try_from(response.status) + .unwrap_or(response_process_proposal::ProposalStatus::Unknown); + tracing::info!( + "process_proposal result ({}): status={:?}, height={}, app_hash=0x{}, tx_results={}, events={}", + path.display(), + status, + height, + hex::encode(response.app_hash), + response.tx_results.len(), + response.events.len() + ); + } + } + } + + Ok(()) +} + +#[derive(Debug)] +enum LoadedRequest { + Prepare(RequestPrepareProposal), + Process(RequestProcessProposal), +} + +impl LoadedRequest { + fn kind(&self) -> &'static str { + match self { + LoadedRequest::Prepare(_) => "prepare_proposal", + LoadedRequest::Process(_) => "process_proposal", + } + } +} + +fn load_request(path: &Path, format: RequestFormat) -> Result> { + let raw = fs::read_to_string(path)?; + + if let Ok(request) = parse_with::(&raw, format) { + return match request.value { + Some(request::Value::PrepareProposal(value)) => Ok(LoadedRequest::Prepare(value)), + Some(request::Value::ProcessProposal(value)) => Ok(LoadedRequest::Process(value)), + Some(other) => Err(format!( + "expected Request::PrepareProposal or Request::ProcessProposal but file contains {}", + other.variant_name() + ) + .into()), + None => Err("request payload does not contain a value".into()), + }; + } + + parse_with::(&raw, format) + .map(LoadedRequest::Prepare) + .or_else(|_| parse_with::(&raw, format).map(LoadedRequest::Process)) +} + +fn log_last_committed_block(platform: &Platform) +where + C: drive_abci::rpc::core::CoreRPCLike, +{ + let platform_state = platform.state.load(); + if let Some(info) = platform_state.last_committed_block_info() { + let basic_info = info.basic_info(); + tracing::info!( + "last_committed_block: height={}, round={}, core_height={}, block_id_hash=0x{}", + basic_info.height, + info.round(), + basic_info.core_height, + hex::encode(info.block_id_hash()) + ); + } else { + tracing::info!("last_committed_block: None"); + } +} + +fn load_env(path: Option<&Path>) -> Result<(), Box> { + if let Some(path) = path { + dotenvy::from_path(path)?; + return Ok(()); + } + + match dotenvy::dotenv() { + Ok(_) => Ok(()), + Err(err) if err.not_found() => { + tracing::warn!("warning: no .env file found"); + Ok(()) + } + Err(err) => Err(err.into()), + } +} + +fn parse_with(raw: &str, format: RequestFormat) -> Result> +where + T: DeserializeOwned, +{ + match format { + RequestFormat::Json => Ok(serde_json::from_str(raw)?), + RequestFormat::Ron => Ok(ron::from_str(raw)?), + } +} + +fn init_logging() { + let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")); + let json_format = PrettyJsonEventFormat::new(TRACE_WRAP_WIDTH); + + let _ = tracing_subscriber::fmt() + .with_env_filter(env_filter) + .event_format(json_format) + .try_init(); +} + +struct PrettyJsonEventFormat { + width: usize, +} + +impl PrettyJsonEventFormat { + fn new(width: usize) -> Self { + Self { width } + } +} + +impl FormatEvent for PrettyJsonEventFormat +where + S: Subscriber + for<'a> LookupSpan<'a>, + N: for<'a> FormatFields<'a> + 'static, +{ + fn format_event( + &self, + ctx: &FmtContext<'_, S, N>, + writer: Writer<'_>, + event: &Event<'_>, + ) -> fmt::Result { + let mut field_visitor = JsonFieldsVisitor::default(); + event.record(&mut field_visitor); + let fields = field_visitor.finish(); + + let mut payload = JsonMap::new(); + payload.insert( + "level".into(), + JsonValue::String(event.metadata().level().to_string()), + ); + payload.insert( + "target".into(), + JsonValue::String(event.metadata().target().to_string()), + ); + + if let Some(file) = event.metadata().file() { + payload.insert("file".into(), JsonValue::String(file.to_string())); + } + + if let Some(line) = event.metadata().line() { + payload.insert("line".into(), JsonValue::Number(JsonNumber::from(line))); + } + + if !fields.is_empty() { + payload.insert("fields".into(), JsonValue::Object(fields)); + } + + let spans = collect_span_names(ctx); + if !spans.is_empty() { + payload.insert( + "spans".into(), + JsonValue::Array(spans.into_iter().map(JsonValue::String).collect()), + ); + } + + let json = + serde_json::to_string_pretty(&JsonValue::Object(payload)).map_err(|_| fmt::Error)?; + + write_wrapped(writer, &json, self.width) + } +} + +fn write_wrapped(mut writer: Writer<'_>, text: &str, width: usize) -> fmt::Result { + if text.is_empty() { + writer.write_char('\n')?; + return Ok(()); + } + + let mut remaining = text; + + while let Some(idx) = remaining.find('\n') { + let line = &remaining[..idx]; + emit_wrapped_line(&mut writer, line, width)?; + remaining = &remaining[idx + 1..]; + } + + if !remaining.is_empty() { + emit_wrapped_line(&mut writer, remaining, width)?; + } + + Ok(()) +} + +fn emit_wrapped_line(writer: &mut Writer<'_>, line: &str, width: usize) -> fmt::Result { + if line.is_empty() { + writer.write_char('\n')?; + return Ok(()); + } + + let indent_end = line + .char_indices() + .find(|(_, c)| !c.is_whitespace()) + .map(|(idx, _)| idx) + .unwrap_or_else(|| line.len()); + let (indent, content) = line.split_at(indent_end); + + if content.is_empty() { + writer.write_str(indent)?; + writer.write_char('\n')?; + return Ok(()); + } + + let indent_chars = indent.chars().count(); + let mut effective_width = width.saturating_sub(indent_chars); + if effective_width == 0 { + effective_width = 1; + } + + let wrap_options = Options::new(effective_width).break_words(false); + let wrapped = textwrap::wrap(content, &wrap_options); + for piece in wrapped { + if !indent.is_empty() { + writer.write_str(indent)?; + } + writer.write_str(piece.as_ref())?; + writer.write_char('\n')?; + } + + Ok(()) +} + +fn collect_span_names(ctx: &FmtContext<'_, S, N>) -> Vec +where + S: Subscriber + for<'a> LookupSpan<'a>, + N: for<'a> FormatFields<'a> + 'static, +{ + let mut names = Vec::new(); + let mut current = ctx.lookup_current(); + while let Some(span) = current { + names.push(span.name().to_string()); + current = span.parent(); + } + names.reverse(); + names +} + +#[derive(Default)] +struct JsonFieldsVisitor { + fields: JsonMap, +} + +impl JsonFieldsVisitor { + fn finish(self) -> JsonMap { + self.fields + } + + fn insert_value(&mut self, field: &Field, value: JsonValue) { + self.fields.insert(field.name().to_string(), value); + } +} + +impl Visit for JsonFieldsVisitor { + fn record_bool(&mut self, field: &Field, value: bool) { + self.insert_value(field, JsonValue::Bool(value)); + } + + fn record_i64(&mut self, field: &Field, value: i64) { + self.insert_value(field, JsonValue::Number(value.into())); + } + + fn record_u64(&mut self, field: &Field, value: u64) { + self.insert_value(field, JsonValue::Number(value.into())); + } + + fn record_i128(&mut self, field: &Field, value: i128) { + self.insert_value(field, JsonValue::String(value.to_string())); + } + + fn record_u128(&mut self, field: &Field, value: u128) { + self.insert_value(field, JsonValue::String(value.to_string())); + } + + fn record_f64(&mut self, field: &Field, value: f64) { + let json_value = JsonNumber::from_f64(value) + .map(JsonValue::Number) + .unwrap_or(JsonValue::Null); + self.insert_value(field, json_value); + } + + fn record_str(&mut self, field: &Field, value: &str) { + self.insert_value(field, JsonValue::String(value.to_owned())); + } + + fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) { + self.insert_value(field, JsonValue::String(value.to_string())); + } + + fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { + self.insert_value(field, JsonValue::String(format!("{value:?}"))); + } + + fn record_bytes(&mut self, field: &Field, value: &[u8]) { + self.insert_value(field, JsonValue::String(hex::encode(value))); + } +} +trait RequestVariantName { + fn variant_name(&self) -> &'static str; +} + +impl RequestVariantName for request::Value { + fn variant_name(&self) -> &'static str { + match self { + request::Value::Echo(_) => "Echo", + request::Value::Flush(_) => "Flush", + request::Value::Info(_) => "Info", + request::Value::InitChain(_) => "InitChain", + request::Value::Query(_) => "Query", + request::Value::CheckTx(_) => "CheckTx", + request::Value::ListSnapshots(_) => "ListSnapshots", + request::Value::OfferSnapshot(_) => "OfferSnapshot", + request::Value::LoadSnapshotChunk(_) => "LoadSnapshotChunk", + request::Value::ApplySnapshotChunk(_) => "ApplySnapshotChunk", + request::Value::PrepareProposal(_) => "PrepareProposal", + request::Value::ProcessProposal(_) => "ProcessProposal", + request::Value::ExtendVote(_) => "ExtendVote", + request::Value::VerifyVoteExtension(_) => "VerifyVoteExtension", + request::Value::FinalizeBlock(_) => "FinalizeBlock", + } + } +} diff --git a/packages/platform-debug-utils/src/bin/rocksdb_dump.rs b/packages/platform-debug-utils/src/bin/rocksdb_dump.rs new file mode 100644 index 0000000000..3bf451f39c --- /dev/null +++ b/packages/platform-debug-utils/src/bin/rocksdb_dump.rs @@ -0,0 +1,81 @@ +use clap::Parser; +use rocksdb::{IteratorMode, Options, DB}; +use std::{ + error::Error, + fs::File, + io::{self, BufWriter, Write}, + path::PathBuf, +}; + +/// Export RocksDB contents to a diff-friendly text format. +#[derive(Parser, Debug)] +#[command(author, version, about)] +struct Args { + /// Path to the RocksDB directory. + #[arg(long)] + db_path: PathBuf, + + /// Output file path. If omitted, the export is printed to STDOUT. + #[arg(long)] + output: Option, +} + +fn main() -> Result<(), Box> { + let args = Args::parse(); + + let mut options = Options::default(); + options.create_if_missing(false); + options.create_missing_column_families(false); + + let mut cf_names = DB::list_cf(&options, &args.db_path)?; + if cf_names.is_empty() { + return Err("database does not contain any column families".into()); + } + + cf_names.sort(); + + let db = DB::open_cf_for_read_only(&options, &args.db_path, cf_names.iter(), false)?; + + let writer: Box = match args.output { + Some(path) => Box::new(File::create(path)?), + None => Box::new(io::stdout()), + }; + + let mut writer = BufWriter::new(writer); + + writeln!( + writer, + "# RocksDB export from {}", + args.db_path.display() + )?; + + for (cf_index, cf_name) in cf_names.iter().enumerate() { + let cf_handle = db + .cf_handle(cf_name) + .ok_or_else(|| format!("column family {cf_name} not found"))?; + + if cf_index > 0 { + writeln!(writer)?; + } + + writeln!(writer, "# column_family={cf_name}")?; + + let iter = db.iterator_cf(cf_handle, IteratorMode::Start); + + for item in iter { + let (key, value) = item?; + let key_hex = hex::encode(&key); + let value_hex = hex::encode(&value); + + writeln!( + writer, + "cf={cf_name}\tkey=0x{key_hex}\tvalue=0x{value_hex}\tkey_len={}\tvalue_len={}", + key.len(), + value.len() + )?; + } + } + + writer.flush()?; + Ok(()) +} diff --git a/packages/platform-debug-utils/vectors/replay_abci_requests_prepare_proposal.ron b/packages/platform-debug-utils/vectors/replay_abci_requests_prepare_proposal.ron new file mode 100644 index 0000000000..55cfa454be --- /dev/null +++ b/packages/platform-debug-utils/vectors/replay_abci_requests_prepare_proposal.ron @@ -0,0 +1,20 @@ +RequestPrepareProposal( + max_tx_bytes: 0, + txs: [], + local_last_commit: Some(CommitInfo( + round: 0, + quorum_hash: [0, 0, 1, 4, 40, 157, 152, 198, 129, 2, 67, 167, 182, 13, 4, 36, 192, 180, 245, 230, 204, 2, 137, 192, 14, 141, 39, 81, 97, 0, 170, 234], + block_signature: [136, 2, 201, 44, 54, 184, 149, 90, 153, 51, 152, 3, 167, 159, 214, 100, 192, 253, 74, 105, 199, 160, 139, 57, 61, 11, 175, 9, 247, 241, 113, 116, 187, 243, 232, 214, 96, 34, 120, 212, 183, 148, 150, 10, 184, 239, 42, 164, 0, 67, 26, 227, 150, 71, 246, 22, 171, 141, 33, 189, 64, 155, 237, 128, 209, 238, 109, 243, 41, 41, 118, 242, 204, 134, 191, 163, 192, 67, 242, 162, 214, 90, 194, 229, 226, 60, 58, 188, 237, 27, 215, 17, 202, 44, 235, 191], + threshold_vote_extensions: [], + )), + misbehavior: [], + height: 86023, + time: Some("2025-01-14T12:50:40.902Z"), + next_validators_hash: [80, 142, 96, 154, 117, 162, 231, 122, 167, 239, 119, 189, 105, 164, 1, 23, 174, 229, 247, 198, 158, 41, 56, 80, 191, 227, 44, 203, 166, 248, 252, 22], + round: 0, + core_chain_locked_height: 1176913, + proposer_pro_tx_hash: [135, 7, 82, 52, 172, 71, 53, 59, 66, 187, 151, 206, 70, 51, 12, 182, 124, 212, 100, 140, 1, 240, 178, 57, 61, 126, 114, 155, 13, 103, 137, 24], + proposed_app_version: 7, + version: Some(Consensus(block: "14", app: "7")), + quorum_hash: [0, 0, 1, 4, 40, 157, 152, 198, 129, 2, 67, 167, 182, 13, 4, 36, 192, 180, 245, 230, 204, 2, 137, 192, 14, 141, 39, 81, 97, 0, 170, 234], +) diff --git a/packages/platform-debug-utils/vectors/replay_abci_requests_process_proposal.ron b/packages/platform-debug-utils/vectors/replay_abci_requests_process_proposal.ron new file mode 100644 index 0000000000..c6fa24a91d --- /dev/null +++ b/packages/platform-debug-utils/vectors/replay_abci_requests_process_proposal.ron @@ -0,0 +1,28 @@ +RequestProcessProposal( + txs: [], + proposed_last_commit: Some(CommitInfo( + round: 0, + quorum_hash: [0, 0, 1, 4, 40, 157, 152, 198, 129, 2, 67, 167, 182, 13, 4, 36, 192, 180, 245, 230, 204, 2, 137, 192, 14, 141, 39, 81, 97, 0, 170, 234], + block_signature: [136, 2, 201, 44, 54, 184, 149, 90, 153, 51, 152, 3, 167, 159, 214, 100, 192, 253, 74, 105, 199, 160, 139, 57, 61, 11, 175, 9, 247, 241, 113, 116, 187, 243, 232, 214, 96, 34, 120, 212, 183, 148, 150, 10, 184, 239, 42, 164, 0, 67, 26, 227, 150, 71, 246, 22, 171, 141, 33, 189, 64, 155, 237, 128, 209, 238, 109, 243, 41, 41, 118, 242, 204, 134, 191, 163, 192, 67, 242, 162, 214, 90, 194, 229, 226, 60, 58, 188, 237, 27, 215, 17, 202, 44, 235, 191], + threshold_vote_extensions: [], + )), + misbehavior: [], + hash: [3, 63, 85, 253, 115, 253, 54, 212, 92, 243, 30, 67, 55, 132, 175, 209, 136, 193, 149, 113, 201, 9, 8, 33, 201, 131, 128, 93, 198, 170, 0, 68], + height: 86023, + round: 0, + time: Some("2025-01-14T12:50:40.902Z"), + next_validators_hash: [80, 142, 96, 154, 117, 162, 231, 122, 167, 239, 119, 189, 105, 164, 1, 23, 174, 229, 247, 198, 158, 41, 56, 80, 191, 227, 44, 203, 166, 248, 252, 22], + core_chain_locked_height: 1176913, + core_chain_lock_update: Some(CoreChainLock( + core_block_height: 1176913, + core_block_hash: [6, 25, 159, 225, 140, 175, 234, 184, 189, 230, 148, 197, 188, 70, 62, 170, 38, 234, 147, 229, 98, 209, 50, 161, 179, 32, 61, 45, 194, 0, 0, 0], + signature: [149, 89, 203, 42, 173, 22, 235, 79, 164, 70, 8, 15, 122, 248, 125, 76, 142, 113, 219, 249, 70, 211, 148, 128, 157, 23, 31, 193, 160, 50, 166, 97, 132, 152, 223, 128, 255, 241, 158, 169, 32, 199, 226, 52, 66, 147, 253, 45, 9, 103, 177, 68, 193, 122, 52, 82, 60, 158, 18, 196, 48, 29, 57, 198, 147, 236, 35, 166, 225, 81, 176, 237, 158, 143, 135, 233, 128, 68, 125, 173, 172, 228, 32, 209, 131, 37, 174, 64, 128, 39, 165, 236, 225, 113, 82, 206], + )), + proposer_pro_tx_hash: [135, 7, 82, 52, 172, 71, 53, 59, 66, 187, 151, 206, 70, 51, 12, 182, 124, 212, 100, 140, 1, 240, 178, 57, 61, 126, 114, 155, 13, 103, 137, 24], + proposed_app_version: 7, + version: Some(Consensus( + block: "14", + app: "7", + )), + quorum_hash: [0, 0, 1, 4, 40, 157, 152, 198, 129, 2, 67, 167, 182, 13, 4, 36, 192, 180, 245, 230, 204, 2, 137, 192, 14, 141, 39, 81, 97, 0, 170, 234], +) diff --git a/packages/rs-drive-abci/Cargo.toml b/packages/rs-drive-abci/Cargo.toml index def4a93cae..0c708b1de4 100644 --- a/packages/rs-drive-abci/Cargo.toml +++ b/packages/rs-drive-abci/Cargo.toml @@ -104,7 +104,7 @@ bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "084 mockall = { version = "0.13" } # For tests of grovedb verify -rocksdb = { version = "0.23.0" } +rocksdb = { version = "0.24.0" } integer-encoding = { version = "4.0.0" } [features]