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
16 changes: 12 additions & 4 deletions mm2src/coins/utxo/utxo_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3282,6 +3282,14 @@ fn rvn_mtp() {
assert_eq!(mtp, 1633946264);
}

#[test]
fn pivx_mtp() {
let electrum = electrum_client_for_test(&["electrum01.chainster.org:50001", "electrum02.chainster.org:50001"]);
let mtp =
block_on_f01(electrum.get_median_time_past(5014894, NonZeroU64::new(11).unwrap(), CoinVariant::PIVX)).unwrap();
assert_eq!(mtp, 1754356500);
}

#[test]
fn qtum_mtp() {
let electrum = electrum_client_for_test(&[
Expand Down Expand Up @@ -5729,13 +5737,13 @@ fn test_electrum_v14_block_hash() {
fn test_scan_and_deserialize_block_headers() {
// ========================== CONFIGURATION ==========================
/// The ticker of the coin to test (e.g., "NMC", "CHTA", "RVN").
const COIN_TICKER: &str = "NMC";
const COIN_TICKER: &str = "PIVX";
/// A list of active Electrum servers for the specified coin.
const ELECTRUM_URLS: &[&str] = &["nmc2.bitcoins.sk:57001", "nmc2.bitcoins.sk:57002"];
const ELECTRUM_URLS: &[&str] = &["electrum01.chainster.org:50001", "electrum02.chainster.org:50001"];
/// The block height to start scanning from.
const START_HEIGHT: u64 = 701614;
const START_HEIGHT: u64 = 4903982;
/// The block height to stop scanning at. Set to `None` to scan to the tip of the chain.
const END_HEIGHT: Option<u64> = Some(701616);
const END_HEIGHT: Option<u64> = Some(4913982);
/// The number of headers to fetch in a single RPC call.
const CHUNK_SIZE: u64 = 100;
// ===================================================================
Expand Down
49 changes: 41 additions & 8 deletions mm2src/mm2_bitcoin/chain/src/block_header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,15 +156,24 @@ impl Serializable for BlockHeader {
if let Some(claim) = &self.claim_trie_root {
s.append(claim);
}
if let Some(h) = &self.hash_final_sapling_root {
s.append(h);
};
// For Zcash-style headers, the sapling root is serialized before the time.
if self.solution.is_some() {
if let Some(h) = &self.hash_final_sapling_root {
s.append(h);
}
}
s.append(&self.time);
s.append(&self.bits);
// If a BTC header uses KAWPOW_VERSION, the nonce can't be zero
if !self.is_prog_pow() && (self.version != KAWPOW_VERSION || self.nonce != BlockHeaderNonce::U32(0)) {
s.append(&self.nonce);
}
// For PIVX-style headers, the sapling root is serialized after the nonce.
if self.solution.is_none() {
if let Some(h) = &self.hash_final_sapling_root {
s.append(h);
}
}
if let Some(sol) = &self.solution {
s.append_list(sol);
}
Expand Down Expand Up @@ -230,16 +239,16 @@ impl Deserializable for BlockHeader {
None
};

let is_zcash = (version == 4 && !reader.coin_variant().is_btc() && !reader.coin_variant().is_ppc())
let is_zcash_style = (version == 4 && !reader.coin_variant().is_btc() && !reader.coin_variant().is_ppc())
|| reader.coin_variant().is_kmd_assetchain();
let hash_final_sapling_root = if is_zcash { Some(reader.read()?) } else { None };
let mut hash_final_sapling_root = if is_zcash_style { Some(reader.read()?) } else { None };
let time = reader.read()?;
let bits = if is_zcash {
let bits = if is_zcash_style {
BlockHeaderBits::U32(reader.read()?)
Comment thread
mariocynicys marked this conversation as resolved.
} else {
BlockHeaderBits::Compact(reader.read()?)
};
let nonce = if is_zcash {
let nonce = if is_zcash_style {
BlockHeaderNonce::H256(reader.read()?)
} else if (version == KAWPOW_VERSION && reader.coin_variant().is_rvn())
|| (version == MTP_POW_VERSION && time >= PROG_POW_SWITCH_TIME)
Expand All @@ -248,7 +257,17 @@ impl Deserializable for BlockHeader {
} else {
BlockHeaderNonce::U32(reader.read()?)
};
let solution = if is_zcash { Some(reader.read_list()?) } else { None };
// A PIVX header is a standard header with a `hash_final_sapling_root` added after the nonce.
hash_final_sapling_root = if reader.coin_variant().is_pivx() {
Some(reader.read()?)
} else {
hash_final_sapling_root
};
let solution = if is_zcash_style {
Some(reader.read_list()?)
} else {
None
};

// https://en.bitcoin.it/wiki/Merged_mining_specification#Merged_mining_coinbase
let aux_pow = if (version & AUXPOW_VERSION_FLAG) != 0 {
Expand Down Expand Up @@ -2896,4 +2915,18 @@ mod tests {
let serialized = serialize(&header);
assert_eq!(serialized.take(), header_bytes);
}

#[test]
fn test_pivx_sapling_header() {
let header_hex = "0b000000097d36aeeb2585e6c08226f8f48cb91213708fcad603cb67be76efa5b3b31c0baf86a77624fd298be0f5a7b908d17d3d83edf8f681de2913b2584fb92380e152594229684411051b00000000c801eff496c2720766cdbf2ec20b5436b37350e2945f85a7feb8a4b4a12d4323";
let header_bytes = &header_hex.from_hex::<Vec<u8>>().unwrap() as &[u8];
let mut reader = Reader::new_with_coin_variant(header_bytes, CoinVariant::PIVX);
let header: BlockHeader = reader.read().unwrap();

// Sapling root must be present
assert!(header.hash_final_sapling_root.is_some());

let serialized = serialize(&header);
assert_eq!(serialized.take(), header_bytes);
}
}
7 changes: 7 additions & 0 deletions mm2src/mm2_bitcoin/serialization/src/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ pub enum CoinVariant {
/// Same reason as RICK.
MORTY,
RVN,
PIVX,
}

impl CoinVariant {
Expand All @@ -86,6 +87,10 @@ impl CoinVariant {
pub fn is_rvn(&self) -> bool {
matches!(self, CoinVariant::RVN)
}

pub fn is_pivx(&self) -> bool {
matches!(self, CoinVariant::PIVX)
}
}

fn ticker_matches(ticker: &str, with: &str) -> bool {
Expand All @@ -111,6 +116,8 @@ impl From<&str> for CoinVariant {
t if ticker_matches(t, "MORTY") => CoinVariant::MORTY,
// "RVN"
t if ticker_matches(t, "RVN") => CoinVariant::RVN,
// "PIVX"
t if ticker_matches(t, "PIVX") => CoinVariant::PIVX,
_ => CoinVariant::Standard,
}
}
Expand Down
Loading