Skip to content

Commit 1e80e75

Browse files
committed
Add ChainVariant::KRGN for Kerrigan multi-algo block headers
Kerrigan (KRGN) is a Dash fork with PIVX-style Sapling that mines four PoW algorithms concurrently (X11, KawPoW, Equihash(200,9), Equihash(192,7)). The mined algorithm is encoded at `(version >> 9) & 0x7` and selects a different header tail per block, so header size varies within a single batch (80, 124, 543, or 1487 bytes on mainnet). On `ChainVariant::Standard` this produces a `blockchain.block.headers` parse failure as soon as the batch crosses a non-X11 block — see the listing PR at GLEECBTC/coins#1791, failing at height 22313: `failed to parse list of 11 headers ... MalformedData`. The fix dispatches the per-algo tail inside `BlockHeader::deserialize` when `ChainVariant::KRGN` is set: - algo 0 (X11) — plain 80-byte path (already the default behaviour) - algo 1 (KawPoW) — reuses the existing RVN ProgPow tail reader (n_height u32, n_nonce_64 u64, mix_hash H256); one detail to note is that Kerrigan keeps the 80-byte prefix u32 nonce on-wire (zeroed), unlike RVN which omits it, so the u32 read path stays active for KRGN - algo 2 or 3 (Equihash) — Zcash-style layout: hash_final_sapling_root before time, U32 bits, H256 nonce, then the variable-length solution list. No new struct fields needed; the existing zcash-style branch handles the whole tail once `is_zcash_style` is extended. All four algos share the canonical 80-byte Bitcoin-shaped prefix for identity hashing (X11 over those 80 bytes), so Serializable round-trips without changes. ### Tests Six new tests in `mm2src/mm2_bitcoin/chain/src/block_header.rs`, all using real mainnet block headers fetched from `electrum-kerrigan.jskitty.cat` around the original failure range: - `test_krgn_chain_variant_try_from` — "KRGN" string parses to `ChainVariant::KRGN` - `test_krgn_x11_header_round_trip` — algo 0, height 22312 (80 bytes) - `test_krgn_kawpow_header_round_trip` — algo 1, height 22316 (124 bytes); asserts n_height/n_nonce_64/mix_hash are populated with the right on-chain values - `test_krgn_equihash_200_header_round_trip` — algo 2, height 22314 (1487 bytes); asserts sapling root + 1344-byte solution + H256 nonce - `test_krgn_equihash_192_header_round_trip` — algo 3, height 22313 (543 bytes); asserts 400-byte solution - `test_krgn_mixed_algo_header_batch` — concatenates one header of each of the four algos and walks the buffer with a single Reader. This reproduces the exact on-wire shape of a `blockchain.block.headers` response and is the strongest regression guard for the swap failure reported on coins#1791. Every fixture is round-tripped byte-for-byte (parse → serialize == original), so the dispatch provably preserves all bytes of the header. All 32 tests pass in `chain::block_header::tests` (6 new, 26 existing); `cargo fmt` and `cargo clippy -- -D warnings` are clean for the two touched crates (`chain`, `serialization`).
1 parent d56a7bc commit 1e80e75

2 files changed

Lines changed: 303 additions & 6 deletions

File tree

mm2src/mm2_bitcoin/chain/src/block_header.rs

Lines changed: 294 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,23 @@ impl Deserializable for BlockHeader {
236236
None
237237
};
238238

