diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index dac83c092..3d34f23f5 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -58,8 +58,8 @@ jobs: - 1.22.0 - beta - stable - steps: - - name: Checkout Crate + steps: + - name: Checkout Crate uses: actions/checkout@v2 - name: Checkout Toolchain uses: actions-rs/toolchain@v1 @@ -67,6 +67,8 @@ jobs: profile: minimal toolchain: ${{ matrix.rust }} override: true + - name: Pin cc if rust 1.22 + if: matrix.rust == '1.22.0' + run: cargo generate-lockfile --verbose && cargo update -p cc --precise "1.0.41" --verbose - name: Running cargo run: ./contrib/test.sh - diff --git a/Cargo.toml b/Cargo.toml index 432790e36..d4397e69d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ unstable = [] default = [] [dependencies] -bitcoin = "0.23" +bitcoin = "0.24" [dependencies.serde] version = "1.0" diff --git a/src/descriptor/create_descriptor.rs b/src/descriptor/create_descriptor.rs index 435f7f46c..d36a7cb4e 100644 --- a/src/descriptor/create_descriptor.rs +++ b/src/descriptor/create_descriptor.rs @@ -23,11 +23,13 @@ use ToPublicKey; /// /// NOTE: Miniscript pushes should only be either boolean, 1 or 0, signatures, and hash preimages. /// As per the current implementation, PUSH_NUM2 results in an error -fn instr_to_stackelem<'txin>(ins: &Instruction<'txin>) -> Result, Error> { +fn instr_to_stackelem<'txin>( + ins: &Result, bitcoin::blockdata::script::Error>, +) -> Result, Error> { match *ins { //Also covers the dissatisfied case as PushBytes0 - Instruction::PushBytes(v) => Ok(StackElement::from(v)), - Instruction::Op(opcodes::all::OP_PUSHNUM_1) => Ok(StackElement::Satisfied), + Ok(Instruction::PushBytes(v)) => Ok(StackElement::from(v)), + Ok(Instruction::Op(opcodes::all::OP_PUSHNUM_1)) => Ok(StackElement::Satisfied), _ => Err(Error::BadScriptSig), } } @@ -39,7 +41,7 @@ fn parse_scriptsig_top<'txin>( script_sig: &'txin bitcoin::Script, ) -> Result<(Vec, Stack<'txin>), Error> { let stack: Result, Error> = script_sig - .iter(true) + .instructions_minimal() .map(|instr| instr_to_stackelem(&instr)) .collect(); let mut stack = stack?; @@ -61,7 +63,7 @@ fn verify_p2pk<'txin>( let pk_bytes = &script_pubkey.to_bytes(); if let Ok(pk) = bitcoin::PublicKey::from_slice(&pk_bytes[1..script_pubkey_len - 1]) { let stack: Result, Error> = script_sig - .iter(true) + .instructions_minimal() .map(|instr| instr_to_stackelem(&instr)) .collect(); if !witness.is_empty() { @@ -87,7 +89,8 @@ fn verify_p2wpkh<'txin>( } if let Some((pk_bytes, witness)) = witness.split_last() { if let Ok(pk) = bitcoin::PublicKey::from_slice(pk_bytes) { - let addr = bitcoin::Address::p2wpkh(&pk.to_public_key(), bitcoin::Network::Bitcoin); + let addr = bitcoin::Address::p2wpkh(&pk.to_public_key(), bitcoin::Network::Bitcoin) + .map_err(|_| Error::InterpreterError(IntError::UncompressedPubkey))?; if addr.script_pubkey() != *script_pubkey { return Err(Error::InterpreterError(IntError::PkEvaluationError(pk))); } @@ -224,7 +227,7 @@ pub fn from_txin_with_witness_stack<'txin>( } else { //bare let stack: Result, Error> = script_sig - .iter(true) + .instructions_minimal() .map(|instr| instr_to_stackelem(&instr)) .collect(); if !witness.is_empty() { @@ -318,8 +321,9 @@ mod tests { assert_eq!(stack, stack![Push(&sigs[0])]); //test wpkh - let script_pubkey = - bitcoin::Address::p2wpkh(&pks[1], bitcoin::Network::Bitcoin).script_pubkey(); + let script_pubkey = bitcoin::Address::p2wpkh(&pks[1], bitcoin::Network::Bitcoin) + .unwrap() + .script_pubkey(); let script_sig = script::Builder::new().into_script(); let witness = vec![sigs[1].clone(), pks[1].clone().to_bytes()]; let (des, stack) = from_txin_with_witness_stack(&script_pubkey, &script_sig, &witness) @@ -381,10 +385,12 @@ mod tests { assert_eq!(stack, stack![Push(&sigs[1]), Push(&sigs[3])]); //test shwpkh - let script_pubkey = - bitcoin::Address::p2shwpkh(&pks[2], bitcoin::Network::Bitcoin).script_pubkey(); - let redeem_script = - bitcoin::Address::p2wpkh(&pks[2], bitcoin::Network::Bitcoin).script_pubkey(); + let script_pubkey = bitcoin::Address::p2shwpkh(&pks[2], bitcoin::Network::Bitcoin) + .unwrap() + .script_pubkey(); + let redeem_script = bitcoin::Address::p2wpkh(&pks[2], bitcoin::Network::Bitcoin) + .unwrap() + .script_pubkey(); let script_sig = script::Builder::new() .push_slice(&redeem_script.to_bytes()) .into_script(); diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index 77b4cd01e..2b3f542f1 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -121,12 +121,14 @@ impl Descriptor { Descriptor::Bare(..) => None, Descriptor::Pk(..) => None, Descriptor::Pkh(ref pk) => Some(bitcoin::Address::p2pkh(&pk.to_public_key(), network)), - Descriptor::Wpkh(ref pk) => { - Some(bitcoin::Address::p2wpkh(&pk.to_public_key(), network)) - } - Descriptor::ShWpkh(ref pk) => { - Some(bitcoin::Address::p2shwpkh(&pk.to_public_key(), network)) - } + Descriptor::Wpkh(ref pk) => Some( + bitcoin::Address::p2wpkh(&pk.to_public_key(), network) + .expect("wpkh descriptors have compressed keys"), + ), + Descriptor::ShWpkh(ref pk) => Some( + bitcoin::Address::p2shwpkh(&pk.to_public_key(), network) + .expect("shwpkh descriptors have compressed keys"), + ), Descriptor::Sh(ref miniscript) => { Some(bitcoin::Address::p2sh(&miniscript.encode(), network)) } @@ -152,12 +154,14 @@ impl Descriptor { addr.script_pubkey() } Descriptor::Wpkh(ref pk) => { - let addr = bitcoin::Address::p2wpkh(&pk.to_public_key(), bitcoin::Network::Bitcoin); + let addr = bitcoin::Address::p2wpkh(&pk.to_public_key(), bitcoin::Network::Bitcoin) + .expect("wpkh descriptors have compressed keys"); addr.script_pubkey() } Descriptor::ShWpkh(ref pk) => { let addr = - bitcoin::Address::p2shwpkh(&pk.to_public_key(), bitcoin::Network::Bitcoin); + bitcoin::Address::p2shwpkh(&pk.to_public_key(), bitcoin::Network::Bitcoin) + .expect("shwpkh descriptors have compressed keys"); addr.script_pubkey() } Descriptor::Sh(ref miniscript) => miniscript.encode().to_p2sh(), @@ -185,7 +189,8 @@ impl Descriptor { Descriptor::Wsh(..) | Descriptor::Wpkh(..) => Script::new(), // segwit+p2sh Descriptor::ShWpkh(ref pk) => { - let addr = bitcoin::Address::p2wpkh(&pk.to_public_key(), bitcoin::Network::Bitcoin); + let addr = bitcoin::Address::p2wpkh(&pk.to_public_key(), bitcoin::Network::Bitcoin) + .expect("wpkh descriptors have compressed keys"); let redeem_script = addr.script_pubkey(); script::Builder::new() .push_slice(&redeem_script[..]) @@ -211,7 +216,8 @@ impl Descriptor { | Descriptor::Pkh(..) | Descriptor::Wpkh(..) => self.script_pubkey(), Descriptor::ShWpkh(ref pk) => { - let addr = bitcoin::Address::p2wpkh(&pk.to_public_key(), bitcoin::Network::Bitcoin); + let addr = bitcoin::Address::p2wpkh(&pk.to_public_key(), bitcoin::Network::Bitcoin) + .expect("shwpkh descriptors have compressed keys"); addr.script_pubkey() } Descriptor::Sh(ref d) => d.encode(), @@ -292,7 +298,8 @@ impl Descriptor { let mut sig_vec = sig.0.serialize_der().to_vec(); sig_vec.push(sig.1.as_u32() as u8); let addr = - bitcoin::Address::p2wpkh(&pk.to_public_key(), bitcoin::Network::Bitcoin); + bitcoin::Address::p2wpkh(&pk.to_public_key(), bitcoin::Network::Bitcoin) + .expect("wpkh descriptors have compressed keys"); let redeem_script = addr.script_pubkey(); txin.script_sig = script::Builder::new() @@ -926,10 +933,10 @@ mod tests { let descriptor = Descriptor::::from_str("wsh(after(1000))").unwrap(); let script = descriptor.witness_script(); - let actual_instructions: Vec<_> = script.iter(false).collect(); + let actual_instructions: Vec<_> = script.instructions().collect(); let check = actual_instructions.last().unwrap(); - assert_eq!(check, &Instruction::Op(OP_CLTV)) + assert_eq!(check, &Ok(Instruction::Op(OP_CLTV))) } #[test] @@ -937,10 +944,10 @@ mod tests { let descriptor = Descriptor::::from_str("wsh(older(1000))").unwrap(); let script = descriptor.witness_script(); - let actual_instructions: Vec<_> = script.iter(false).collect(); + let actual_instructions: Vec<_> = script.instructions().collect(); let check = actual_instructions.last().unwrap(); - assert_eq!(check, &Instruction::Op(OP_CSV)) + assert_eq!(check, &Ok(Instruction::Op(OP_CSV))) } #[test] diff --git a/src/descriptor/satisfied_constraints.rs b/src/descriptor/satisfied_constraints.rs index fe8968337..63f37ed8d 100644 --- a/src/descriptor/satisfied_constraints.rs +++ b/src/descriptor/satisfied_constraints.rs @@ -25,6 +25,9 @@ use {BitcoinSig, ToPublicKey}; /// Detailed Error type for Interpreter #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum Error { + /// An uncompressed public key was encountered in a context where it is + /// disallowed (e.g. in a Segwit script or p2wpkh output) + UncompressedPubkey, /// Unexpected Stack End, caused by popping extra elements from stack UnexpectedStackEnd, /// Unexpected Stack Push `StackElement::Push` element when the interpreter @@ -95,6 +98,7 @@ impl error::Error for Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { + Error::UncompressedPubkey => f.write_str("Illegal use of uncompressed pubkey"), Error::UnexpectedStackEnd => f.write_str("Unexpected Stack End"), Error::UnexpectedStackElementPush => write!(f, "Got {}, expected Stack Boolean", 1), Error::VerifyFailed => { diff --git a/src/miniscript/lex.rs b/src/miniscript/lex.rs index bf4a12011..cfd3cfac1 100644 --- a/src/miniscript/lex.rs +++ b/src/miniscript/lex.rs @@ -116,9 +116,8 @@ impl Iterator for TokenIter { pub fn lex(script: &script::Script) -> Result, Error> { let mut ret = Vec::with_capacity(script.len()); - for ins in script.iter(true) { - match ins { - script::Instruction::Error(e) => return Err(Error::Script(e)), + for ins in script.instructions_minimal() { + match ins.map_err(Error::Script)? { script::Instruction::Op(opcodes::all::OP_BOOLAND) => { ret.push(Token::BoolAnd); }