diff --git a/dash/src/blockdata/transaction/mod.rs b/dash/src/blockdata/transaction/mod.rs index 400b72f83a..a50f44ce78 100644 --- a/dash/src/blockdata/transaction/mod.rs +++ b/dash/src/blockdata/transaction/mod.rs @@ -653,6 +653,9 @@ impl Decodable for Transaction { if special_transaction_type == TransactionType::AssetUnlock { segwit = false; } + if special_transaction_type == TransactionType::QuorumCommitment { + segwit = false; + } if segwit { let segwit_flag = u8::consensus_decode_from_finite_reader(r)?; match segwit_flag { diff --git a/dash/src/blockdata/transaction/special_transaction/quorum_commitment.rs b/dash/src/blockdata/transaction/special_transaction/quorum_commitment.rs index 794d0d18ea..16450ec843 100644 --- a/dash/src/blockdata/transaction/special_transaction/quorum_commitment.rs +++ b/dash/src/blockdata/transaction/special_transaction/quorum_commitment.rs @@ -17,6 +17,7 @@ //! It is defined in DIP6 [dip-0006.md](https://github.com/dashpay/dips/blob/master/dip-0006.md). //! +use std::io::{Read, Write}; use crate::bls_sig_utils::{BLSPublicKey, BLSSignature}; use crate::consensus::{Decodable, Encodable, encode}; use crate::hash_types::{QuorumHash, QuorumVVecHash}; @@ -33,6 +34,7 @@ pub struct QuorumFinalizationCommitment { pub version: u16, pub llmq_type: u8, pub quorum_hash: QuorumHash, + pub quorum_index: Option, pub signers: Vec, pub valid_members: Vec, pub quorum_public_key: BLSPublicKey, @@ -47,6 +49,9 @@ impl QuorumFinalizationCommitment { let mut size = 2 + 1 + 32 + 48 + 32 + 96 + 96; size += VarInt(self.signers.len() as u64).len() + self.signers.len(); size += VarInt(self.valid_members.len() as u64).len() + self.valid_members.len(); + if self.version == 2 || self.version == 4 { + size += 2; + } size } } @@ -57,6 +62,11 @@ impl Encodable for QuorumFinalizationCommitment { len += self.version.consensus_encode(w)?; len += self.llmq_type.consensus_encode(w)?; len += self.quorum_hash.consensus_encode(w)?; + if let Some(q_index) = self.quorum_index { + if self.version == 2 || self.version == 4 { + len += q_index.consensus_encode(w)?; + } + } len += self.signers.consensus_encode(w)?; len += self.valid_members.consensus_encode(w)?; len += self.quorum_public_key.consensus_encode(w)?; @@ -72,8 +82,11 @@ impl Decodable for QuorumFinalizationCommitment { let version = u16::consensus_decode(r)?; let llmq_type = u8::consensus_decode(r)?; let quorum_hash = QuorumHash::consensus_decode(r)?; - let signers = Vec::::consensus_decode(r)?; - let valid_members = Vec::::consensus_decode(r)?; + let quorum_index = if version == 2 || version == 4 { Some(i16::consensus_decode(r)?) } else { None }; + let signers_count = read_compact_size(r)?; + let signers = read_fixed_bitset(r, signers_count as usize)?; + let valid_members_count = read_compact_size(r)?; + let valid_members = read_fixed_bitset(r, valid_members_count as usize)?; let quorum_public_key = BLSPublicKey::consensus_decode(r)?; let quorum_vvec_hash = QuorumVVecHash::consensus_decode(r)?; let quorum_sig = BLSSignature::consensus_decode(r)?; @@ -82,8 +95,9 @@ impl Decodable for QuorumFinalizationCommitment { version, llmq_type, quorum_hash, - signers, - valid_members, + quorum_index, + signers: signers.iter().map(|&b| b as u8).collect(), + valid_members: valid_members.iter().map(|&b| b as u8).collect(), quorum_public_key, quorum_vvec_hash, quorum_sig, @@ -130,6 +144,54 @@ impl Decodable for QuorumCommitmentPayload { } } +fn read_compact_size(r: &mut R) -> io::Result { + let mut marker = [0u8; 1]; + r.read_exact(&mut marker)?; + match marker[0] { + 0xFD => { + // Read the next 2 bytes as a little-endian u16 + let mut buf = [0u8; 2]; + r.read_exact(&mut buf)?; + Ok(u16::from_le_bytes(buf) as u64) + } + 0xFE => { + // Read the next 4 bytes as a little-endian u32 + let mut buf = [0u8; 4]; + r.read_exact(&mut buf)?; + Ok(u32::from_le_bytes(buf) as u64) + } + 0xFF => { + // Read the next 8 bytes as a little-endian u64 + let mut buf = [0u8; 8]; + r.read_exact(&mut buf)?; + Ok(u64::from_le_bytes(buf)) + } + value => { + // For values less than 253, the value is stored directly in the marker byte + Ok(value as u64) + } + } +} + +fn read_fixed_bitset(r: &mut R, size: usize) -> std::io::Result> { + // Calculate the number of bytes needed + let num_bytes = (size + 7) / 8; + let mut bytes = vec![0u8; num_bytes]; + + // Read bytes from the reader + r.read_exact(&mut bytes)?; + + // Unpack bits into a vector of bools + let mut bits = Vec::with_capacity(size); + for p in 0..size { + let byte = bytes[p / 8]; + let bit = (byte >> (p % 8)) & 1; + bits.push(bit != 0); + } + + Ok(bits) +} + #[cfg(test)] mod tests { use hashes::Hash; @@ -151,6 +213,7 @@ mod tests { version: 0, llmq_type: 0, quorum_hash: QuorumHash::all_zeros(), + quorum_index: None, signers: vec![1, 2, 3, 4, 5], valid_members: vec![6, 7, 8, 9, 0], quorum_public_key: BLSPublicKey::from([0; 48]),