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
45 changes: 45 additions & 0 deletions dash/src/blockdata/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -918,10 +918,17 @@ mod tests {

use super::*;
use crate::blockdata::constants::WITNESS_SCALE_FACTOR;
use crate::blockdata::transaction::special_transaction::provider_dissolve::ProviderDissolvePayload;
use crate::blockdata::transaction::special_transaction::provider_registration::CompactSignature;
use crate::blockdata::transaction::special_transaction::provider_update_share::ProviderUpdateSharePayload;
use crate::blockdata::transaction::special_transaction::provider_update_shared_registrar::ProviderUpdateSharedRegistrarPayload;
use crate::bls_sig_utils::BLSPublicKey;
use crate::consensus::encode::{deserialize, serialize};
use crate::hash_types::PubkeyHash;
use crate::internal_macros::hex;
use crate::network::message::{NetworkMessage, RawNetworkMessage};
use crate::test_utils::UNASSIGNED_SPECIAL_TX_TYPE;
use test_case::test_case;

#[test]
fn test_is_coinbase() {
Expand Down Expand Up @@ -1157,6 +1164,44 @@ mod tests {
assert_eq!(serialize(&decoded), bytes);
}

#[test_case(10, TransactionPayload::ProviderDissolvePayloadType(ProviderDissolvePayload {
version: 1,
pro_tx_hash: Txid::all_zeros(),
actor_index: 0,
signatures: vec![CompactSignature::from([1; 65])],
}); "ProDisTx")]
#[test_case(11, TransactionPayload::ProviderUpdateSharePayloadType(ProviderUpdateSharePayload {
version: 1,
pro_tx_hash: Txid::all_zeros(),
share_index: 0,
script_reward: ScriptBuf::new(),
inputs_hash: InputsHash::all_zeros(),
payload_sig: vec![1; 65],
}); "ProUpShareTx")]
#[test_case(12, TransactionPayload::ProviderUpdateSharedRegistrarPayloadType(
ProviderUpdateSharedRegistrarPayload {
version: 1,
pro_tx_hash: Txid::all_zeros(),
operator_public_key: BLSPublicKey::from([1; 48]),
voting_key_hash: PubkeyHash::all_zeros(),
inputs_hash: InputsHash::all_zeros(),
signatures: vec![CompactSignature::from([1; 65])],
}
); "ProUpSharedRegTx")]
fn shared_masternode_types_decode_typed(tx_type: u16, payload: TransactionPayload) {
let tx = Transaction {
special_transaction_payload: Some(payload),
..unknown_special_tx(vec![TxIn::default()])
};
let bytes = serialize(&tx);
assert_eq!(&bytes[2..4], &tx_type.to_le_bytes());

let decoded: Transaction = deserialize(&bytes).unwrap();
assert_eq!(decoded, tx);
assert_eq!(decoded.tx_type().to_u16(), tx_type);
assert_eq!(decoded.size(), bytes.len());
}

