Merge pull request #286 from apoelstra/2019-06-builder-ext

script: give `Builder` ability to verify-ify opcodes
This commit is contained in:
Andrew Poelstra 2019-07-03 21:50:57 +00:00 committed by GitHub
commit 1a4bb371a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 120 additions and 11 deletions

View File

@ -798,7 +798,7 @@ macro_rules! ordinary_opcode {
); );
} }
/// "Ordinary" opcodes -- should be 60 of these // "Ordinary" opcodes -- should be 60 of these
ordinary_opcode! { ordinary_opcode! {
// pushdata // pushdata
OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4, OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4,

View File

@ -77,7 +77,7 @@ impl fmt::UpperHex for Script {
#[derive(PartialEq, Eq, Debug, Clone)] #[derive(PartialEq, Eq, Debug, Clone)]
/// An object which can be used to construct a script piece by piece /// An object which can be used to construct a script piece by piece
pub struct Builder(Vec<u8>); pub struct Builder(Vec<u8>, Option<opcodes::All>);
display_from_debug!(Builder); display_from_debug!(Builder);
/// Ways that a script might fail. Not everything is split up as /// Ways that a script might fail. Not everything is split up as
@ -541,7 +541,9 @@ impl<'a> Iterator for Instructions<'a> {
impl Builder { impl Builder {
/// Creates a new empty script /// Creates a new empty script
pub fn new() -> Builder { Builder(vec![]) } pub fn new() -> Builder {
Builder(vec![], None)
}
/// The length in bytes of the script /// The length in bytes of the script
pub fn len(&self) -> usize { self.0.len() } pub fn len(&self) -> usize { self.0.len() }
@ -552,16 +554,17 @@ impl Builder {
/// Adds instructions to push an integer onto the stack. Integers are /// Adds instructions to push an integer onto the stack. Integers are
/// encoded as little-endian signed-magnitude numbers, but there are /// encoded as little-endian signed-magnitude numbers, but there are
/// dedicated opcodes to push some small integers. /// dedicated opcodes to push some small integers.
pub fn push_int(mut self, data: i64) -> Builder { pub fn push_int(self, data: i64) -> Builder {
// We can special-case -1, 1-16 // We can special-case -1, 1-16
if data == -1 || (data >= 1 && data <= 16) { if data == -1 || (data >= 1 && data <= 16) {
self.0.push((data - 1 + opcodes::OP_TRUE.into_u8() as i64) as u8); let opcode = opcodes::All::from(
self (data - 1 + opcodes::OP_TRUE.into_u8() as i64) as u8
);
self.push_opcode(opcode)
} }
// We can also special-case zero // We can also special-case zero
else if data == 0 { else if data == 0 {
self.0.push(opcodes::OP_FALSE.into_u8()); self.push_opcode(opcodes::OP_FALSE)
self
} }
// Otherwise encode it as data // Otherwise encode it as data
else { self.push_scriptint(data) } else { self.push_scriptint(data) }
@ -596,8 +599,9 @@ impl Builder {
} }
_ => panic!("tried to put a 4bn+ sized object into a script!") _ => panic!("tried to put a 4bn+ sized object into a script!")
} }
// Then push the acraw // Then push the raw bytes
self.0.extend(data.iter().cloned()); self.0.extend(data.iter().cloned());
self.1 = None;
self self
} }
@ -613,9 +617,35 @@ impl Builder {
/// Adds a single opcode to the script /// Adds a single opcode to the script
pub fn push_opcode(mut self, data: opcodes::All) -> Builder { pub fn push_opcode(mut self, data: opcodes::All) -> Builder {
self.0.push(data.into_u8()); self.0.push(data.into_u8());
self.1 = Some(data);
self self
} }
/// Adds an `OP_VERIFY` to the script, unless the most-recently-added
/// opcode has an alternate `VERIFY` form, in which case that opcode
/// is replaced. e.g. `OP_CHECKSIG` will become `OP_CHECKSIGVERIFY`.
pub fn push_verify(mut self) -> Builder {
match self.1 {
Some(opcodes::all::OP_EQUAL) => {
self.0.pop();
self.push_opcode(opcodes::all::OP_EQUALVERIFY)
},
Some(opcodes::all::OP_NUMEQUAL) => {
self.0.pop();
self.push_opcode(opcodes::all::OP_NUMEQUALVERIFY)
},
Some(opcodes::all::OP_CHECKSIG) => {
self.0.pop();
self.push_opcode(opcodes::all::OP_CHECKSIGVERIFY)
},
Some(opcodes::all::OP_CHECKMULTISIG) => {
self.0.pop();
self.push_opcode(opcodes::all::OP_CHECKMULTISIGVERIFY)
},
_ => self.push_opcode(opcodes::all::OP_VERIFY),
}
}
/// Converts the `Builder` into an unmodifiable `Script` /// Converts the `Builder` into an unmodifiable `Script`
pub fn into_script(self) -> Script { pub fn into_script(self) -> Script {
Script(self.0.into_boxed_slice()) Script(self.0.into_boxed_slice())
@ -624,12 +654,19 @@ impl Builder {
/// Adds an individual opcode to the script /// Adds an individual opcode to the script
impl Default for Builder { impl Default for Builder {
fn default() -> Builder { Builder(vec![]) } fn default() -> Builder { Builder::new() }
} }
/// Creates a new script from an existing vector /// Creates a new script from an existing vector
impl From<Vec<u8>> for Builder { impl From<Vec<u8>> for Builder {
fn from(v: Vec<u8>) -> Builder { Builder(v) } fn from(v: Vec<u8>) -> Builder {
let script = Script(v.into_boxed_slice());
let last_op = match script.iter(false).last() {
Some(Instruction::Op(op)) => Some(op),
_ => None,
};
Builder(script.into_bytes(), last_op)
}
} }
impl_index_newtype!(Builder, u8); impl_index_newtype!(Builder, u8);
@ -763,6 +800,78 @@ mod test {
assert_eq!(&format!("{:x}", script), "76a91416e1ae70ff0fa102905d4af297f6912bda6cce1988ac"); assert_eq!(&format!("{:x}", script), "76a91416e1ae70ff0fa102905d4af297f6912bda6cce1988ac");
} }
#[test]
fn script_builder_verify() {
let simple = Builder::new()
.push_verify()
.into_script();
assert_eq!(format!("{:x}", simple), "69");
let simple2 = Builder::from(vec![])
.push_verify()
.into_script();
assert_eq!(format!("{:x}", simple2), "69");
let nonverify = Builder::new()
.push_verify()
.push_verify()
.into_script();
assert_eq!(format!("{:x}", nonverify), "6969");
let nonverify2 = Builder::from(vec![0x69])
.push_verify()
.into_script();
assert_eq!(format!("{:x}", nonverify2), "6969");
let equal = Builder::new()
.push_opcode(opcodes::all::OP_EQUAL)
.push_verify()
.into_script();
assert_eq!(format!("{:x}", equal), "88");
let equal2 = Builder::from(vec![0x87])
.push_verify()
.into_script();
assert_eq!(format!("{:x}", equal2), "88");
let numequal = Builder::new()
.push_opcode(opcodes::all::OP_NUMEQUAL)
.push_verify()
.into_script();
assert_eq!(format!("{:x}", numequal), "9d");
let numequal2 = Builder::from(vec![0x9c])
.push_verify()
.into_script();
assert_eq!(format!("{:x}", numequal2), "9d");
let checksig = Builder::new()
.push_opcode(opcodes::all::OP_CHECKSIG)
.push_verify()
.into_script();
assert_eq!(format!("{:x}", checksig), "ad");
let checksig2 = Builder::from(vec![0xac])
.push_verify()
.into_script();
assert_eq!(format!("{:x}", checksig2), "ad");
let checkmultisig = Builder::new()
.push_opcode(opcodes::all::OP_CHECKMULTISIG)
.push_verify()
.into_script();
assert_eq!(format!("{:x}", checkmultisig), "af");
let checkmultisig2 = Builder::from(vec![0xae])
.push_verify()
.into_script();
assert_eq!(format!("{:x}", checkmultisig2), "af");
let trick_slice = Builder::new()
.push_slice(&[0xae]) // OP_CHECKMULTISIG
.push_verify()
.into_script();
assert_eq!(format!("{:x}", trick_slice), "01ae69");
let trick_slice2 = Builder::from(vec![0x01, 0xae])
.push_verify()
.into_script();
assert_eq!(format!("{:x}", trick_slice2), "01ae69");
}
#[test] #[test]
fn script_serialize() { fn script_serialize() {
let hex_script = hex_decode("6c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52").unwrap(); let hex_script = hex_decode("6c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52").unwrap();