Skip to content

Commit 46531a3

Browse files
Match Core generateblock multipath, ranged, and Expand errors
getScriptFromDescriptor throws Multipath before IsRange, both as -8, and Expand failure as -5 Cannot derive script without private keys. Those throws skip the address fallback. Co-authored-by: metaphorics <metaphorics@users.noreply.github.com>
1 parent 0f343ad commit 46531a3

6 files changed

Lines changed: 97 additions & 16 deletions

File tree

crates/rpc/src/handlers/mining.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -759,7 +759,10 @@ mod tests {
759759
};
760760
use parking_lot::Mutex;
761761

762-
use crate::handlers::util::{GENERATEBLOCK_INVALID_OUTPUT, descriptor_checksum};
762+
use crate::handlers::util::{
763+
GENERATEBLOCK_INVALID_OUTPUT, GENERATEBLOCK_MULTIPATH, GENERATEBLOCK_NEEDS_PRIVATE_KEYS,
764+
GENERATEBLOCK_RANGED, descriptor_checksum,
765+
};
763766

764767
struct FakeMiningControl {
765768
template: Mutex<Option<BlockTemplate>>,
@@ -2244,4 +2247,40 @@ mod tests {
22442247
assert_eq!(error.code(), RpcError::CORE_NOT_FOUND);
22452248
assert_eq!(error.to_string(), GENERATEBLOCK_INVALID_OUTPUT);
22462249
}
2250+
2251+
// CONTRACT: docs/contracts/external-api.md#API-28
2252+
#[test]
2253+
fn generateblock_rejects_multipath_before_ranged_like_core() {
2254+
let control = FakeMiningControl::with_template(sample_template());
2255+
let ctx = ctx_with_control(control);
2256+
let tpub = "tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B";
2257+
let multipath = generateblock(&ctx, &json!([format!("wpkh({tpub}/<0;1>/0)"), []]))
2258+
.err()
2259+
.unwrap_or_else(|| panic!("multipath descriptor must fail"));
2260+
assert_eq!(multipath.code(), RpcError::CORE_INVALID_PARAMETER);
2261+
assert_eq!(multipath.to_string(), GENERATEBLOCK_MULTIPATH);
2262+
let both = generateblock(&ctx, &json!([format!("wpkh({tpub}/<0;1>/*)"), []]))
2263+
.err()
2264+
.unwrap_or_else(|| panic!("multipath+ranged descriptor must fail as multipath"));
2265+
assert_eq!(both.code(), RpcError::CORE_INVALID_PARAMETER);
2266+
assert_eq!(both.to_string(), GENERATEBLOCK_MULTIPATH);
2267+
let ranged = generateblock(&ctx, &json!([format!("wpkh({tpub}/0/*)"), []]))
2268+
.err()
2269+
.unwrap_or_else(|| panic!("ranged descriptor must fail"));
2270+
assert_eq!(ranged.code(), RpcError::CORE_INVALID_PARAMETER);
2271+
assert_eq!(ranged.to_string(), GENERATEBLOCK_RANGED);
2272+
}
2273+
2274+
// CONTRACT: docs/contracts/external-api.md#API-28
2275+
#[test]
2276+
fn generateblock_rejects_hardened_xpub_like_core() {
2277+
let control = FakeMiningControl::with_template(sample_template());
2278+
let ctx = ctx_with_control(control);
2279+
let tpub = "tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B";
2280+
let error = generateblock(&ctx, &json!([format!("wpkh({tpub}/0h/0)"), []]))
2281+
.err()
2282+
.unwrap_or_else(|| panic!("hardened xpub must fail Expand"));
2283+
assert_eq!(error.code(), RpcError::CORE_NOT_FOUND);
2284+
assert_eq!(error.to_string(), GENERATEBLOCK_NEEDS_PRIVATE_KEYS);
2285+
}
22472286
}

crates/rpc/src/handlers/util.rs

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,9 @@ pub(crate) fn deriveaddresses(ctx: &Arc<Context>, params: &Value) -> Result<Valu
333333
fn descriptor_error(error: DescriptorError) -> RpcError {
334334
match error {
335335
DescriptorError::Range(message) => RpcError::InvalidParameter(message.to_owned()),
336+
DescriptorError::PrivateKeys => {
337+
RpcError::InvalidAddressOrKey(GENERATEBLOCK_NEEDS_PRIVATE_KEYS.to_owned())
338+
}
336339
DescriptorError::Parse(message) => RpcError::InvalidAddressOrKey(message),
337340
}
338341
}
@@ -532,13 +535,16 @@ enum DescriptorError {
532535
Parse(String),
533536
/// The derivation range does not match the descriptor.
534537
Range(&'static str),
538+
/// `Expand` needs a private key (hardened path from an xpub, …).
539+
PrivateKeys,
535540
}
536541

537542
impl core::fmt::Display for DescriptorError {
538543
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
539544
match self {
540545
Self::Parse(message) => write!(f, "{message}"),
541546
Self::Range(message) => write!(f, "{message}"),
547+
Self::PrivateKeys => write!(f, "{GENERATEBLOCK_NEEDS_PRIVATE_KEYS}"),
542548
}
543549
}
544550
}
@@ -972,17 +978,25 @@ fn strip_checksum(text: &str) -> &str {
972978
}
973979

974980
pub(crate) const GENERATEBLOCK_INVALID_OUTPUT: &str = "Error: Invalid address or descriptor";
981+
pub(crate) const GENERATEBLOCK_MULTIPATH: &str = "Multipath descriptor not accepted";
982+
pub(crate) const GENERATEBLOCK_RANGED: &str =
983+
"Ranged descriptor not accepted. Maybe pass through deriveaddresses first?";
984+
pub(crate) const GENERATEBLOCK_NEEDS_PRIVATE_KEYS: &str =
985+
"Cannot derive script without private keys";
975986