#[test]
fn tx_no_input_deserialization() {
let tx_bytes = hex_conservative::decode_to_vec(
Expand Down
63 changes: 58 additions & 5 deletions dash/src/blockdata/transaction/special_transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,28 @@ use bincode::{Decode, Encode};
use crate::blockdata::transaction::special_transaction::TransactionPayload::{
AssetLockPayloadType, AssetUnlockPayloadType,
ClassicalWithNonStandardVersionTypeBytesPayloadType, CoinbasePayloadType,
MnhfSignalPayloadType, ProviderRegistrationPayloadType, ProviderUpdateRegistrarPayloadType,
ProviderUpdateRevocationPayloadType, ProviderUpdateServicePayloadType,
QuorumCommitmentPayloadType, UnknownPayloadType,
MnhfSignalPayloadType, ProviderDissolvePayloadType, ProviderRegistrationPayloadType,
ProviderUpdateRegistrarPayloadType, ProviderUpdateRevocationPayloadType,
ProviderUpdateServicePayloadType, ProviderUpdateSharePayloadType,
ProviderUpdateSharedRegistrarPayloadType, QuorumCommitmentPayloadType, UnknownPayloadType,
};
use crate::blockdata::transaction::special_transaction::TransactionType::{
AssetLock, AssetUnlock, Classic, ClassicalWithNonStandardVersionTypeBytes, Coinbase,
MnhfSignal, ProviderRegistration, ProviderUpdateRegistrar, ProviderUpdateRevocation,
ProviderUpdateService, QuorumCommitment, Unknown,
MnhfSignal, ProviderDissolve, ProviderRegistration, ProviderUpdateRegistrar,
ProviderUpdateRevocation, ProviderUpdateService, ProviderUpdateShare,
ProviderUpdateSharedRegistrar, QuorumCommitment, Unknown,
};
use crate::blockdata::transaction::special_transaction::asset_lock::AssetLockPayload;
use crate::blockdata::transaction::special_transaction::asset_unlock::qualified_asset_unlock::AssetUnlockPayload;
use crate::blockdata::transaction::special_transaction::coinbase::CoinbasePayload;
use crate::blockdata::transaction::special_transaction::mnhf_signal::MnhfSignalPayload;
use crate::blockdata::transaction::special_transaction::provider_dissolve::ProviderDissolvePayload;
use crate::blockdata::transaction::special_transaction::provider_registration::ProviderRegistrationPayload;
use crate::blockdata::transaction::special_transaction::provider_update_registrar::ProviderUpdateRegistrarPayload;
use crate::blockdata::transaction::special_transaction::provider_update_revocation::ProviderUpdateRevocationPayload;
use crate::blockdata::transaction::special_transaction::provider_update_service::ProviderUpdateServicePayload;
use crate::blockdata::transaction::special_transaction::provider_update_share::ProviderUpdateSharePayload;
use crate::blockdata::transaction::special_transaction::provider_update_shared_registrar::ProviderUpdateSharedRegistrarPayload;
use crate::blockdata::transaction::special_transaction::quorum_commitment::QuorumCommitmentPayload;
use crate::consensus::encode::VarInt;
use crate::consensus::{Decodable, Encodable, encode};
Expand All @@ -56,10 +61,13 @@ pub mod asset_lock;
pub mod asset_unlock;
pub mod coinbase;
pub mod mnhf_signal;
pub mod provider_dissolve;
pub mod provider_registration;
pub mod provider_update_registrar;
pub mod provider_update_revocation;
pub mod provider_update_service;
pub mod provider_update_share;
pub mod provider_update_shared_registrar;
pub mod quorum_commitment;

/// Largest ECDSA signature a ProRegTx or ProUpRegTx payload is sized with before it is signed.
Expand Down Expand Up @@ -89,6 +97,12 @@ pub enum TransactionPayload {
AssetLockPayloadType(AssetLockPayload),
/// A wrapper for an Asset Unlock payload
AssetUnlockPayloadType(AssetUnlockPayload),
/// A wrapper for a Masternode Dissolve payload
ProviderDissolvePayloadType(ProviderDissolvePayload),
/// A wrapper for a Masternode Update Share payload
ProviderUpdateSharePayloadType(ProviderUpdateSharePayload),
/// A wrapper for a Masternode Update Shared Registrar payload
ProviderUpdateSharedRegistrarPayloadType(ProviderUpdateSharedRegistrarPayload),
/// A pseudo-payload that carries the raw `nTxType` u16 read from a pre-DIP-0002
/// transaction (`version == 0`). Older transactions on the chain were free to put
/// arbitrary bytes in the type slot; we keep the original value here so that
Expand All @@ -113,6 +127,9 @@ impl Encodable for TransactionPayload {
MnhfSignalPayloadType(p) => p.consensus_encode(w),
AssetLockPayloadType(p) => p.consensus_encode(w),
AssetUnlockPayloadType(p) => p.consensus_encode(w),
ProviderDissolvePayloadType(p) => p.consensus_encode(w),
ProviderUpdateSharePayloadType(p) => p.consensus_encode(w),
ProviderUpdateSharedRegistrarPayloadType(p) => p.consensus_encode(w),
ClassicalWithNonStandardVersionTypeBytesPayloadType(_) => Ok(0),
UnknownPayloadType(_, bytes) => {
w.write_all(bytes)?;
Expand All @@ -135,6 +152,9 @@ impl TransactionPayload {
MnhfSignalPayloadType(_) => MnhfSignal,
AssetLockPayloadType(_) => AssetLock,
AssetUnlockPayloadType(_) => AssetUnlock,
ProviderDissolvePayloadType(_) => ProviderDissolve,
ProviderUpdateSharePayloadType(_) => ProviderUpdateShare,
ProviderUpdateSharedRegistrarPayloadType(_) => ProviderUpdateSharedRegistrar,
ClassicalWithNonStandardVersionTypeBytesPayloadType(raw) => {
ClassicalWithNonStandardVersionTypeBytes(*raw)
}
Expand All @@ -154,6 +174,9 @@ impl TransactionPayload {
MnhfSignalPayloadType(p) => p.size(),
AssetLockPayloadType(p) => p.size(),
AssetUnlockPayloadType(p) => p.size(),
ProviderDissolvePayloadType(p) => p.size(),
ProviderUpdateSharePayloadType(p) => p.size(),
ProviderUpdateSharedRegistrarPayloadType(p) => p.size(),
// Pre-DIP-0002 transactions have no payload section on the wire.
ClassicalWithNonStandardVersionTypeBytesPayloadType(_) => 0,
UnknownPayloadType(_, bytes) => bytes.len(),
Expand Down Expand Up @@ -337,6 +360,13 @@ pub enum TransactionType {
AssetLock,
/// An Asset Unlock Transaction, used to withdraw credits from Dash Platform, by unlocking them
AssetUnlock,
/// A Masternode Dissolve Transaction, used to dissolve a masternode with a shared collateral
ProviderDissolve,
/// A Masternode Update Share Transaction, used to change the reward script of a collateral share
ProviderUpdateShare,
/// A Masternode Update Shared Registrar Transaction, used by all collateral shares to change
/// the operator and voting keys
ProviderUpdateSharedRegistrar,
/// A pre-DIP-0002 Classic transaction (`version == 0`) whose on-wire `nTxType`
/// bytes were non-zero. The wrapped value is the original u16 read from the wire,
/// which must be re-emitted verbatim during serialization to preserve the txid.
Expand All @@ -359,6 +389,11 @@ impl Debug for TransactionType {
MnhfSignal => write!(f, "MNHF Signal Transaction"),
AssetLock => write!(f, "Asset Lock Transaction"),
AssetUnlock => write!(f, "Asset Unlock Transaction"),
ProviderDissolve => write!(f, "Provider Dissolve Transaction"),
ProviderUpdateShare => write!(f, "Provider Update Share Transaction"),
ProviderUpdateSharedRegistrar => {
write!(f, "Provider Update Shared Registrar Transaction")
}
ClassicalWithNonStandardVersionTypeBytes(raw) => {
write!(f, "Classic Transaction (pre-DIP-0002, raw type bytes 0x{raw:04x})")
}
Expand All @@ -380,6 +415,9 @@ impl Display for TransactionType {
MnhfSignal => write!(f, "MNHF Signal"),
AssetLock => write!(f, "Asset Lock"),
AssetUnlock => write!(f, "Asset Unlock"),
ProviderDissolve => write!(f, "Provider Dissolve"),
ProviderUpdateShare => write!(f, "Provider Update Share"),
ProviderUpdateSharedRegistrar => write!(f, "Provider Update Shared Registrar"),
ClassicalWithNonStandardVersionTypeBytes(raw) => {
write!(f, "Classic (pre-DIP-0002, raw 0x{raw:04x})")
}
Expand All @@ -403,6 +441,9 @@ impl TryFrom<u16> for TransactionType {
7 => Ok(MnhfSignal),
8 => Ok(AssetLock),
9 => Ok(AssetUnlock),
10 => Ok(ProviderDissolve),
11 => Ok(ProviderUpdateShare),
12 => Ok(ProviderUpdateSharedRegistrar),
_ => Err(encode::Error::UnknownSpecialTransactionType(value)),
}
}
Expand Down Expand Up @@ -442,6 +483,9 @@ impl TransactionType {
MnhfSignal => 7,
AssetLock => 8,
AssetUnlock => 9,
ProviderDissolve => 10,
ProviderUpdateShare => 11,
ProviderUpdateSharedRegistrar => 12,
ClassicalWithNonStandardVersionTypeBytes(raw) => raw,
Unknown(raw) => raw,
}
Expand Down Expand Up @@ -484,6 +528,15 @@ impl TransactionType {
MnhfSignal => Some(MnhfSignalPayloadType(MnhfSignalPayload::consensus_decode(d)?)),
AssetLock => Some(AssetLockPayloadType(AssetLockPayload::consensus_decode(d)?)),
AssetUnlock => Some(AssetUnlockPayloadType(AssetUnlockPayload::consensus_decode(d)?)),
ProviderDissolve => {
Some(ProviderDissolvePayloadType(ProviderDissolvePayload::consensus_decode(d)?))
}
ProviderUpdateShare => Some(ProviderUpdateSharePayloadType(
ProviderUpdateSharePayload::consensus_decode(d)?,
)),
ProviderUpdateSharedRegistrar => Some(ProviderUpdateSharedRegistrarPayloadType(
ProviderUpdateSharedRegistrarPayload::consensus_decode(d)?,
)),
Unknown(raw) => Some(UnknownPayloadType(raw, Vec::<u8>::consensus_decode(d)?)),
})
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//
// This file is a part of rust-dashcore.
// SPDX-License-Identifier: CC0-1.0
// See the accompanying file LICENSE or https://creativecommons.org/publicdomain/zero/1.0
//

//! Dash Provider Dissolve Special Transaction (ProDisTx).
//!
//! Dissolves a masternode with a shared collateral, either unilaterally (one signature, by the
//! share at `actor_index`) or unanimously (one signature per share, in share order).
//! Defined in [dip-0187](https://github.com/dashpay/dips/pull/187).
//!
//! The special transaction type used for ProDisTx Transactions is 10.

#[cfg(feature = "bincode")]
use bincode::{Decode, Encode};

use crate::blockdata::transaction::special_transaction::provider_registration::{
CompactSignature, decode_compact_signatures, encode_compact_signatures,
};
use crate::consensus::{Decodable, Encodable, encode};
use crate::hash_types::Txid;
use crate::io;
use crate::prelude::*;

/// A Provider Dissolve Payload used in a Provider Dissolve Special Transaction.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
#[cfg_attr(feature = "bincode", derive(Encode, Decode))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ProviderDissolvePayload {
pub version: u16,
pub pro_tx_hash: Txid,
/// Index of the share dissolving the masternode.
pub actor_index: u16,
/// One signature for a unilateral dissolution, one per share for a unanimous one.
pub signatures: Vec<CompactSignature>,
}

impl ProviderDissolvePayload {
/// Latest spec version of the ProDisTx payload.
pub const CURRENT_VERSION: u16 = 1;

/// The size of the payload in bytes.
pub fn size(&self) -> usize {
2 + 32 + 2 + 1 + 65 * self.signatures.len()
}
}

impl Encodable for ProviderDissolvePayload {
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
let mut len = 0;
len += self.version.consensus_encode(w)?;
len += self.pro_tx_hash.consensus_encode(w)?;
len += self.actor_index.consensus_encode(w)?;
len += encode_compact_signatures(&self.signatures, w)?;
Ok(len)
}
}

impl Decodable for ProviderDissolvePayload {
fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
Ok(ProviderDissolvePayload {
version: u16::consensus_decode(r)?,
pro_tx_hash: Txid::consensus_decode(r)?,
actor_index: u16::consensus_decode(r)?,
signatures: decode_compact_signatures(r)?,
})
}
}

#[cfg(test)]
mod tests {
use hashes::Hash;

use super::*;
use crate::consensus::{deserialize, serialize};

#[test]
fn layout() {
let payload = ProviderDissolvePayload {
version: 1,
pro_tx_hash: Txid::from_byte_array([0x11; 32]),
actor_index: 2,
signatures: vec![
CompactSignature::from([0x22; 65]),
CompactSignature::from([0x33; 65]),
],
};
let mut want = vec![0x01, 0x00];
want.extend([0x11; 32]);
want.extend([0x02, 0x00, 0x02]);
want.extend([0x22; 65]);
want.extend([0x33; 65]);

assert_eq!(serialize(&payload), want);
assert_eq!(payload.size(), want.len());
assert_eq!(deserialize::<ProviderDissolvePayload>(&want).unwrap(), payload);
}
}
Loading
Loading