Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions Cargo.lock

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

Empty file modified contrib/lint/lint_javascript.py
100644 → 100755
Empty file.
4 changes: 2 additions & 2 deletions docs/guide_rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,8 @@ Consistent conversion names tell the reader the cost and ownership semantics of

| Prefix | Cost | Ownership | Example |
| ------- | --------------------- | -------------- | ------------------------- |
| `as_` | Free | `&T` to `&U` | `KeyId::as_byte_array()` |
| `to_` | Allocates or computes | Borrows input | `KeyId::to_byte_array()` |
| `as_` | Free | `&T` to `&U` | `KeyId::as_bytes()` |
| `to_` | Allocates or computes | Borrows input | `Hash256::to_bytes()` |
| `into_` | Variable | Consumes input | `String::into_bytes()` |

- Implement `From<T>` for infallible conversions; the blanket impl provides `Into<T>` automatically, so we never implement `Into` directly
Expand Down
2 changes: 1 addition & 1 deletion pkgs/num/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ license = "MIT"

[features]
default = []
std = []
std = ["bitcoin-consensus-encoding/std"]
full = ["std", "serde"]
serde = ["dep:serde"]
_internal = []
Expand Down
3 changes: 3 additions & 0 deletions pkgs/p2p_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ serde = { version = "1", default-features = false, features = [
[dev-dependencies]
hex-conservative = "0.3"
hex-literal = "0.4"
json5 = "0.4"
rstest = "0.25"
serde = { version = "1", features = ["derive"] }
serde_json = "1"

[lints]
workspace = true
Expand Down
1,686 changes: 1,686 additions & 0 deletions pkgs/p2p_core/corpus/mnlistdiff.json5

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions pkgs/p2p_core/src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use core::fmt;

pub use dash_primitives::codec::{BufferDecoder, VecEncoder};

/// Maximum buffered P2P message payload (4 MB).
pub(crate) const MAX_P2P_PAYLOAD: usize = 4_000_000;
/// Maximum buffered P2P message payload (3 MiB).
pub(crate) const MAX_P2P_PAYLOAD: usize = 3_145_728;

/// Encodes a `usize` as a Bitcoin-style CompactSize integer.
pub(crate) fn encode_compact_size(value: usize, buf: &mut Vec<u8>) {
Expand Down
5 changes: 1 addition & 4 deletions pkgs/p2p_core/src/primitives/governance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,6 @@ impl dash_types::TryFromUint<u32> for VoteSignal {
}
}

/// Maximum governance object data length (16 KiB).
const MAX_GOV_DATA: usize = 16 * 1024;

/// A governance object (proposal or superblock trigger).
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
Expand Down Expand Up @@ -193,7 +190,7 @@ impl GovernanceObject {
hash: outpoint_hash,
index: outpoint_n,
};
let data_len = wire::read_compact_size(sl, MAX_GOV_DATA)?;
let data_len = wire::read_compact_size(sl, MAX_P2P_PAYLOAD)?;
let obj_data = wire::read_bytes(sl, data_len)?.to_vec();
let sig = BlsSignatureBytes(wire::read_array(sl)?);
Ok(Self {
Expand Down
39 changes: 17 additions & 22 deletions pkgs/p2p_core/src/primitives/mn_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,18 @@ pub struct SimplifiedMnListEntry {
}

impl SimplifiedMnListEntry {
/// Decodes an entry using the enclosing diff's version to gate
/// conditional fields.
pub(crate) fn decode_versioned(sl: &mut &[u8], diff_version: u16) -> Result<Self, WireDecodeError> {
let version = if diff_version >= 2 { wire::read_u16_le(sl)? } else { 0 };
/// Decodes an entry from the wire format.
pub(crate) fn decode(sl: &mut &[u8]) -> Result<Self, WireDecodeError> {
let version = wire::read_u16_le(sl)?;
let pro_reg_tx_hash = TxHash::from_bytes(wire::read_array(sl)?);
let confirmed_hash = BlockHash::from_bytes(wire::read_array(sl)?);
let service = wire::read_cservice(sl)?;
let operator_key = BlsPublicKeyBytes(wire::read_array(sl)?);
let voting_key_id = KeyId(wire::read_array(sl)?);
let is_valid = wire::read_bool(sl)?;

let mn_type = if diff_version >= 2 {
// nType is gated by the entry's version
let mn_type = if version >= 2 {
MnType::from_u16(wire::read_u16_le(sl)?)
} else {
MnType::Regular
Expand Down Expand Up @@ -96,27 +96,22 @@ impl SimplifiedMnListEntry {
})
}

/// Encodes this entry for the given diff version.
pub(crate) fn encode_versioned(&self, diff_version: u16, buf: &mut Vec<u8>) {
if diff_version >= 2 {
buf.extend_from_slice(&self.version.to_le_bytes());
}
/// Encodes this entry to the wire format.
pub(crate) fn encode(&self, buf: &mut Vec<u8>) {
buf.extend_from_slice(&self.version.to_le_bytes());
buf.extend_from_slice(&self.pro_reg_tx_hash.to_bytes());
buf.extend_from_slice(&self.confirmed_hash.to_bytes());
buf.extend_from_slice(&self.service.addr);
buf.extend_from_slice(&self.service.port.to_be_bytes());
buf.extend_from_slice(&self.operator_key.0);
buf.extend_from_slice(&self.voting_key_id.0);
buf.push(u8::from(self.is_valid));
if diff_version >= 2 {
// nType and platform fields are gated by the entry's version
if self.version >= 2 {
buf.extend_from_slice(&self.mn_type.to_u16().to_le_bytes());
}
if self.mn_type == MnType::Evo {
if let Some(port) = self.platform_http_port {
buf.extend_from_slice(&port.to_le_bytes());
}
if let Some(ref nid) = self.platform_node_id {
buf.extend_from_slice(&nid.0);
if self.mn_type == MnType::Evo {
buf.extend_from_slice(&self.platform_http_port.unwrap_or(0).to_le_bytes());
buf.extend_from_slice(self.platform_node_id.as_ref().map_or(&[0u8; 20], |n| &n.0));
}
}
}
Expand Down Expand Up @@ -213,7 +208,7 @@ impl MnListDiffPayload {
let mn_count = wire::read_compact_size(sl, MAX_MN_LIST)?;
let mut mn_list = Vec::with_capacity(mn_count);
for _ in 0..mn_count {
mn_list.push(SimplifiedMnListEntry::decode_versioned(sl, version)?);
mn_list.push(SimplifiedMnListEntry::decode(sl)?);
}

let dq_count = wire::read_compact_size(sl, MAX_QUORUMS)?;
Expand All @@ -227,8 +222,8 @@ impl MnListDiffPayload {
let nq_count = wire::read_compact_size(sl, MAX_QUORUMS)?;
let mut new_quorums = Vec::with_capacity(nq_count);
for _ in 0..nq_count {
let commitment = encoding::decode_from_slice_unbounded::<Commitment>(sl)
.map_err(|e| WireDecodeError(alloc::format!("commitment decode: {e}")))?;
let commitment =
Commitment::decode_inner(sl).map_err(|e| WireDecodeError(alloc::format!("commitment decode: {e}")))?;
new_quorums.push(commitment);
}

Expand Down Expand Up @@ -286,7 +281,7 @@ impl MnListDiffPayload {

encode_compact_size(self.mn_list.len(), &mut buf);
for entry in &self.mn_list {
entry.encode_versioned(self.version, &mut buf);
entry.encode(&mut buf);
}

encode_compact_size(self.deleted_quorums.len(), &mut buf);
Expand Down
59 changes: 59 additions & 0 deletions pkgs/p2p_core/tests/mnlistdiff.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//
// Copyright (c) 2026-present, The Dash Core developers
// SPDX-License-Identifier: MIT
// See the accompanying file LICENSE or https://opensource.org/license/MIT
//

//! KAT tests for MnListDiffPayload.

#![expect(clippy::unwrap_used, reason = "test code")]

use dash_p2p_core::primitives::mn_list::MnListDiffPayload;

use std::collections::BTreeMap;

use bitcoin_consensus_encoding::{decode_from_slice, encode_to_vec};
use hex_conservative::FromHex;
use rstest::rstest;
use serde::Deserialize;

/// A single entry from the mnlistdiff corpus.
#[derive(Debug, Deserialize)]
struct CorpusEntry {
raw: String,
details: MnListDiffPayload,
}

/// Loads the mnlistdiff corpus file.
fn load_corpus() -> BTreeMap<String, CorpusEntry> {
let path = format!("{}/corpus/mnlistdiff.json5", env!("CARGO_MANIFEST_DIR"));
let text = std::fs::read_to_string(&path).unwrap();
let outer: BTreeMap<String, BTreeMap<String, CorpusEntry>> = json5::from_str(&text).unwrap();
outer.into_values().next().unwrap()
}

#[rstest]
fn decode_fields() {
let corpus = load_corpus();
for (block_hash, entry) in &corpus {
let bytes = Vec::<u8>::from_hex(&entry.raw).unwrap();
let decoded: MnListDiffPayload = decode_from_slice(&bytes).unwrap();

assert_eq!(decoded.block_hash.to_string(), *block_hash, "block_hash key mismatch");
assert_eq!(
decoded, entry.details,
"decoded payload != corpus details for {block_hash}"
);
}
}

#[rstest]
fn round_trip() {
let corpus = load_corpus();
for (block_hash, entry) in &corpus {
let bytes = Vec::<u8>::from_hex(&entry.raw).unwrap();
let decoded: MnListDiffPayload = decode_from_slice(&bytes).unwrap();
let encoded = encode_to_vec(&decoded);
assert_eq!(encoded, bytes, "round-trip mismatch for {block_hash}");
}
}
3 changes: 1 addition & 2 deletions pkgs/primitives/src/payload/assetunlock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ use crate::validation::DeploymentContext;
use crate::wire;
use crate::QuorumHash;

use dash_types::BlsSignatureBytes;

use core::fmt;

use bitcoin_consensus_encoding as encoding;
use dash_types::BlsSignatureBytes;

/// AssetUnlock: Platform-to-L1 (type 9).
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
Expand Down
6 changes: 2 additions & 4 deletions pkgs/primitives/src/payload/cbtx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,11 @@ use crate::validation::DeploymentContext;
use crate::wire;
use crate::MerkleRoot;

use dash_types::BlsSignatureBytes;

use bitcoin_units::BlockHeight;

use core::fmt;

use bitcoin_consensus_encoding as encoding;
use bitcoin_units::BlockHeight;
use dash_types::BlsSignatureBytes;

/// CoinbaseCommitment -- coinbase commitment payload.
///
Expand Down
3 changes: 1 addition & 2 deletions pkgs/primitives/src/payload/mnhftx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ use crate::validation::{DeploymentContext, VERSIONBITS_NUM_BITS};
use crate::wire;
use crate::QuorumHash;

use dash_types::BlsSignatureBytes;

use core::fmt;

use bitcoin_consensus_encoding as encoding;
use dash_types::BlsSignatureBytes;

/// MnHardFork -- hard-fork signal (type 7).
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
Expand Down
2 changes: 1 addition & 1 deletion pkgs/primitives/src/payload/proregtx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ impl ProRegTx {
}

if let Some(hash) = dash_script::p2pkh_hash160(payout) {
if hash == self.key_id_owner.as_byte_array() || hash == self.key_id_voting.as_byte_array() {
if hash == self.key_id_owner.as_bytes() || hash == self.key_id_voting.as_bytes() {
return Err(ProTxInvalid::PayoutKeyReuse);
}
}
Expand Down
6 changes: 2 additions & 4 deletions pkgs/primitives/src/payload/proupregtx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@

//! ProUpRegTx registrar-update payload (type 3).

use dash_script::KeyId;

use crate::error::DecodeError;
use crate::script::Script;
use crate::validation::{
Expand All @@ -16,12 +14,12 @@ use crate::validation::{
use crate::wire;
use crate::{InputsHash, TxHash};

use bitcoin_consensus_encoding as encoding;
use dash_script::KeyId;
use dash_types::BlsPublicKeyBytes;

use core::fmt;

use bitcoin_consensus_encoding as encoding;

/// Maximum owner ECDSA signature size.
const MAX_VCH_SIG_SIZE: usize = 256;

Expand Down
3 changes: 1 addition & 2 deletions pkgs/primitives/src/payload/prouprevtx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@ use crate::validation::{check_protx_version, max_protx_version_no_ext, Deploymen
use crate::wire;
use crate::{InputsHash, TxHash};

use dash_types::BlsSignatureBytes;

use core::fmt;

use bitcoin_consensus_encoding as encoding;
use dash_types::BlsSignatureBytes;

/// ProUpRevTx -- revoke a masternode (type 4).
///
Expand Down
3 changes: 1 addition & 2 deletions pkgs/primitives/src/payload/proupservtx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ use crate::validation::{
use crate::wire;
use crate::{InputsHash, TxHash};

use dash_types::{BlsSignatureBytes, PlatformNodeId};

use core::fmt;

use bitcoin_consensus_encoding as encoding;
use dash_types::{BlsSignatureBytes, PlatformNodeId};

/// ProUpServTx -- update MN service addr (type 2).
///
Expand Down
3 changes: 1 addition & 2 deletions pkgs/primitives/src/payload/quorum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@ use crate::support::{DynBitset, LlmqType};
use crate::wire;
use crate::{QuorumHash, QuorumVvecHash};

use dash_types::{BlsPublicKeyBytes, BlsSignatureBytes};

use core::fmt;

use bitcoin_consensus_encoding as encoding;
use dash_types::{BlsPublicKeyBytes, BlsSignatureBytes};

/// DKG session output for one LLMQ.
///
Expand Down
6 changes: 3 additions & 3 deletions pkgs/primitives/src/script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use core::fmt;

use bitcoin_consensus_encoding as encoding;

/// Maximum script size in bytes (consensus limit).
const MAX_SCRIPT_SIZE: usize = 10_000;
/// Maximum serialized object size (32 MiB).
const MAX_SIZE: usize = 0x0200_0000;

/// A variable-length script, CompactSize-prefixed on the wire.
#[derive(Clone, PartialEq, Eq, Hash, Default)]
Expand Down Expand Up @@ -89,7 +89,7 @@ pub struct ScriptDecoder(encoding::ByteVecDecoder);
impl ScriptDecoder {
/// Constructs a new decoder with the default script size limit.
pub const fn new() -> Self {
Self(encoding::ByteVecDecoder::new_with_limit(MAX_SCRIPT_SIZE))
Self(encoding::ByteVecDecoder::new_with_limit(MAX_SIZE))
}
}

Expand Down
3 changes: 1 addition & 2 deletions pkgs/primitives/src/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@
use crate::support::LlmqType;
use crate::tx_types::{MnType, TxType};

use dash_types::{AsUint, TryFromUint};

pub use dash_types::serialize::uint;
use dash_types::{AsUint, TryFromUint};

/// Serializes [`Amount`](bitcoin_units::Amount) as a `u64` (satoshis).
pub mod amount {
Expand Down
11 changes: 11 additions & 0 deletions pkgs/primitives/src/support.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,17 @@ impl<'de> serde::Deserialize<'de> for DynBitset {
required,
)));
}
let remainder = num_bits % 8;
if remainder != 0 {
let mask = !((1u8 << remainder) - 1);
if raw.data[required - 1] & mask != 0 {
return Err(serde::de::Error::custom(alloc::format!(
"DynBitset padding bits set in last byte: {:#04x} for {1} bits",
raw.data[required - 1],
raw.num_bits,
)));
}
}
Ok(Self {
num_bits: raw.num_bits,
data: raw.data,
Expand Down
6 changes: 3 additions & 3 deletions pkgs/script/src/key_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ pub struct KeyId(#[cfg_attr(feature = "serde", serde(with = "dash_types::seriali

impl KeyId {
/// Returns the inner byte array.
pub const fn to_byte_array(self) -> [u8; 20] {
pub const fn to_bytes(self) -> [u8; 20] {
self.0
}

/// Returns a reference to the inner byte array.
pub const fn as_byte_array(&self) -> &[u8; 20] {
/// Borrows the inner byte array.
pub const fn as_bytes(&self) -> &[u8; 20] {
&self.0
}

Expand Down
Loading
Loading