simplified iteration over script opcodes

This commit is contained in:
debris 2016-09-14 23:54:52 +02:00
parent ea409efccd
commit 601a8277a8
2 changed files with 64 additions and 56 deletions

View File

@ -1917,9 +1917,6 @@ mod tests {
// https://blockchain.info/rawtx/02b082113e35d5386285094c2829e7e2963fa0b5369fb7f4b79c4c90877dcd3d
#[test]
fn test_check_transaction_multisig() {
let redeem: Script =
"524104a882d414e478039cd5b52a92ffb13dd5e6bd4515497439dffd691a0f12af9575fa349b5694ed3155b136f09e63975a1700c9f4d4df849323dac06cf3bd6458cd41046ce31db9bdd543e72fe3039a1f1c047dab87037c36a669ff90e28da1848f640de68c2fe913d363a51154a0c62d7adea1b822d05035077418267b1a1379790187410411ffd36c70776538d079fbae117dc38effafb33304af83ce4894589747aee1ef992f63280567f52f5ba870678b4ab4ff6c8ea600bd217870a8b4f1f09f3a8e8353ae".into();
let tx: Transaction = "01000000013dcd7d87904c9cb7f4b79f36b5a03f96e2e729284c09856238d5353e1182b00200000000fd5e0100483045022100deeb1f13b5927b5e32d877f3c42a4b028e2e0ce5010fdb4e7f7b5e2921c1dcd2022068631cb285e8c1be9f061d2968a18c3163b780656f30a049effee640e80d9bff01483045022100ee80e164622c64507d243bd949217d666d8b16486e153ac6a1f8e04c351b71a502203691bef46236ca2b4f5e60a82a853a33d6712d6a1e7bf9a65e575aeb7328db8c014cc9524104a882d414e478039cd5b52a92ffb13dd5e6bd4515497439dffd691a0f12af9575fa349b5694ed3155b136f09e63975a1700c9f4d4df849323dac06cf3bd6458cd41046ce31db9bdd543e72fe3039a1f1c047dab87037c36a669ff90e28da1848f640de68c2fe913d363a51154a0c62d7adea1b822d05035077418267b1a1379790187410411ffd36c70776538d079fbae117dc38effafb33304af83ce4894589747aee1ef992f63280567f52f5ba870678b4ab4ff6c8ea600bd217870a8b4f1f09f3a8e8353aeffffffff0130d90000000000001976a914569076ba39fc4ff6a2291d9ea9196d8c08f9c7ab88ac00000000".into();
let signer: TransactionInputSigner = tx.into();
let checker = TransactionSignatureChecker {

View File

@ -92,6 +92,45 @@ impl Script {
Opcode::from_u8(self.data[position]).ok_or(Error::BadOpcode)
}
pub fn get_instruction(&self, position: usize) -> Result<Instruction, Error> {
let opcode = try!(self.get_opcode(position));
let instruction = match opcode {
Opcode::OP_PUSHDATA1 |
Opcode::OP_PUSHDATA2 |
Opcode::OP_PUSHDATA4 => {
let len = match opcode {
Opcode::OP_PUSHDATA1 => 1,
Opcode::OP_PUSHDATA2 => 2,
_ => 4,
};
let slice = try!(self.take(position + 1, len));
let n = try!(read_usize(slice, len));
let bytes = try!(self.take_checked(position + 1 + len, n));
Instruction {
opcode: opcode,
step: len + n + 1,
data: Some(bytes),
}
},
o if o >= Opcode::OP_0 && o <= Opcode::OP_PUSHBYTES_75 => {
let bytes = try!(self.take_checked(position+ 1, opcode as usize));
Instruction {
opcode: o,
step: opcode as usize + 1,
data: Some(bytes),
}
},
_ => Instruction {
opcode: opcode,
step: 1,
data: None,
}
};
Ok(instruction)
}
#[inline]
pub fn take(&self, offset: usize, len: usize) -> Result<&[u8], Error> {
if offset + len > self.data.len() {
@ -114,37 +153,21 @@ impl Script {
pub fn without_separators(&self) -> Script {
let mut pc = 0;
let mut result = Vec::new();
while pc < self.len() {
match self.get_opcode(pc) {
Ok(opcode @ Opcode::OP_PUSHDATA1) |
Ok(opcode @ Opcode::OP_PUSHDATA2) |
Ok(opcode @ Opcode::OP_PUSHDATA4) => {
let len = match opcode {
Opcode::OP_PUSHDATA1 => 1,
Opcode::OP_PUSHDATA2 => 2,
_ => 4,
};
match self.get_instruction(pc) {
Ok(instruction) => {
if instruction.opcode != Opcode::OP_CODESEPARATOR {
result.extend_from_slice(&self[pc..pc + instruction.step]);
}
let slice = match self.take(pc + 1, len) {
Ok(slice) => slice,
_ => {
result.extend_from_slice(&self[pc..]);
break;
}
};
let n = read_usize(slice, len).expect("slice.len() is equal len");
result.extend(&self[pc..pc + len + n + 1]);
pc += len + n;
pc += instruction.step;
},
Ok(o) if o >= Opcode::OP_0 && o <= Opcode::OP_PUSHBYTES_75 => {
result.extend(&self[pc..pc + o as usize + 1]);
pc += o as usize;
},
Ok(Opcode::OP_CODESEPARATOR) => {},
_ => result.push(self[pc]),
_ => {
result.push(self[pc]);
pc += 1;
}
}
pc += 1;
}
Script::new(result)
@ -159,6 +182,12 @@ impl ops::Deref for Script {
}
}
pub struct Instruction<'a> {
opcode: Opcode,
step: usize,
data: Option<&'a [u8]>,
}
pub fn read_usize(data: &[u8], size: usize) -> Result<usize, Error> {
if data.len() < size {
return Err(Error::BadOpcode);
@ -190,36 +219,18 @@ impl fmt::Debug for Script {
impl fmt::Display for Script {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut pc = 0;
while pc < self.len() {
let opcode = try_or_fmt!(f, self.get_opcode(pc));
let instruction = try_or_fmt!(f, self.get_instruction(pc));
match opcode {
Opcode::OP_PUSHDATA1 |
Opcode::OP_PUSHDATA2 |
Opcode::OP_PUSHDATA4 => {
let len = match opcode {
Opcode::OP_PUSHDATA1 => 1,
Opcode::OP_PUSHDATA2 => 2,
_ => 4,
};
let slice = try_or_fmt!(f, self.take(pc + 1, len));
let n = try_or_fmt!(f, read_usize(slice, len));
let bytes = try_or_fmt!(f, self.take_checked(pc + 1 + len, n));
pc += len + n;
try!(writeln!(f, "{:?} 0x{}", opcode, bytes.to_hex()));
},
o if o >= Opcode::OP_0 && o <= Opcode::OP_PUSHBYTES_75 => {
let bytes = try_or_fmt!(f, self.take_checked(pc + 1, opcode as usize));
pc += opcode as usize;
try!(writeln!(f, "{:?} 0x{}", opcode, bytes.to_hex()));
},
_ => {
try!(writeln!(f, "{:?}", opcode));
},
match instruction.data {
Some(data) => try!(writeln!(f, "{:?} 0x{}", instruction.opcode, data.to_hex())),
None => try!(writeln!(f, "{:?}", instruction.opcode)),
}
pc += 1;
pc += instruction.step;
}
Ok(())
}
}