239+
// Kerrigan is a multi-algo chain: the mined algorithm is encoded at bits 9-11 of
240+
// the version field. Different algos produce different header tails, so we cache
241+
// the id here and use it to dispatch the per-algo sections below.
242+
// 0 = X11 (standard 80-byte Bitcoin header, no tail)
243+
// 1 = KawPoW (tail: n_height u32 + n_nonce u64 + mix_hash H256, like RVN)
244+
// 2 = Equihash(200,9), 3 = Equihash(192,7) — Zcash-style Sapling layout
245+
let krgn_algo: Option<u32> = if reader.chain_variant().is_krgn() {
246+
Some((version >> 9) & 0x7u32)
247+
} else {
248+
None
249+
};
250+
let is_krgn_equihash = matches!(krgn_algo, Some(2) | Some(3));
251+
let is_krgn_kawpow = matches!(krgn_algo, Some(1));
252+
239253
let is_zcash_style = (version == 4 && !reader.chain_variant().is_btc() && !reader.chain_variant().is_ppc())
240-
|| reader.chain_variant().is_kmd_assetchain();
254+
|| reader.chain_variant().is_kmd_assetchain()
255+
|| is_krgn_equihash;
241256
let mut hash_final_sapling_root = if is_zcash_style { Some(reader.read()?) } else { None };
242257
let time = reader.read()?;
243258
let bits = if is_zcash_style {
@@ -311,11 +326,16 @@ impl Deserializable for BlockHeader {
311326
};
312327

313328
// https://github.com/RavenProject/Ravencoin/blob/61c790447a5afe150d9892705ac421d595a2df60/src/primitives/block.h#L67
314-
let (n_height, n_nonce_u64, mix_hash) = if version == KAWPOW_VERSION && reader.chain_variant().is_rvn() {
315-
(Some(reader.read()?), Some(reader.read()?), Some(reader.read()?))
316-
} else {
317-
(None, None, None)
318-
};
329+
// Also used by Kerrigan KawPoW blocks: identical on-wire layout as RVN's KawPoW
330+
// tail (n_height u32, n_nonce u64, mix_hash H256), just guarded by different
331+
// version bits. Kerrigan keeps the 80-byte prefix nonce (zeroed) on the wire, so
332+
// unlike RVN we DID consume the u32 nonce above — only the tail fields differ.
333+
let (n_height, n_nonce_u64, mix_hash) =
334+
if (version == KAWPOW_VERSION && reader.chain_variant().is_rvn()) || is_krgn_kawpow {
335+
(Some(reader.read()?), Some(reader.read()?), Some(reader.read()?))
336+
} else {
337+
(None, None, None)
338+
};
319339

320340
Ok(BlockHeader {
321341
version,
@@ -2926,4 +2946,272 @@ mod tests {
29262946
let serialized = serialize(&header);
29272947
assert_eq!(serialized.take(), header_bytes);
29282948
}
2949+
2950+
// ── Kerrigan (KRGN) multi-algo block header tests ────────────────────────
2951+
//
2952+
// Kerrigan is a Dash fork with PIVX-style Sapling. It mines four PoW
2953+
// algorithms concurrently; the algo id lives at `(version >> 9) & 0x7u32` and
2954+
// selects a different header tail per block. The four fixtures below are
2955+
// real mainnet headers fetched from electrum-kerrigan.jskitty.cat around
2956+
// the `blockchain.block.headers` failure point (height 22313+) reported
2957+
// against `ChainVariant::Standard`. See:
2958+
// https://github.com/GLEECBTC/coins/pull/1791
2959+
2960+
/// Fixture: KRGN algo 0 (X11), height 22312. Standard 80-byte BTC header.
2961+
/// Field layout: version | prev | merkle | time | bits | nonce.
2962+
const KRGN_X11_HEADER_22312: &str = "\
2963+
00000020\
2964+
cecedd36fc1337bd11435021f61d3659bb3878adda6c32bfe7d03039d3323cb8\
2965+
9219536a41d8c300ee41722afa992ab97853c5793fa0f14965dafd62dd9cfaad\
2966+
4714dd69\
2967+
6eb8141a\
2968+
de037dfa";
2969+
2970+
/// Fixture: KRGN algo 1 (KawPoW), height 22316. 80-byte BTC prefix
2971+
/// (with the prefix u32 nonce zeroed on-wire) + 44-byte tail:
2972+
/// n_height (u32), n_nonce_64 (u64), mix_hash (H256).
2973+
const KRGN_KAWPOW_HEADER_22316: &str = "\
2974+
00020020\
2975+
2fdf79031346fae14cec946b538edfcdfe7e862c362b479153051ea03c18fe08\
2976+
83110049a44527184cfdb69384cc7f1ab5d79cc5385b97765e072431a708dbf1\
2977+
3b17dd69\
2978+
6ed4321b\
2979+
00000000\
2980+
2c570000\
2981+
c62f509228239162\
2982+
ec71a141c148176b53e852de723ec0a6fb8228ed9d18766bd554764a90cda7a4";
2983+
2984+
/// Fixture: KRGN algo 2 (Equihash(200,9)), height 22314. Zcash-style
2985+
/// header: hash_final_sapling_root before time, U32 bits, H256 nonce,
2986+
/// and a 1344-byte solution (compact-int length prefix `fd 40 05`).
2987+
const KRGN_EQUIHASH_200_HEADER_22314: &str = "\
2988+
00040020\
2989+
030604f488da6a2b8eabb6bf2b6ce56d9934fdc6deef9bed8762feb8fe70a4ba\
2990+
064f49dd64f3283ccf955b3f835e41a77c7d9f22556840a0a3df10196acb6f67\
2991+
0000000000000000000000000000000000000000000000000000000000000000\
2992+
9316dd69\
2993+
2963021d\
2994+
6000000a00000000000000000000000000003e0000000000000000000000df5c\
2995+
fd4005\
2996+
0077d885da4dfa9b1378024dcca2ee91e8e6bcee7609862bec42a6697940b452\
2997+
85642e26d967d9794f7f158cbf7ca885e464b7eb71d286c9d4c1351892fefe36\
2998+
66979381d59cc4dd5ac3eb77c884a285fb778d7a229e19793e0928398732a2cd\
2999+
7d50fb54bb99115a832ee44aa973a3bee3755eb3b031323b72e99d3ed86b2772\
3000+
5ba395d144d34525998af7f7238765cd7cf9d84f436da305566d6be85ac89e07\
3001+
fe135360157df72d0351e0b07acdaaa322aab11ab2dc29a1bdc07adbbf0c2a35\
3002+
fb40467d408a99b1528157437ea71c1ac7b408f6ad0a8b2dfa578d11b15e0135\
3003+
4aae8971354e682ba7b99433ef2e71c9bef3139b764777328b7fc34e0981bfd9\
3004+
dd9cd40b2ac2e1415c57cbe920dfbd41660ebdda9c17cdea44f4d44480ceef7b\
3005+
1739017c52d60db157d6eae8130dbd5fb5b4b2511f2af7243d0f335c92b71520\
3006+
99601988ea5770dcf8f735f47614aa8700f31597cfc7477e55786363aa59163a\
3007+
77db3fa8601c689d5a13f6f92bf1f284303ff6604a54025624d018aa7f917087\
3008+
7fa7a6b6b4273e7d4c8e7012bcd876386e3be8701ca8932566d438616e490d38\
3009+
148b712d04dcf20383488db92044e1c991dac4e5960fbe97bc26f90b276d537f\
3010+
d5a0918810df79f83a0e6d97c07b096ef84dcc5b55fbe064d5ca92dc807a09ff\
3011+
18acb91256be90935da8bf567da2432d65120330fafd175a032c3ead5214e42f\
3012+
7dc1733cd0dfe11a9ba496e099042408de85c7c131499541724fd93da2d9bdbb\
3013+
d1a015d926982d92a35913f7c2fd83e35841dff5b0e68017d955c2a6149a07da\
3014+
df33e443fb1d86cf5319b41d051c0713a14d79a5525ea3d48027b8c5c165dc34\
3015+
f305d7373554c61e5ae5dbf3b2c9fe3291552c9ea95308d963b7799aefc5afff\
3016+
20eee3b3cc48f5c60ca1763df616bc3a22bcb5a22d14598dcb249d74707be32e\
3017+
0181ccfa41c4e592763643310b2e5b4f111739939e14ee61b213ceb47f3b4401\
3018+
d72f5d442aeecfdbb3290756ad2f878d8101e86692dc07af71aa57aad555fb0d\
3019+
c60ebc8eafe7f7ac0a33791f7ea63124a3d70ed707510c66f3c3668d80b615ae\
3020+
4dcab935c0e6b170780df7d0ba7c3075239eeb3272219a3aad85021a32e60af6\
3021+
d9faff4a0de5b8cfa3fe6da1b43daf127e40a334b7d794890e6f5345e73a1967\
3022+
62e3f6f4bc9a3f05079e617e35c1edcf672d07edc3d885221f31fc0ef10ad7a0\
3023+
fa738fc3dcfec4428b8e2439f2083972b276113caeeba98a486ac029f29e67be\
3024+
d7929d4abc43a235276e4afe4ead6b6766f390bc7e4c2dc82a7b773f22a4acc6\
3025+
a1dc123ffcf13427826e2b61c8ad157eb24790461ad55e73035c32b7f30fe9c3\
3026+
3e2a33df33442ac4ae20099fac634dba547a16ac95fa2b74db9be03c9c1796cf\
3027+
670ffd83d6f4b783d9e1de2e62f9559d01a2452717905aac86a2a57a76bd1f45\
3028+
e0f95e118d136627bd21ab79f5e6d865029cf7c0296338b6f75620c2b3d19da0\
3029+
4fa35fd44246b8ea55a99e983eaa443bd54ba1bd62138b9534a410882d92cd87\
3030+
105bf8830576f56a99cf935cdd971236fe72ab36ceedfa77160669c957bf19ae\
3031+
f9a92697cb30f31a0eefe01cba660881b3fc7943acbab77bb501cf3b75a944da\
3032+
9d9d5a118b9cb4e484fa610453e1979dc706a947c48a712005ad370e658a5e29\
3033+
7e6bb48d8c57763d752a4e2f9f86e9aec12af5e01dbd720afd9a5faf6b243cf9\
3034+
8eac2d3d6dfdfa1a690b8eb6e3e00546a3f20982f37f5b33bf745286721da9a7\
3035+
20f4d1d15c915158733d9e7909b66a33d98731cdd41c22f5f8d3efb4bf062e16\
3036+
1915f0036e758d43fd570144afa6dd6d4661a739d4980bd43f9c780f20579b30\
3037+
b22c8eb9e8a5d817ba6df50fd77609e18789e1f7a6b4ce87da0ba156e3f9b63c";
3038+
3039+
/// Fixture: KRGN algo 3 (Equihash(192,7)), height 22313. Same Zcash-style
3040+
/// layout as Equihash(200,9) but with a shorter 400-byte solution.
3041+
const KRGN_EQUIHASH_192_HEADER_22313: &str = "\
3042+
00060020\
3043+
864a47125d16e571501cad908ca8d7f8c824e1414d8178079809000000000000\
3044+
41a42ad52ba9c797d4bdbec73f6283015a474735b868e46d6ce45426cb92dea4\
3045+
0000000000000000000000000000000000000000000000000000000000000000\
3046+
6815dd69\
3047+
e446011e\
3048+
c02cf51819836be40135d5f8728995f74d3d0b930000000000000000d1103020\
3049+
fd9001\
3050+
01dab64d2b6fe3b0dfdfc4f7601e523461d3d4bd2b7da4bbf63a45ffda27052d\
3051+
6adcd7d8ac382a3ebe370d725b7823ceb7a91c2d1ce6882056e01e7ac68db692\
3052+
a5a4a26ef6fed979b48d1a4e5e78f09d65d51f4c902c35f6ca42cf5d78c1d733\
3053+
0524185614df0f78d3e6df53569a4d1aa35e7306f84fd5665ce2c69664439dea\
3054+
a737de204372d1aa1a9479bba4d1d4ddaed13f4e03521c59067b361be41cdf74\
3055+
4652b26a0b3d4bbfe2021e9315a7a0494b25e493425eb4435a8b0e755569f42c\
3056+
1f016eae9fd7abdd08c4b79b4f4b02520c9d7d9de1b3889df5f5c28185bd9ff3\
3057+
4c2fc9ef9a519fcd84115b601fb36197dfa99693951cf3dbd82910d693790746\
3058+
62fc783a9fd7744fe8a39ee8f91bbaab5cef7e2d6eccad449d1942f43851b1d6\
3059+
df5e33e68146e83f55d9610d09ca6968f4b04af30a8f9fd3d0e3a3dcabef1272\
3060+
bbdbd44b30148edca805232c442c7cdfd8144b66af220b99dc954beb86f71e54\
3061+
10c0b1ec4e4508790b3c4383a3ccf782394249d323944f475de549f04f9dcf06\
3062+
b202e9f63713ae6af7663149edc27da2";
3063+
3064+
#[test]
3065+
fn test_krgn_chain_variant_try_from() {
3066+
use std::convert::TryFrom;
3067+
let cv = ChainVariant::try_from("KRGN").expect("KRGN variant must be recognised");
3068+
assert!(cv.is_krgn());
3069+
assert!(!cv.is_btc() && !cv.is_rvn() && !cv.is_pivx());
3070+
}
3071+
3072+
#[test]
3073+
fn test_krgn_x11_header_round_trip() {
3074+
let bytes: Vec<u8> = KRGN_X11_HEADER_22312.from_hex().unwrap();
3075+
let mut reader = Reader::new_with_chain_variant(bytes.as_slice(), ChainVariant::KRGN);
3076+
let header: BlockHeader = reader.read().unwrap();
3077+
3078+
assert_eq!(header.version, 0x20000000);
3079+
assert_eq!(header.time, 1_776_096_327);
3080+
assert!(matches!(header.bits, BlockHeaderBits::Compact(_)));
3081+
assert_eq!(header.nonce, BlockHeaderNonce::U32(0xfa7d03de));
3082+
assert!(header.solution.is_none(), "X11 headers have no Equihash solution");
3083+
assert!(header.n_height.is_none(), "X11 headers have no KawPoW tail");
3084+
assert!(header.hash_final_sapling_root.is_none());
3085+
3086+
let serialized = serialize(&header);
3087+
assert_eq!(serialized.take(), bytes, "round-trip must be byte-exact");
3088+
}
3089+
3090+
#[test]
3091+
fn test_krgn_kawpow_header_round_trip() {
3092+
let bytes: Vec<u8> = KRGN_KAWPOW_HEADER_22316.from_hex().unwrap();
3093+
assert_eq!(bytes.len(), 124, "KRGN KawPoW headers are exactly 124 bytes");
3094+
3095+
let mut reader = Reader::new_with_chain_variant(bytes.as_slice(), ChainVariant::KRGN);
3096+
let header: BlockHeader = reader.read().unwrap();
3097+
3098+
assert_eq!(header.version, 0x20000200);
3099+
assert_eq!((header.version >> 9) & 0x7, 1, "version encodes algo=1 (KawPoW)");
3100+
// Kerrigan keeps the 80-byte prefix u32 nonce on the wire (zeroed for KawPoW);
3101+
// this is the one observable difference from RVN KawPoW which omits it.
3102+
assert_eq!(header.nonce, BlockHeaderNonce::U32(0));
3103+
// KawPoW tail: n_height, n_nonce_64, mix_hash.
3104+
assert_eq!(header.n_height, Some(22316));
3105+
assert_eq!(header.n_nonce_u64, Some(0x6291_2328_9250_2fc6));
3106+
assert!(header.mix_hash.is_some());
3107+
assert!(header.solution.is_none());
3108+
3109+
let serialized = serialize(&header);
3110+
assert_eq!(serialized.take(), bytes, "round-trip must be byte-exact");
3111+
}
3112+
3113+
#[test]
3114+
fn test_krgn_equihash_200_header_round_trip() {
3115+
let bytes: Vec<u8> = KRGN_EQUIHASH_200_HEADER_22314.from_hex().unwrap();
3116+
assert_eq!(bytes.len(), 1487, "KRGN Equihash(200,9) headers are 1487 bytes");
3117+
3118+
let mut reader = Reader::new_with_chain_variant(bytes.as_slice(), ChainVariant::KRGN);
3119+
let header: BlockHeader = reader.read().unwrap();
3120+
3121+
assert_eq!(header.version, 0x20000400);
3122+
assert_eq!(
3123+
(header.version >> 9) & 0x7,
3124+
2,
3125+
"version encodes algo=2 (Equihash 200,9)"
3126+
);
3127+
assert!(matches!(header.bits, BlockHeaderBits::U32(_)), "Equihash uses U32 bits");
3128+
assert!(
3129+
matches!(header.nonce, BlockHeaderNonce::H256(_)),
3130+
"Equihash uses H256 nonce"
3131+
);
3132+
assert!(
3133+
header.hash_final_sapling_root.is_some(),
3134+
"Equihash has hashReserved / sapling root"
3135+
);
3136+
let sol = header.solution.as_ref().expect("Equihash must carry a solution");
3137+
assert_eq!(sol.len(), 1344, "Equihash(200,9) solutions are 1344 bytes");
3138+
assert!(header.n_height.is_none(), "Equihash headers have no KawPoW tail");
3139+
3140+
let serialized = serialize(&header);
3141+
assert_eq!(serialized.take(), bytes, "round-trip must be byte-exact");
3142+
}
3143+
3144+
#[test]
3145+
fn test_krgn_equihash_192_header_round_trip() {
3146+
let bytes: Vec<u8> = KRGN_EQUIHASH_192_HEADER_22313.from_hex().unwrap();
3147+
assert_eq!(bytes.len(), 543, "KRGN Equihash(192,7) headers are 543 bytes");
3148+
3149+
let mut reader = Reader::new_with_chain_variant(bytes.as_slice(), ChainVariant::KRGN);
3150+
let header: BlockHeader = reader.read().unwrap();
3151+
3152+
assert_eq!(header.version, 0x20000600);
3153+
assert_eq!(
3154+
(header.version >> 9) & 0x7,
3155+
3,
3156+
"version encodes algo=3 (Equihash 192,7)"
3157+
);
3158+
assert!(matches!(header.bits, BlockHeaderBits::U32(_)));
3159+
assert!(matches!(header.nonce, BlockHeaderNonce::H256(_)));
3160+
let sol = header.solution.as_ref().expect("Equihash must carry a solution");
3161+
assert_eq!(sol.len(), 400, "Equihash(192,7) solutions are 400 bytes");
3162+
3163+
let serialized = serialize(&header);
3164+
assert_eq!(serialized.take(), bytes, "round-trip must be byte-exact");
3165+
}
3166+
3167+
/// Reproduces the `blockchain.block.headers` batch parse that was failing
3168+
/// with `MalformedData` on `ChainVariant::Standard`. Concatenates one header
3169+
/// of each of the four Kerrigan algos in a single buffer and verifies the
3170+
/// reader consumes them sequentially, producing the right field shape for
3171+
/// each — which is exactly how an Electrum batch response is laid out.
3172+
#[test]
3173+
fn test_krgn_mixed_algo_header_batch() {
3174+
let mut buf: Vec<u8> = Vec::new();
3175+
for hex in [
3176+
KRGN_X11_HEADER_22312,
3177+
KRGN_KAWPOW_HEADER_22316,
3178+
KRGN_EQUIHASH_200_HEADER_22314,
3179+
KRGN_EQUIHASH_192_HEADER_22313,
3180+
] {
3181+
buf.extend_from_slice(&hex.from_hex::<Vec<u8>>().unwrap());
3182+
}
3183+
let total_len = buf.len();
3184+
3185+
let mut reader = Reader::new_with_chain_variant(buf.as_slice(), ChainVariant::KRGN);
3186+
let mut parsed: Vec<BlockHeader> = Vec::new();
3187+
while !reader.is_finished() {
3188+
parsed.push(reader.read().expect("batch parse must not fail on KRGN"));
3189+
}
3190+
assert_eq!(parsed.len(), 4, "exactly four headers in the fixture batch");
3191+
3192+
// Each parsed header should have the algo-specific field signature that
3193+
// matches its version bits — i.e. the deserializer actually dispatched
3194+
// correctly rather than silently skipping bytes.
3195+
assert_eq!((parsed[0].version >> 9) & 0x7, 0); // X11
3196+
assert!(parsed[0].solution.is_none());
3197+
assert!(parsed[0].n_height.is_none());
3198+
3199+
assert_eq!((parsed[1].version >> 9) & 0x7, 1); // KawPoW
3200+
assert!(parsed[1].n_height.is_some());
3201+
assert!(parsed[1].solution.is_none());
3202+
3203+
assert_eq!((parsed[2].version >> 9) & 0x7, 2); // Equihash(200,9)
3204+
assert_eq!(parsed[2].solution.as_ref().unwrap().len(), 1344);
3205+
3206+
assert_eq!((parsed[3].version >> 9) & 0x7, 3); // Equihash(192,7)
3207+
assert_eq!(parsed[3].solution.as_ref().unwrap().len(), 400);
3208+
3209+
// Re-serialize the whole batch and verify it matches the original buffer
3210+
// byte-for-byte: this is the strongest round-trip guarantee we can give.
3211+
let mut round_trip: Vec<u8> = Vec::with_capacity(total_len);
3212+
for h in &parsed {
3213+
round_trip.extend_from_slice(&serialize(h).take());
3214+
}
3215+
assert_eq!(round_trip, buf, "full batch round-trip must be byte-exact");
3216+
}
29293217
}

mm2src/mm2_bitcoin/serialization/src/reader.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ pub enum ChainVariant {
6767
MORTY,
6868
RVN,
6969
PIVX,
70+
/// Kerrigan: Dash fork with PIVX-style Sapling, multi-algo PoW (X11/KawPoW/Equihash).
71+
/// The mined algorithm is encoded in `(version >> 9) & 0x7` and selects a different
72+
/// header tail per block, so header size varies within a single batch.
73+
KRGN,
7074
}
7175

7276
impl ChainVariant {
@@ -92,6 +96,10 @@ impl ChainVariant {
9296
pub fn is_pivx(&self) -> bool {
9397
matches!(self, ChainVariant::PIVX)
9498
}
99+
100+
pub fn is_krgn(&self) -> bool {
101+
matches!(self, ChainVariant::KRGN)
102+
}
95103
}
96104

97105
impl TryFrom<&str> for ChainVariant {
@@ -107,6 +115,7 @@ impl TryFrom<&str> for ChainVariant {
107115
"MORTY" => Ok(ChainVariant::MORTY),
108116
"RVN" => Ok(ChainVariant::RVN),
109117
"PIVX" => Ok(ChainVariant::PIVX),
118+
"KRGN" => Ok(ChainVariant::KRGN),
110119
_ => Err(format!("Unknown chain variant: {}", value)),
111120
}
112121
}

0 commit comments

Comments
 (0)