[BREAKING CHANGE] Make `script::Builder` implement the actual Builder pattern
Rather than having methods taking &mut self, have them consume self and return another Builder, so that methods can be chained. Bump major version number.
This commit is contained in:
parent
5e03adc9aa
commit
66eb08aab5
|
@ -1,7 +1,7 @@
|
|||
|
||||
[package]
|
||||
name = "bitcoin"
|
||||
version = "0.3.10"
|
||||
version = "0.4.0"
|
||||
authors = ["Andrew Poelstra <apoelstra@wpsoftware.net>"]
|
||||
license = "CC0-1.0"
|
||||
homepage = "https://github.com/apoelstra/rust-bitcoin/"
|
||||
|
|
|
@ -65,24 +65,25 @@ fn bitcoin_genesis_tx() -> Transaction {
|
|||
};
|
||||
|
||||
// Inputs
|
||||
let mut in_script = script::Builder::new();
|
||||
in_script.push_scriptint(486604799);
|
||||
in_script.push_scriptint(4);
|
||||
in_script.push_slice("The Times 03/Jan/2009 Chancellor on brink of second bailout for banks".as_bytes());
|
||||
let in_script = script::Builder::new().push_scriptint(486604799)
|
||||
.push_scriptint(4)
|
||||
.push_slice("The Times 03/Jan/2009 Chancellor on brink of second bailout for banks".as_bytes())
|
||||
.into_script();
|
||||
ret.input.push(TxIn {
|
||||
prev_hash: Default::default(),
|
||||
prev_index: 0xFFFFFFFF,
|
||||
script_sig: in_script.into_script(),
|
||||
script_sig: in_script,
|
||||
sequence: MAX_SEQUENCE
|
||||
});
|
||||
|
||||
// Outputs
|
||||
let mut out_script = script::Builder::new();
|
||||
out_script.push_slice(&hex_bytes("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f").unwrap());
|
||||
out_script.push_opcode(opcodes::All::OP_CHECKSIG);
|
||||
let out_script = script::Builder::new()
|
||||
.push_slice(&hex_bytes("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f").unwrap())
|
||||
.push_opcode(opcodes::All::OP_CHECKSIG)
|
||||
.into_script();
|
||||
ret.output.push(TxOut {
|
||||
value: 50 * COIN_VALUE,
|
||||
script_pubkey: out_script.into_script()
|
||||
script_pubkey: out_script
|
||||
});
|
||||
|
||||
// end
|
||||
|
|
|
@ -2059,8 +2059,7 @@ impl Script {
|
|||
// Compute the section of script that needs to be hashed: everything
|
||||
// from the last CODESEPARATOR, except the signature itself.
|
||||
let mut script = (&self.0[codeseparator_index..]).to_vec();
|
||||
let mut remove = Builder::new();
|
||||
remove.push_slice(sig_slice);
|
||||
let remove = Builder::new().push_slice(sig_slice);
|
||||
script_find_and_remove(&mut script, &remove[..]);
|
||||
// Also all of the OP_CODESEPARATORS, even the unevaluated ones
|
||||
script_find_and_remove(&mut script, &[opcodes::Ordinary::OP_CODESEPARATOR as u8]);
|
||||
|
@ -2110,8 +2109,7 @@ impl Script {
|
|||
// from the last CODESEPARATOR, except the signatures themselves.
|
||||
let mut script = (&self.0[codeseparator_index..]).to_vec();
|
||||
for sig in &sigs {
|
||||
let mut remove = Builder::new();
|
||||
remove.push_slice(&sig[..]);
|
||||
let remove = Builder::new().push_slice(&sig[..]);
|
||||
script_find_and_remove(&mut script, &remove[..]);
|
||||
script_find_and_remove(&mut script, &[opcodes::Ordinary::OP_CODESEPARATOR as u8]);
|
||||
}
|
||||
|
@ -2629,27 +2627,29 @@ impl Builder {
|
|||
/// Adds instructions to push an integer onto the stack. Integers are
|
||||
/// encoded as little-endian signed-magnitude numbers, but there are
|
||||
/// dedicated opcodes to push some small integers.
|
||||
pub fn push_int(&mut self, data: i64) {
|
||||
pub fn push_int(mut self, data: i64) -> Builder {
|
||||
// We can special-case -1, 1-16
|
||||
if data == -1 || (data >= 1 && data <= 16) {
|
||||
self.0.push((data + opcodes::OP_TRUE as i64) as u8);
|
||||
self
|
||||
}
|
||||
// We can also special-case zero
|
||||
else if data == 0 {
|
||||
self.0.push(opcodes::OP_FALSE as u8);
|
||||
self
|
||||
}
|
||||
// Otherwise encode it as data
|
||||
else { self.push_scriptint(data); }
|
||||
else { self.push_scriptint(data) }
|
||||
}
|
||||
|
||||
/// Adds instructions to push an integer onto the stack, using the explicit
|
||||
/// encoding regardless of the availability of dedicated opcodes.
|
||||
pub fn push_scriptint(&mut self, data: i64) {
|
||||
self.push_slice(&build_scriptint(data));
|
||||
pub fn push_scriptint(self, data: i64) -> Builder {
|
||||
self.push_slice(&build_scriptint(data))
|
||||
}
|
||||
|
||||
/// Adds instructions to push some arbitrary data onto the stack
|
||||
pub fn push_slice(&mut self, data: &[u8]) {
|
||||
pub fn push_slice(mut self, data: &[u8]) -> Builder {
|
||||
// Start with a PUSH opcode
|
||||
match data.len() {
|
||||
n if n < opcodes::Ordinary::OP_PUSHDATA1 as usize => { self.0.push(n as u8); },
|
||||
|
@ -2673,11 +2673,13 @@ impl Builder {
|
|||
}
|
||||
// Then push the acraw
|
||||
self.0.extend(data.iter().cloned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a single opcode to the script
|
||||
pub fn push_opcode(&mut self, data: opcodes::All) {
|
||||
pub fn push_opcode(mut self, data: opcodes::All) -> Builder {
|
||||
self.0.push(data as u8);
|
||||
self
|
||||
}
|
||||
|
||||
/// Converts the `Builder` into an unmodifiable `Script`
|
||||
|
@ -2773,25 +2775,37 @@ mod test {
|
|||
assert_eq!(&script[..], &comp[..]);
|
||||
|
||||
// small ints
|
||||
script.push_int(1); comp.push(82u8); assert_eq!(&script[..], &comp[..]);
|
||||
script.push_int(0); comp.push(0u8); assert_eq!(&script[..], &comp[..]);
|
||||
script.push_int(4); comp.push(85u8); assert_eq!(&script[..], &comp[..]);
|
||||
script.push_int(-1); comp.push(80u8); assert_eq!(&script[..], &comp[..]);
|
||||
script = script.push_int(1); comp.push(82u8); assert_eq!(&script[..], &comp[..]);
|
||||
script = script.push_int(0); comp.push(0u8); assert_eq!(&script[..], &comp[..]);
|
||||
script = script.push_int(4); comp.push(85u8); assert_eq!(&script[..], &comp[..]);
|
||||
script = script.push_int(-1); comp.push(80u8); assert_eq!(&script[..], &comp[..]);
|
||||
// forced scriptint
|
||||
script.push_scriptint(4); comp.extend([1u8, 4].iter().cloned()); assert_eq!(&script[..], &comp[..]);
|
||||
script = script.push_scriptint(4); comp.extend([1u8, 4].iter().cloned()); assert_eq!(&script[..], &comp[..]);
|
||||
// big ints
|
||||
script.push_int(17); comp.extend([1u8, 17].iter().cloned()); assert_eq!(&script[..], &comp[..]);
|
||||
script.push_int(10000); comp.extend([2u8, 16, 39].iter().cloned()); assert_eq!(&script[..], &comp[..]);
|
||||
script = script.push_int(17); comp.extend([1u8, 17].iter().cloned()); assert_eq!(&script[..], &comp[..]);
|
||||
script = script.push_int(10000); comp.extend([2u8, 16, 39].iter().cloned()); assert_eq!(&script[..], &comp[..]);
|
||||
// notice the sign bit set here, hence the extra zero/128 at the end
|
||||
script.push_int(10000000); comp.extend([4u8, 128, 150, 152, 0].iter().cloned()); assert_eq!(&script[..], &comp[..]);
|
||||
script.push_int(-10000000); comp.extend([4u8, 128, 150, 152, 128].iter().cloned()); assert_eq!(&script[..], &comp[..]);
|
||||
script = script.push_int(10000000); comp.extend([4u8, 128, 150, 152, 0].iter().cloned()); assert_eq!(&script[..], &comp[..]);
|
||||
script = script.push_int(-10000000); comp.extend([4u8, 128, 150, 152, 128].iter().cloned()); assert_eq!(&script[..], &comp[..]);
|
||||
|
||||
// data
|
||||
script.push_slice("NRA4VR".as_bytes()); comp.extend([6u8, 78, 82, 65, 52, 86, 82].iter().cloned()); assert_eq!(&script[..], &comp[..]);
|
||||
script = script.push_slice("NRA4VR".as_bytes()); comp.extend([6u8, 78, 82, 65, 52, 86, 82].iter().cloned()); assert_eq!(&script[..], &comp[..]);
|
||||
|
||||
// opcodes
|
||||
script.push_opcode(opcodes::All::OP_CHECKSIG); comp.push(0xACu8); assert_eq!(&script[..], &comp[..]);
|
||||
script.push_opcode(opcodes::All::OP_CHECKSIG); comp.push(0xACu8); assert_eq!(&script[..], &comp[..]);
|
||||
script = script.push_opcode(opcodes::All::OP_CHECKSIG); comp.push(0xACu8); assert_eq!(&script[..], &comp[..]);
|
||||
script = script.push_opcode(opcodes::All::OP_CHECKSIG); comp.push(0xACu8); assert_eq!(&script[..], &comp[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn script_builder() {
|
||||
// from txid 3bb5e6434c11fb93f64574af5d116736510717f2c595eb45b52c28e31622dfff which was in my mempool when I wrote the test
|
||||
let script = Builder::new().push_opcode(opcodes::All::OP_DUP)
|
||||
.push_opcode(opcodes::All::OP_HASH160)
|
||||
.push_slice(&"16e1ae70ff0fa102905d4af297f6912bda6cce19".from_hex().unwrap())
|
||||
.push_opcode(opcodes::All::OP_EQUALVERIFY)
|
||||
.push_opcode(opcodes::All::OP_CHECKSIG)
|
||||
.into_script();
|
||||
assert_eq!(&format!("{:x}", script), "76a91416e1ae70ff0fa102905d4af297f6912bda6cce1988ac");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2824,7 +2838,7 @@ mod test {
|
|||
let mut script = Builder::new();
|
||||
assert!(script.clone().into_script().evaluate(&s, &mut vec![], None, None).is_ok());
|
||||
|
||||
script.push_opcode(opcodes::All::OP_RETURN);
|
||||
script = script.push_opcode(opcodes::All::OP_RETURN);
|
||||
assert!(script.clone().into_script().evaluate(&s, &mut vec![], None, None).is_err());
|
||||
}
|
||||
|
||||
|
|
|
@ -76,22 +76,22 @@ impl Address {
|
|||
/// Generates a script pubkey spending to this address
|
||||
#[inline]
|
||||
pub fn script_pubkey(&self) -> script::Script {
|
||||
let mut script = script::Builder::new();
|
||||
match self.ty {
|
||||
Type::PubkeyHash => {
|
||||
script.push_opcode(opcodes::All::OP_DUP);
|
||||
script.push_opcode(opcodes::All::OP_HASH160);
|
||||
script.push_slice(&self.hash[..]);
|
||||
script.push_opcode(opcodes::All::OP_EQUALVERIFY);
|
||||
script.push_opcode(opcodes::All::OP_CHECKSIG);
|
||||
script::Builder::new()
|
||||
.push_opcode(opcodes::All::OP_DUP)
|
||||
.push_opcode(opcodes::All::OP_HASH160)
|
||||
.push_slice(&self.hash[..])
|
||||
.push_opcode(opcodes::All::OP_EQUALVERIFY)
|
||||
.push_opcode(opcodes::All::OP_CHECKSIG)
|
||||
}
|
||||
Type::ScriptHash => {
|
||||
script.push_opcode(opcodes::All::OP_HASH160);
|
||||
script.push_slice(&self.hash[..]);
|
||||
script.push_opcode(opcodes::All::OP_EQUAL);
|
||||
script::Builder::new()
|
||||
.push_opcode(opcodes::All::OP_HASH160)
|
||||
.push_slice(&self.hash[..])
|
||||
.push_opcode(opcodes::All::OP_EQUAL)
|
||||
}
|
||||
}
|
||||
script.into_script()
|
||||
}.into_script()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -113,14 +113,14 @@ impl Template {
|
|||
let mut key_index = 0;
|
||||
let mut ret = script::Builder::new();
|
||||
for elem in &self.0 {
|
||||
match *elem {
|
||||
ret = match *elem {
|
||||
TemplateElement::Op(opcode) => ret.push_opcode(opcode),
|
||||
TemplateElement::Key => {
|
||||
if key_index == keys.len() {
|
||||
return Err(Error::TooFewKeys(key_index));
|
||||
}
|
||||
ret.push_slice(&keys[key_index].serialize_vec(&secp, true)[..]);
|
||||
key_index += 1;
|
||||
ret.push_slice(&keys[key_index - 1].serialize_vec(&secp, true)[..])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -215,19 +215,19 @@ pub fn untemplate(script: &script::Script) -> Result<(Template, Vec<PublicKey>),
|
|||
match instruction {
|
||||
script::Instruction::PushBytes(data) => {
|
||||
let n = data.len();
|
||||
match PublicKey::from_slice(&secp, data) {
|
||||
ret = match PublicKey::from_slice(&secp, data) {
|
||||
Ok(key) => {
|
||||
if n == 65 { return Err(Error::UncompressedKey); }
|
||||
if mode == Mode::SeekingCheckMulti { return Err(Error::ExpectedChecksig); }
|
||||
retkeys.push(key);
|
||||
ret.push_opcode(opcodes::All::from(PUBKEY));
|
||||
mode = Mode::CopyingKeys;
|
||||
ret.push_opcode(opcodes::All::from(PUBKEY))
|
||||
}
|
||||
Err(_) => {
|
||||
// Arbitrary pushes are only allowed before we've found any keys.
|
||||
// Otherwise we have to wait for a N CHECKSIG pair.
|
||||
match mode {
|
||||
Mode::SeekingKeys => { ret.push_slice(data); }
|
||||
Mode::SeekingKeys => { ret.push_slice(data) }
|
||||
Mode::CopyingKeys => { return Err(Error::ExpectedKey); },
|
||||
Mode::SeekingCheckMulti => { return Err(Error::ExpectedChecksig); }
|
||||
}
|
||||
|
@ -257,7 +257,7 @@ pub fn untemplate(script: &script::Script) -> Result<(Template, Vec<PublicKey>),
|
|||
// All other opcodes do nothing
|
||||
_ => {}
|
||||
}
|
||||
ret.push_opcode(op);
|
||||
ret = ret.push_opcode(op);
|
||||
}
|
||||
script::Instruction::Error(e) => { return Err(Error::Script(e)); }
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue