Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
8142b73
fix(drive): reuse existing platform node id during operator update (#…
pauldelucia Oct 29, 2025
a71b221
chore(release): update changelog and bump version to 2.1.3 (#2835)
QuantumExplorer Oct 29, 2025
077ec82
chore(dashmate)!: backport port conflicts with mainnet and testnet o…
lklimek Oct 29, 2025
4b268dc
test: try to reproduce non deterministic resync causing apphash misma…
lklimek Nov 17, 2025
9f83eec
chore: prepare_proposal_apphash updated
lklimek Nov 17, 2025
7f552f2
chore: prepare proposal apphash final
lklimek Nov 18, 2025
3a6a287
drive: bump rocksdb to 0.24
lklimek Nov 18, 2025
3db8943
chore: replay_abci_requests final binary
lklimek Nov 18, 2025
a3af485
Merge tag 'v2.1.3' into backport/2.1.3
lklimek Nov 18, 2025
5813a0b
Merge branch 'backport/2.1.3' into test/resync-apphash-mismatch
lklimek Nov 18, 2025
e11fb8d
chore: improve help
lklimek Nov 18, 2025
0f056cd
chore: replayer, most recent version
lklimek Nov 26, 2025
45dc854
feat(drive-abci): improved state verification in drive-abci verify
lklimek Nov 26, 2025
8b311e4
feat(replay): verify initial database on start
lklimek Nov 26, 2025
323193f
refactor: move replay to `drive-abci replay`
lklimek Nov 27, 2025
abaa8d5
chore: adjust .env.testnet to match testnet
lklimek Nov 27, 2025
282febe
Merge remote-tracking branch 'origin/v2.2-dev' into test/resync-appha…
lklimek Dec 5, 2025
9352a62
refactor: move finalize block apphash verification to separate pr
lklimek Dec 5, 2025
4750697
only 1 log file supported
lklimek Dec 5, 2025
d7960ec
chore: clippy
lklimek Dec 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions packages/rs-drive-abci/.env.testnet
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,21 @@ CORE_CHECK_TX_JSON_RPC_PASSWORD=password
INITIAL_CORE_CHAINLOCKED_HEIGHT=1243

# https://github.com/dashevo/dashcore-lib/blob/286c33a9d29d33f05d874c47a9b33764a0be0cf1/lib/constants/index.js#L42-L57
VALIDATOR_SET_QUORUM_TYPE=llmq_25_67
VALIDATOR_SET_QUORUM_SIZE=25
VALIDATOR_SET_QUORUM_TYPE=6
VALIDATOR_SET_QUORUM_SIZE=100
Comment on lines +36 to +37
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Search for quorum type enum or constant definitions to validate numeric mappings
rg -A 10 -B 2 'llmq_25_67|llmq_50_60|llmq_60_75|QUORUM_TYPE' --type rust --type toml

Repository: dashpay/platform

Length of output: 833


🏁 Script executed:

# Check for numeric quorum type constants and mappings
rg '(QUORUM_TYPE|quorum.*type).*[=:].*[0-6]' --type rust -A 3 -B 1 | head -100

Repository: dashpay/platform

Length of output: 6758


🏁 Script executed:

# Look for .env files to compare configurations across environments
git ls-files | grep -E '\.env'

Repository: dashpay/platform

Length of output: 550


🏁 Script executed:

# Check the git history and context of the .env.testnet file
git log --oneline -20 -- packages/rs-drive-abci/.env.testnet

Repository: dashpay/platform

Length of output: 150


🏁 Script executed:

# Search for documentation about quorum types in the repository
rg -i 'quorum.*type.*6|quorum.*type.*1|quorum.*type.*5' --type md --type rs -B 2 -A 2 | head -50

Repository: dashpay/platform

Length of output: 86


🏁 Script executed:

# Find QuorumType enum definition
rg 'enum QuorumType|pub enum QuorumType' -A 20

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

# Search for the specific quorum type names and their numeric equivalents
rg 'Llmq.*_67|Llmq.*_60|Llmq.*_75' -A 2 -B 2 | head -80

Repository: dashpay/platform

Length of output: 7173


🏁 Script executed:

# Read .env.mainnet to compare with .env.testnet
cat packages/rs-drive-abci/.env.mainnet

Repository: dashpay/platform

Length of output: 3135


🏁 Script executed:

# Read .env.testnet to see all the changes
cat packages/rs-drive-abci/.env.testnet

Repository: dashpay/platform

Length of output: 3113


🏁 Script executed:

# Search for numeric to string quorum mapping
rg 'from_str|parse.*quorum|quorum.*parse' -B 3 -A 5 --type-list | head -100

Repository: dashpay/platform

Length of output: 449


🏁 Script executed:

# Find QuorumType enum definition
find . -name "*.rs" -type f | xargs grep -l "enum QuorumType" | head -5

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

# Search for QuorumType definition and its variants
rg "enum QuorumType" -A 30

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

# Look for serialization or From/Into implementations for numeric codes
rg "impl.*From.*QuorumType|impl.*Into.*QuorumType" -A 10

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

# Search for the numeric constant definitions
rg "Llmq100_67.*=|Llmq400_60.*=|Llmq60_75.*=" -B 2 -A 2

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

# Check dashcore-lib reference mentioned in .env.testnet comment
rg "dashcore-lib" -B 2 -A 2

Repository: dashpay/platform

Length of output: 50374


🏁 Script executed:

# Search for QuorumType enum in all Rust files
find packages/rs-drive-abci -name "*.rs" -exec grep -l "enum QuorumType" {} \;

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

# Read the config.rs file to understand QuorumType parsing
head -300 packages/rs-drive-abci/src/config.rs | grep -A 50 "QuorumType"

Repository: dashpay/platform

Length of output: 1658


🏁 Script executed:

# Search for serialization or string parsing of quorum types
rg "from_str|parse" packages/rs-drive-abci/src/config.rs -B 3 -A 5

Repository: dashpay/platform

Length of output: 6426


🏁 Script executed:

# Check if there's any mapping documentation or conversion code
rg "llmq_100_67|llmq_400_60|llmq_60_75" packages/rs-drive-abci/src/ -B 2 -A 2

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

# Find QuorumType enum in dpp package
find packages -name "*.rs" -path "*/dpp/*" -exec grep -l "enum QuorumType" {} \;

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

# Search for QuorumType enum definition
rg "enum QuorumType" packages/ -A 25

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

# Look for numeric constant definitions for quorum types
rg "Llmq100_67|Llmq400_60|Llmq60_75" packages/ -B 1 -A 1

Repository: dashpay/platform

Length of output: 12967


🏁 Script executed:

# Check if there's documentation about the numeric mapping
rg "impl.*From.*QuorumType|QuorumType.*from" packages/ -A 10 | head -80

Repository: dashpay/platform

Length of output: 1841


🌐 Web query:

dashcore-lib quorum type constants LLMQ_100_67 LLMQ_400_60 LLMQ_60_75 numeric values

💡 Result:

Values:

  • LLMQ_100_67 = 4. [1]
  • LLMQ_400_60 = 2. [1]
  • LLMQ_60_75 = 5. [1]

Sources: Dash LLMQ types table. [1] (docs.dash.org)

Citations:


🏁 Script executed:

# Double-check the web results by looking at any Dash documentation references in the codebase
rg "LLMQ.*TYPE|quorum.*type" packages/rs-drive-abci/.env.testnet -A 1 -B 1

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

# Verify the mainnet configuration once more to compare
rg "VALIDATOR_SET_QUORUM_TYPE|CHAIN_LOCK_QUORUM_TYPE|INSTANT_LOCK_QUORUM_TYPE" packages/rs-drive-abci/.env.mainnet

Repository: dashpay/platform

Length of output: 168


🏁 Script executed:

# Check if there are any other test configurations that show the correct mappings
find packages -name ".env*" -type f -exec grep -H "QUORUM_TYPE" {} \;

Repository: dashpay/platform

Length of output: 691


The quorum type numeric mappings are incorrect; they do not match the Dash LLMQ specification.

According to the official Dash LLMQ documentation, the correct numeric codes are:

  • LLMQ_100_67 = 4 (currently: 6)
  • LLMQ_400_60 = 2 (currently: 1)
  • LLMQ_60_75 = 5 (currently: 5 ✓)

Update lines 36, 43, and 49 in .env.testnet:

  • Line 36: VALIDATOR_SET_QUORUM_TYPE=64
  • Line 43: CHAIN_LOCK_QUORUM_TYPE=12
  • Line 49: INSTANT_LOCK_QUORUM_TYPE=5 → keep as is

The other configuration changes (EPOCH_TIME_LENGTH_S=3600, CHAIN_ID=dash-testnet-51, quorum window adjustments) are intentional for the testnet environment and align properly with the test/resync focus of this PR.

🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 37-37: [UnorderedKey] The VALIDATOR_SET_QUORUM_SIZE key should go before the VALIDATOR_SET_QUORUM_TYPE key

(UnorderedKey)

🤖 Prompt for AI Agents
In packages/rs-drive-abci/.env.testnet around lines 36 to 49, the numeric
mappings for quorum types are incorrect per Dash LLMQ spec; update
VALIDATOR_SET_QUORUM_TYPE on line 36 from 6 to 4, update CHAIN_LOCK_QUORUM_TYPE
on line 43 from 1 to 2, and leave INSTANT_LOCK_QUORUM_TYPE on line 49 as 5 (no
change); ensure only those three lines are modified and other testnet-specific
settings remain untouched.

VALIDATOR_SET_QUORUM_WINDOW=24
VALIDATOR_SET_QUORUM_ACTIVE_SIGNERS=24
VALIDATOR_SET_QUORUM_ROTATION=false
VALIDATOR_SET_ROTATION_BLOCK_COUNT=64

CHAIN_LOCK_QUORUM_TYPE=llmq_50_60
CHAIN_LOCK_QUORUM_SIZE=50
CHAIN_LOCK_QUORUM_TYPE=1
CHAIN_LOCK_QUORUM_SIZE=400
CHAIN_LOCK_QUORUM_WINDOW=24
CHAIN_LOCK_QUORUM_ACTIVE_SIGNERS=24
CHAIN_LOCK_QUORUM_ROTATION=false

INSTANT_LOCK_QUORUM_TYPE=llmq_60_75
INSTANT_LOCK_QUORUM_SIZE=50
INSTANT_LOCK_QUORUM_TYPE=5
INSTANT_LOCK_QUORUM_SIZE=60
INSTANT_LOCK_QUORUM_WINDOW=288
INSTANT_LOCK_QUORUM_ACTIVE_SIGNERS=32
INSTANT_LOCK_QUORUM_ROTATION=true
Expand Down Expand Up @@ -77,9 +77,9 @@ MASTERNODE_REWARD_SHARES_SECOND_PUBLIC_KEY=02bf55f97f189895da29824781053140ee66b
WITHDRAWALS_MASTER_PUBLIC_KEY=027057cdf58628635ef7b75e6b6c90dd996a16929cd68130e16b9328d429e5e03a
WITHDRAWALS_SECOND_PUBLIC_KEY=022084d827fea4823a69aa7c8d3e02fe780eaa0ef1e5e9841af395ba7e40465ab6

EPOCH_TIME_LENGTH_S=788400
EPOCH_TIME_LENGTH_S=3600

CHAIN_ID=devnet
CHAIN_ID=dash-testnet-51
BLOCK_SPACING_MS=5000

TOKIO_CONSOLE_ENABLED=false
Expand Down
13 changes: 10 additions & 3 deletions packages/rs-drive-abci/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ license = "MIT"
[dependencies]
arc-swap = "1.7.0"
bincode = { version = "=2.0.0-rc.3", features = ["serde"] }
base64 = { version = "0.22.1", optional = true }
ciborium = { version = "0.2.2" }
chrono = "0.4.35"
serde = { version = "1.0.219", features = ["derive"] }
Expand Down Expand Up @@ -54,6 +55,11 @@ tracing-subscriber = { version = "0.3.16", default-features = false, features =
tenderdash-abci = { git = "https://github.com/dashpay/rs-tenderdash-abci", tag = "v1.5.0-dev.2", features = [
"grpc",
] }
time = { version = "0.3", optional = true, features = [
"macros",
"formatting",
"serde-human-readable",
] }

lazy_static = "1.4.0"
itertools = { version = "0.13" }
Expand All @@ -75,9 +81,9 @@ tokio = { version = "1.40", features = [
tokio-util = { version = "0.7" }
derive_more = { version = "1.0", features = ["from", "deref", "deref_mut"] }
async-trait = "0.1.77"
ron = { version = "0.12", optional = true }
console-subscriber = { version = "0.4", optional = true }
bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f", optional = true }

[dev-dependencies]
bs58 = { version = "0.5.0" }
base64 = "0.22.1"
Expand All @@ -104,7 +110,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]
Expand All @@ -113,7 +119,8 @@ mocks = ["mockall", "drive/fixtures-and-mocks", "bls-signatures"]
console = ["console-subscriber", "tokio/tracing"]
testing-config = []
grovedbg = ["drive/grovedbg"]

# `abci-server replay` command
replay = ["dep:ron", "dep:time", "tenderdash-abci/serde"]
[[bin]]
name = "drive-abci"
path = "src/main.rs"
Expand Down
16 changes: 12 additions & 4 deletions packages/rs-drive-abci/src/abci/handler/process_proposal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,11 @@ where
if let Some(block_execution_context) = block_execution_context_guard.as_mut() {
// We are already in a block, or in init chain.
// This only makes sense if we were the proposer unless we are at a future round
if block_execution_context.block_state_info().round() != (request.round as u32) {
let block_state_info = block_execution_context.block_state_info();
if block_state_info.round() != (request.round as u32) {
// We were not the proposer, and we should process something new
drop_block_execution_context = true;
} else if let Some(current_block_hash) =
block_execution_context.block_state_info().block_hash()
{
} else if let Some(current_block_hash) = block_state_info.block_hash() {
// There is also the possibility that this block already came in, but tenderdash crashed
// Now tenderdash is sending it again
if let Some(proposal_info) = block_execution_context.proposer_results() {
Expand Down Expand Up @@ -69,6 +68,15 @@ where
} else {
// We are getting a different block hash for a block of the same round
// This is a terrible issue
tracing::error!(
method = "process_proposal",
block_state_info = ?block_state_info,
"received a process proposal request twice with different hash for height {}/round {}: existing hash {:?}, new hash {:?}",
request.height,
request.round,
current_block_hash,
request.hash,
);
Err(Error::Abci(AbciError::BadRequest(
"received a process proposal request twice with different hash".to_string(),
)))?;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@ where
tracing::trace!(
method = "run_block_proposal_v0",
app_hash = hex::encode(root_hash),
block_hash = hex::encode(block_proposal.block_hash.unwrap_or_default()),
platform_state_fingerprint = hex::encode(
block_execution_context
.block_platform_state()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Fetches execution state from grovedb storage

use crate::error::execution::ExecutionError;
use crate::error::Error;
use crate::platform_types::platform::Platform;
Expand Down
2 changes: 1 addition & 1 deletion packages/rs-drive-abci/src/execution/storage/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
mod fetch_platform_state;
pub mod fetch_platform_state;
mod store_platform_state;
5 changes: 5 additions & 0 deletions packages/rs-drive-abci/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,10 @@ pub mod query;
/// Various utils
pub mod utils;

/// Replay captured ABCI requests against drive-abci
#[cfg(feature = "replay")]
pub mod replay;
/// Drive server
pub mod server;
/// Verification helpers
pub mod verify;
73 changes: 14 additions & 59 deletions packages/rs-drive-abci/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
//! Main server process for RS-Drive-ABCI
//!
//! RS-Drive-ABCI server starts a single-threaded server and listens to connections from Tenderdash.
#[cfg(feature = "replay")]
use drive_abci::replay::{self, ReplayArgs};
use drive_abci::verify::verify_grovedb;

use clap::{Parser, Subcommand};
use dapi_grpc::platform::v0::get_status_request;
use dapi_grpc::platform::v0::get_status_request::GetStatusRequestV0;
use dapi_grpc::platform::v0::platform_client::PlatformClient;
use dapi_grpc::tonic::transport::Uri;
use dpp::version::PlatformVersion;
use drive_abci::config::{FromEnv, PlatformConfig};
use drive_abci::core::wait_for_core_to_sync::v0::wait_for_core_to_sync_v0;
use drive_abci::logging::{LogBuilder, LogConfig, LogDestination, Loggers};
Expand All @@ -16,7 +18,6 @@ use drive_abci::platform_types::platform::Platform;
use drive_abci::rpc::core::DefaultCoreRPC;
use drive_abci::{logging, server};
use itertools::Itertools;
use std::fs::remove_file;
#[cfg(all(tokio_unstable, feature = "console"))]
use std::net::SocketAddr;
use std::path::PathBuf;
Expand Down Expand Up @@ -63,6 +64,11 @@ enum Commands {
/// Print current software version
#[command()]
Version,

/// Replay ABCI requests captured from drive-abci logs.
#[cfg(feature = "replay")]
#[command()]
Replay(ReplayArgs),
}

/// Server that accepts connections from Tenderdash, and
Expand Down Expand Up @@ -151,8 +157,13 @@ impl Cli {
}
Commands::Config => dump_config(&config)?,
Commands::Status => runtime.block_on(check_status(&config))?,
Commands::Verify => verify_grovedb(&config.db_path, true)?,
Commands::Verify => drive_abci::verify::run(&config, true)?,
Commands::Version => print_version(),
#[cfg(feature = "replay")]
Commands::Replay(args) => {
replay::run(config, args, cancel.clone()).map_err(|e| e.to_string())?;
return Ok(());
}
};

Ok(())
Expand Down Expand Up @@ -331,62 +342,6 @@ async fn check_status(config: &PlatformConfig) -> Result<(), String> {
.map_err(|e| format!("can't request status: {e}"))
}

/// Verify GroveDB integrity.
///
/// This function will execute GroveDB integrity checks if one of the following conditions is met:
/// - `force` is `true`
/// - file `.fsck` in `config.db_path` exists
///
/// After successful verification, .fsck file is removed.
fn verify_grovedb(db_path: &PathBuf, force: bool) -> Result<(), String> {
let fsck = PathBuf::from(db_path).join(".fsck");

if !force {
if !fsck.exists() {
return Ok(());
}
tracing::info!(
"found {} file, starting grovedb verification",
fsck.display()
);
}

let grovedb = drive::grovedb::GroveDb::open(db_path).expect("open grovedb");
//todo: get platform version instead of taking latest
let result = grovedb
.visualize_verify_grovedb(
None,
true,
true,
&PlatformVersion::latest().drive.grove_version,
)
.map_err(|e| e.to_string());

match result {
Ok(data) => {
for result in data {
tracing::warn!(?result, "grovedb verification")
}
tracing::info!("grovedb verification finished");

if fsck.exists() {
if let Err(e) = remove_file(&fsck) {
tracing::warn!(
error = ?e,
path =fsck.display().to_string(),
"grovedb verification: cannot remove .fsck file: please remove it manually to avoid running verification again",
);
}
}
Ok(())
}
Err(e) => {
tracing::error!("grovedb verification failed: {}", e);
Err(e)
}
}
}

/// Print current software version.
fn print_version() {
println!("{}", env!("CARGO_PKG_VERSION"));
Expand Down
Loading
Loading