976987
/// Coinbase script for `generateblock`'s `output` argument (`API-05`).
977988
///
978989
/// CONTRACT: docs/contracts/external-api.md#API-26
990+
/// CONTRACT: docs/contracts/external-api.md#API-28
979991
pub(crate) fn generateblock_payout_script(
980992
text: &str,
981993
network: bitcoin::Network,
982994
) -> Result<Vec<u8>, RpcError> {
983995
match script_from_descriptor(text, network) {
984996
Ok(script) => Ok(script),
985-
Err(error @ DescriptorError::Range(_)) => Err(descriptor_error(error)),
997+
Err(error @ (DescriptorError::Range(_) | DescriptorError::PrivateKeys)) => {
998+
Err(descriptor_error(error))
999+
}
9861000
Err(_) => payout_script_from_address(text, network, GENERATEBLOCK_INVALID_OUTPUT),
9871001
}
9881002
}
@@ -1028,20 +1042,26 @@ fn script_from_descriptor(
10281042
let (descriptor, keys) =
10291043
MiniscriptDescriptor::<DescriptorPublicKey>::parse_descriptor(&secp, &checksummed)
10301044
.map_err(|error| DescriptorError::Parse(error.to_string()))?;
1031-
if descriptor.has_wildcard() || descriptor.is_multipath() {
1045+
if descriptor.is_multipath() {
1046+
return Err(multipath_descriptor_rejected());
1047+
}
1048+
if descriptor.has_wildcard() {
10321049
return Err(ranged_descriptor_rejected());
10331050
}
10341051
ensure_keys_match_network(&descriptor, network)?;
10351052
ensure_secret_keys_match_network(keys, network)?;
10361053
let derived = descriptor
10371054
.at_derivation_index(0)
1038-
.map_err(|error| DescriptorError::Parse(error.to_string()))?;
1055+
.map_err(|_| DescriptorError::PrivateKeys)?;
10391056
Ok(derived.script_pubkey().as_bytes().to_vec())
10401057
}
10411058

10421059
fn combo_payout_script(key: &str, network: bitcoin::Network) -> Result<Vec<u8>, DescriptorError> {
10431060
let combo = parse_combo_info(key, network)?;
1044-
if combo.is_range || combo.paths.len() != 1 {
1061+
if combo.paths.len() != 1 {
1062+
return Err(multipath_descriptor_rejected());
1063+
}
1064+
if combo.is_range {
10451065
return Err(ranged_descriptor_rejected());
10461066
}
10471067
let path = combo
@@ -1050,16 +1070,18 @@ fn combo_payout_script(key: &str, network: bitcoin::Network) -> Result<Vec<u8>,
10501070
.ok_or_else(|| DescriptorError::Parse("Invalid combo descriptor".into()))?;
10511071
let derived = path
10521072
.at_derivation_index(0)
1053-
.map_err(|error| DescriptorError::Parse(error.to_string()))?;
1073+
.map_err(|_| DescriptorError::PrivateKeys)?;
10541074
// Core's combo Expand emits P2PK first and generateblock uses scripts[0].
10551075
let pk = MiniscriptDescriptor::new_pk(combo_key(&derived)?);
10561076
Ok(pk.script_pubkey().as_bytes().to_vec())
10571077
}
10581078

1079+
fn multipath_descriptor_rejected() -> DescriptorError {
1080+
DescriptorError::Range(GENERATEBLOCK_MULTIPATH)
1081+
}
1082+
10591083
fn ranged_descriptor_rejected() -> DescriptorError {
1060-
DescriptorError::Range(
1061-
"Ranged descriptor not accepted. Maybe pass through deriveaddresses first?",
1062-
)
1084+
DescriptorError::Range(GENERATEBLOCK_RANGED)
10631085
}
10641086

10651087
fn descriptor_text_with_optional_checksum(text: &str) -> Result<String, DescriptorError> {

crates/rpc/src/registry.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ declare_rows! {
141141
"submitheader", SurfaceKind::Rpc, Status::Implemented, "", CORE_VERSION, "Header-only admission through the same tree path as inbound P2P headers. Decode failures are -22; missing previous or invalid headers are -25.", "0.4.0", Some(mining::submitheader);
142142
"prioritisetransaction", SurfaceKind::Rpc, Status::Implemented, "", CORE_VERSION, "Dummy (params[1]) must be 0 or null; fee_delta is params[2]. Non-zero dummy is Core -8. Pooled dust outputs are -8 except on regtest.", "0.4.0", Some(mining::prioritisetransaction);
143143
"generatetoaddress", SurfaceKind::Rpc, Status::Implemented, "", CORE_VERSION, "Assembles, solves, and submits n blocks paying the given address through the mining coordinator. A non-address is Core -5 Error: Invalid address.", "0.4.0", Some(mining::generatetoaddress);
144-
"generateblock", SurfaceKind::Rpc, Status::Implemented, "", CORE_VERSION, "Assembles and solves one block paying an address or descriptor from the listed mempool txids or raw txs in that order; third param is Core's submit flag. Unknown 64-hex txids are -5; raw-tx decode failures are -22. Invalid output is -5 Error: Invalid address or descriptor. Consensus failure before solve is -25 TestBlockValidity failed: {reason}.", "0.4.0", Some(mining::generateblock);
144+
"generateblock", SurfaceKind::Rpc, Status::Implemented, "", CORE_VERSION, "Assembles and solves one block paying an address or descriptor from the listed mempool txids or raw txs in that order; third param is Core's submit flag. Unknown 64-hex txids are -5; raw-tx decode failures are -22. Invalid output is -5 Error: Invalid address or descriptor. Consensus failure before solve is -25 TestBlockValidity failed: {reason}. Multipath is -8 Multipath descriptor not accepted; ranged is -8 Ranged descriptor not accepted…; Expand without private keys is -5 Cannot derive script without private keys.", "0.4.0", Some(mining::generateblock);
145145
"getnetworkhashps", SurfaceKind::Rpc, Status::Implemented, "", CORE_VERSION, "Estimated hashes/s over a caller-chosen lookback ending at a caller-chosen height; default lookback 120, height the applied tip.", "0.4.0", Some(mining::getnetworkhashps);
146146
"getprioritisedtransactions", SurfaceKind::Rpc, Status::Implemented, "", CORE_VERSION, "Projects the mempool's signed fee-delta overlay, including txids not currently pooled. modified_fee is satoshis like Core mining RPCs, not BTC.", "0.4.0", Some(mining::getprioritisedtransactions);
147147

docs/contracts/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ match a regression.
4646
| [chain-events.md](chain-events.md) | `EVT-01``EVT-05` | `ChainSnapshot`, `ChainEventHint`, `ChainEventPublisher`, `ConsumerCursor`, `UndoStore`/`DisconnectMarker`, `ChainChangeProof`: the seam between the apply path and reconciliation consumers | `crates/node/src/txindex_worker.rs` (first consumer); any index mirroring the applied chain | `crates/node/src/state.rs` tests `record_publishes_snapshot_and_hints_in_commit_order`, `record_drops_hints_when_channel_full`, `active_chain_snapshot_anchors_at_restored_tip_after_restart`; `crates/node/src/txindex_worker_recovery_tests.rs` tests `shallow_reorg_rewinds_to_common_ancestor_then_replays`, `tip_change_during_rebuild_converges_on_new_tip`; `crates/node/src/apply.rs` tests `a_clean_disconnect_leaves_no_in_flight_marker`, `chain_change_proof_finish_restores_even_generation` |
4747
| [mempool-mutations.md](mempool-mutations.md) | `MPL-01``MPL-04` | Gateway ordering invariant, `MutationEnvelope`/`MutationResult` semantics, ZMQ `A`/`R` payload bytes, generation-validated admission and chain-change fencing | apply path (`crates/node/src/apply.rs`), `sendrawtransaction` (`crates/rpc/src/handlers/tx.rs`), ZMQ `sequence` subscribers (enforcer `--enable-mempool`) | `crates/mempool/src/gateway.rs` test `accepted_and_block_inclusion_events_arrive_in_commit_order`; `crates/node/src/zmq_publisher.rs` tests `block_inclusion_suppresses_r_frames` and `mempool_event_payloads_carry_reversed_txid_label_and_le_sequence`; `crates/node/src/apply.rs` test `stable_generation_is_even_before_and_after_connect` |
4848
| [mempool-policy.md](mempool-policy.md) | `POL-01` | Pointer: relay policy contract pinned to Core 31.1 | `sendrawtransaction`/`testmempoolaccept` (`crates/rpc/src/handlers/tx.rs`), P2P relay admission | `crates/mempool/tests/policy_contract.rs` and `crates/rpc/tests/policy_contract.rs` (`cargo test -p bitcoin-rs-mempool --test policy_contract` / `-p bitcoin-rs-rpc --test policy_contract`) |
49-
| [external-api.md](external-api.md) | `API-01`–`API-27` | Pointer: JSON-RPC/REST/ZMQ manifest, generated reference, error code mappings, query budgeting, solo-mining generate, `getnetworkhashps` snapshot behavior, BIP22/BIP23 template extras, mainnet GBT operational gates, `submitheader`, GBT client-rule negotiation, `submitblock` decode, GBT proposal request parsing, `submitblock` uncommitted witness fill, Core v31 submit/proposal duplicate vocabulary, BIP22 reject-reason mapping, GBT `vbrequired` always 0, Core `CheckWitnessMalleation` reject reasons, GBT `coinbaseaux.flags` empty hex, `prioritisetransaction` dummy/`fee_delta` arity, pooled-dust refusal, reorged scripts-valid duplicate, getmininginfo omitted optional fields, and estimatesmartfee Core conf_target/estimate_mode gates, and generateblock Core txid/raw-tx parse errors, and getprioritisedtransactions modified_fee in satoshis, and generate invalid-output Core -5 text, and generateblock TestBlockValidity before solve | RPC/REST/ZMQ clients; `tools/bip300301-enforcer` | `crates/rpc/tests/manifest_coverage.rs` tests `rpc_rows_and_the_live_registry_agree_both_ways`, `generated_reference_matches_checked_in`; `crates/rpc/src/handlers/mining.rs` generate, GBT extra, GBT gate, `submitheader`, GBT rule-negotiation, `submitblock` decode, GBT proposal-parse, `coinbaseaux`, `prioritisetransaction` dummy/dust, and `getmininginfo` omit-null tests; `crates/node/tests/mining.rs` generate, GBT extra, `submitheader`, uncommitted-witness submit, submit/proposal duplicate, BIP22 reject-reason, `vbrequired`, witness-malleation proposal, and reorged scripts-valid duplicate tests; `crates/mining/src/coinbase.rs` uncommitted-witness tests; `crates/consensus/src/verify_block.rs` CheckWitnessMalleation tests; `crates/node/src/mining.rs` test `hash_ps_at_rejects_a_height_the_tip_cannot_resolve`; `crates/node/tests/mining.rs` test `network_hash_ps_rejects_core_invalid_windows` |
49+
| [external-api.md](external-api.md) | `API-01`–`API-28` | Pointer: JSON-RPC/REST/ZMQ manifest, generated reference, error code mappings, query budgeting, solo-mining generate, `getnetworkhashps` snapshot behavior, BIP22/BIP23 template extras, mainnet GBT operational gates, `submitheader`, GBT client-rule negotiation, `submitblock` decode, GBT proposal request parsing, `submitblock` uncommitted witness fill, Core v31 submit/proposal duplicate vocabulary, BIP22 reject-reason mapping, GBT `vbrequired` always 0, Core `CheckWitnessMalleation` reject reasons, GBT `coinbaseaux.flags` empty hex, `prioritisetransaction` dummy/`fee_delta` arity, pooled-dust refusal, reorged scripts-valid duplicate, getmininginfo omitted optional fields, and estimatesmartfee Core conf_target/estimate_mode gates, and generateblock Core txid/raw-tx parse errors, and getprioritisedtransactions modified_fee in satoshis, and generate invalid-output Core -5 text, and generateblock TestBlockValidity before solve, and generateblock Core multipath/ranged/Expand private-key errors | RPC/REST/ZMQ clients; `tools/bip300301-enforcer` | `crates/rpc/tests/manifest_coverage.rs` tests `rpc_rows_and_the_live_registry_agree_both_ways`, `generated_reference_matches_checked_in`; `crates/rpc/src/handlers/mining.rs` generate, GBT extra, GBT gate, `submitheader`, GBT rule-negotiation, `submitblock` decode, GBT proposal-parse, `coinbaseaux`, `prioritisetransaction` dummy/dust, and `getmininginfo` omit-null tests; `crates/node/tests/mining.rs` generate, GBT extra, `submitheader`, uncommitted-witness submit, submit/proposal duplicate, BIP22 reject-reason, `vbrequired`, witness-malleation proposal, and reorged scripts-valid duplicate tests; `crates/mining/src/coinbase.rs` uncommitted-witness tests; `crates/consensus/src/verify_block.rs` CheckWitnessMalleation tests; `crates/node/src/mining.rs` test `hash_ps_at_rejects_a_height_the_tip_cannot_resolve`; `crates/node/tests/mining.rs` test `network_hash_ps_rejects_core_invalid_windows` |
5050
| [wallet-facing.md](wallet-facing.md) | `WF-01``WF-03` | Public Esplora/JSON-RPC surface an external wallet may use; no `NodeState` / `UtxoSet` / index types | [bitcoin-wallet](https://github.com/gosuda/bitcoin-wallet) (`btcw`); any Esplora HTTP client | `bin/bitcoin-rs/tests/wallet_facing.rs` tests `external_wallet_can_scan_estimate_and_broadcast`, `source_does_not_import_node_internals`; `crates/rpc/src/esplora.rs` tests `esplora_lives_only_under_the_api_prefix`, `api_is_the_public_electrs_directory`, `esplora_is_the_mempool_backend_superset` |
5151
| [p2p-wire.md](p2p-wire.md) | `P2P-01``P2P-02` | Pointer: command inventory in `crates/p2p/src/compat.rs`, handshake/reject/deviations pinned to Core 31.1 | `crates/p2p` peers; `crates/p2p/src/chain_query.rs` active-chain serving | `crates/p2p/tests/core_compat.rs` (`cargo test -p bitcoin-rs-p2p --test core_compat`); live lane `scripts/run-p2p-core-interop.sh` |
5252
| [qa-corpus.md](qa-corpus.md) | `QAC-01` | Pointer: fuzz seed provenance and refresh rules | `fuzz/fuzz_targets/{p2p_message,block_decode,tx_decode,script_eval}.rs`; CI fuzz lanes | `fuzz/CORPUS_PROVENANCE.md` mapping table; targets run under `cargo fuzz run <target> -- -runs=10000` |

docs/contracts/external-api.md

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ reject reasons. `API-18` is GBT `coinbaseaux.flags`. `API-19` is
1919
is `generateblock` Core txid/raw-tx parse errors. `API-25` is
2020
`getprioritisedtransactions` `modified_fee` in satoshis. `API-26` is
2121
Core `generatetoaddress` / `generateblock` invalid-output text. `API-27`
22-
is `generateblock` `TestBlockValidity` before solve.
22+
is `generateblock` `TestBlockValidity` before solve. `API-28` is
23+
`generateblock` multipath, ranged, and Expand private-key errors.
2324

2425
## Clauses
2526

@@ -82,8 +83,9 @@ is `generateblock` `TestBlockValidity` before solve.
8283
- `generatetoaddress` accepts only a network-valid address, uses mempool
8384
package selection, collects fees, and always submits.
8485
- `generateblock` accepts an address or descriptor (`require_checksum = false`;
85-
a supplied checksum is verified). Ranged/multipath descriptors are refused.
86-
The transactions array is required (an explicit `[]` is coinbase-only).
86+
a supplied checksum is verified). Ranged and multipath descriptors are
87+
refused (`API-28`). The transactions array is required (an explicit `[]` is
88+
coinbase-only).
8789
Listed order is kept, those fees are not added to the coinbase, 64-character
8890
hex is a mempool txid, and decoded raw transactions are included without
8991
mempool admission. Extra positional arguments are rejected. Transaction
@@ -334,7 +336,7 @@ is `generateblock` `TestBlockValidity` before solve.
334336
- `generatetoaddress` refuses a non-address with `-5`
335337
`Error: Invalid address`.
336338
- `generateblock` tries a descriptor first (`require_checksum = false`).
337-
Ranged/multipath descriptors stay `-8`. If Parse fails, the text is
339+
Ranged/multipath descriptors are `-8` (`API-28`). If Parse fails, the text is
338340
tried as an address; a miss is `-5`
339341
`Error: Invalid address or descriptor`, matching Core
340342
`src/rpc/mining.cpp`. A supplied checksum that fails Parse is refused
@@ -358,6 +360,20 @@ is `generateblock` `TestBlockValidity` before solve.
358360
vocabulary). Shutdown and journal backpressure stay `Unavailable`,
359361
not TestBlockValidity.
360362

363+
### `API-28`: `generateblock` multipath, ranged, and Expand errors
364+
365+
- **Owner**: `generateblock_payout_script` in
366+
`crates/rpc/src/handlers/util.rs`.
367+
- Core `getScriptFromDescriptor` throws before the address fallback:
368+
- more than one parsed descriptor → `-8`
369+
`Multipath descriptor not accepted`
370+
- `IsRange()``-8`
371+
`Ranged descriptor not accepted. Maybe pass through deriveaddresses first?`
372+
- `Expand(0)` failure → `-5`
373+
`Cannot derive script without private keys`
374+
- Multipath is checked first, matching Core `descs.size() > 1` before
375+
`IsRange()`. A descriptor that is both is the multipath error.
376+
361377
The wallet-facing subset of this surface — tip, fees, address/script
362378
queries, and broadcast over Esplora, plus the key-free node RPCs — is
363379
owned by [wallet-facing.md](wallet-facing.md).
@@ -509,3 +525,7 @@ owned by [wallet-facing.md](wallet-facing.md).
509525
`generateblock_raw_tx_does_not_require_mempool_admission`
510526
- `crates/rpc/src/handlers/mining.rs` test
511527
`generateblock_maps_test_block_validity_to_verify_error`
528+
- `API-28`:
529+
- `crates/rpc/src/handlers/mining.rs` tests
530+
`generateblock_rejects_multipath_before_ranged_like_core`,
531+
`generateblock_rejects_hardened_xpub_like_core`

0 commit comments

Comments
 (0)