Skip to content

Commit 8874640

Browse files
committed
miniscript: fix ExtData to correctly account for uncompressed keys
In our existing code we assume that all "ECDSA keys" are compressed, i.e. 34 bytes long. This is not a valid assumption. We originally did this because under some circumstances we legitimately did not know the key and didn't want to penalize users by assuming uncompressed keys when this was unlikely the case. However, now the only way to have a pk or pkh where we don't know the key is if the user is using a raw pkh. And if they do this in Taproot or Segwit we still know that there are no uncompressed keys (though with the current Ctx trait we can't distinguish Segwit from pre-Segwit so the new logic assumes uncompressed keys even for Segwit.) This logic will be cleaned up and improved with ValidationParams; but currently it is wrong, and it is causing me trouble with later commits. So fix the situation here as best we can. The next commit will make a similar change to the multi() combinator (multi_a is fine because the keys and sigs are fixed size); the commit after that will introduce a unit test to SortedMulti which exercises this logic.
1 parent 040c29d commit 8874640

File tree

2 files changed

+30
-24
lines changed

2 files changed

+30
-24
lines changed

src/miniscript/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,19 +183,19 @@ mod private {
183183
/// The `pk_k` combinator.
184184
pub fn pk_k(pk: Pk) -> Self {
185185
Self {
186+
ext: types::extra_props::ExtData::pk_k::<_, Ctx>(&pk),
186187
node: Terminal::PkK(pk),
187188
ty: types::Type::pk_k(),
188-
ext: types::extra_props::ExtData::pk_k::<Ctx>(),
189189
phantom: PhantomData,
190190
}
191191
}
192192

193193
/// The `pk_h` combinator.
194194
pub fn pk_h(pk: Pk) -> Self {
195195
Self {
196+
ext: types::extra_props::ExtData::pk_h::<_, Ctx>(Some(&pk)),
196197
node: Terminal::PkH(pk),
197198
ty: types::Type::pk_h(),
198-
ext: types::extra_props::ExtData::pk_h::<Ctx>(),
199199
phantom: PhantomData,
200200
}
201201
}
@@ -205,7 +205,7 @@ mod private {
205205
Self {
206206
node: Terminal::RawPkH(hash),
207207
ty: types::Type::pk_h(),
208-
ext: types::extra_props::ExtData::pk_h::<Ctx>(),
208+
ext: types::extra_props::ExtData::pk_h::<Pk, Ctx>(None),
209209
phantom: PhantomData,
210210
}
211211
}

src/miniscript/types/extra_props.rs

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use core::cmp;
77
use core::iter::once;
88

99
use super::ScriptContext;
10-
use crate::miniscript::context::SigType;
1110
use crate::prelude::*;
1211
use crate::{script_num_size, AbsLockTime, MiniscriptKey, RelLockTime, Terminal};
1312

@@ -191,20 +190,22 @@ impl ExtData {
191190
}
192191

193192
/// Extra properties for the `pk_k` fragment.
194-
pub fn pk_k<Ctx: ScriptContext>() -> Self {
193+
///
194+
/// The key must be provided to determine its size.
195+
pub fn pk_k<Pk: MiniscriptKey, Ctx: ScriptContext>(pk: &Pk) -> Self {
196+
let (key_bytes, max_sig_bytes) = match Ctx::sig_type() {
197+
crate::SigType::Ecdsa if pk.is_uncompressed() => (65, 73),
198+
crate::SigType::Ecdsa => (34, 73),
199+
crate::SigType::Schnorr => (33, 66),
200+
};
201+
195202
ExtData {
196-
pk_cost: match Ctx::sig_type() {
197-
SigType::Ecdsa => 34,
198-
SigType::Schnorr => 33,
199-
},
203+
pk_cost: key_bytes,
200204
has_free_verify: false,
201205
ops: OpLimits::new(0, Some(0), Some(0)),
202206
stack_elem_count_sat: Some(1),
203207
stack_elem_count_dissat: Some(1),
204-
max_sat_size: match Ctx::sig_type() {
205-
SigType::Ecdsa => Some((73, 73)),
206-
SigType::Schnorr => Some((66, 66)),
207-
},
208+
max_sat_size: Some((max_sig_bytes, max_sig_bytes)),
208209
max_dissat_size: Some((1, 1)),
209210
timelock_info: TimelockInfo::default(),
210211
exec_stack_elem_count_sat: Some(1), // pushes the pk
@@ -214,21 +215,25 @@ impl ExtData {
214215
}
215216

216217
/// Extra properties for the `pk_h` fragment.
217-
pub fn pk_h<Ctx: ScriptContext>() -> Self {
218+
///
219+
/// If the key is known, it should be provided to gain a size estimate from
220+
/// it. If not, the worst-case for the context will be assumed.
221+
pub fn pk_h<Pk: MiniscriptKey, Ctx: ScriptContext>(pk: Option<&Pk>) -> Self {
222+
// With a raw pkh we don't know the preimage size so we have to assume the worst.
223+
// FIXME with ValidationParams we will be able to determine if Ctx is Segwitv0 and exclude uncompressed keys.
224+
let (key_bytes, max_sig_bytes) = match (Ctx::sig_type(), pk) {
225+
(crate::SigType::Ecdsa, Some(pk)) if pk.is_uncompressed() => (65, 73),
226+
(crate::SigType::Ecdsa, _) => (34, 73),
227+
(crate::SigType::Schnorr, _) => (33, 66),
228+
};
218229
ExtData {
219230
pk_cost: 24,
220231
has_free_verify: false,
221232
ops: OpLimits::new(3, Some(0), Some(0)),
222233
stack_elem_count_sat: Some(2),
223234
stack_elem_count_dissat: Some(2),
224-
max_sat_size: match Ctx::sig_type() {
225-
SigType::Ecdsa => Some((34 + 73, 34 + 73)),
226-
SigType::Schnorr => Some((66 + 33, 33 + 66)),
227-
},
228-
max_dissat_size: match Ctx::sig_type() {
229-
SigType::Ecdsa => Some((35, 35)),
230-
SigType::Schnorr => Some((34, 34)),
231-
},
235+
max_sat_size: Some((key_bytes + max_sig_bytes, key_bytes + max_sig_bytes)),
236+
max_dissat_size: Some((key_bytes + 1, key_bytes + 1)),
232237
timelock_info: TimelockInfo::default(),
233238
exec_stack_elem_count_sat: Some(2), // dup and hash push
234239
exec_stack_elem_count_dissat: Some(2),
@@ -932,8 +937,9 @@ impl ExtData {
932937
let ret = match *fragment {
933938
Terminal::True => Self::TRUE,
934939
Terminal::False => Self::FALSE,
935-
Terminal::PkK(..) => Self::pk_k::<Ctx>(),
936-
Terminal::PkH(..) | Terminal::RawPkH(..) => Self::pk_h::<Ctx>(),
940+
Terminal::PkK(ref k) => Self::pk_k::<_, Ctx>(k),
941+
Terminal::PkH(ref k) => Self::pk_h::<_, Ctx>(Some(k)),
942+
Terminal::RawPkH(..) => Self::pk_h::<Pk, Ctx>(None),
937943
Terminal::Multi(ref thresh) => Self::multi(thresh.k(), thresh.n()),
938944
Terminal::MultiA(ref thresh) => Self::multi_a(thresh.k(), thresh.n()),
939945
Terminal::After(t) => Self::after(t),

0 commit comments

Comments
 (0)