Skip to content

Commit be4dffb

Browse files
committed
Merge rust-bitcoin#3411: script: refactor push_int_unchecked and test push_int overflow
a33bcd3 test: ensure push_int check i32::MIN of overflow error (Chris Hyunhum Cho) c9988ba refactor: use match for OP_N push in push_int_unchecked (Chris Hyunhum Cho) Pull request description: Follow up rust-bitcoin#3392 c9988ba - refactor `push_int_unchecked` with match expression for cleaner code(many thanks for tcharding rust-bitcoin#3407). a33bcd3 - ensure newly introduced safe `push_int` function as expected, testing if returns `Error::NumericOverflow` when `n` is `i32::MIN` ACKs for top commit: tcharding: ACK a33bcd3 apoelstra: ACK a33bcd3 successfully ran local tests Tree-SHA512: 14f19d37f35b47e148b40c5017f0270c534c136d86be0c061cb476e1693130c5fc1bfc45a6f7c75a473022490c5f4e061cbc02640b1a616619ae721116e3cd54
2 parents 3a9f111 + a33bcd3 commit be4dffb

File tree

2 files changed

+23
-14
lines changed

2 files changed

+23
-14
lines changed

bitcoin/src/blockdata/script/builder.rs

+17-14
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use core::fmt;
55
use super::{opcode_to_verify, write_scriptint, Error, PushBytes, Script, ScriptBuf};
66
use crate::locktime::absolute;
77
use crate::opcodes::all::*;
8-
use crate::opcodes::{self, Opcode};
8+
use crate::opcodes::Opcode;
99
use crate::prelude::Vec;
1010
use crate::script::{ScriptBufExt as _, ScriptBufExtPriv as _, ScriptExtPriv as _};
1111
use crate::Sequence;
@@ -46,20 +46,23 @@ impl Builder {
4646
///
4747
/// Integers are encoded as little-endian signed-magnitude numbers, but there are dedicated
4848
/// opcodes to push some small integers.
49-
/// It doesn't check whether the integer in the range of [-2^31 +1...2^31 -1].
49+
///
50+
/// This function implements `CScript::push_int64` from Core `script.h`.
51+
///
52+
/// > Numeric opcodes (OP_1ADD, etc) are restricted to operating on 4-byte integers.
53+
/// > The semantics are subtle, though: operands must be in the range [-2^31 +1...2^31 -1],
54+
/// > but results may overflow (and are valid as long as they are not used in a subsequent
55+
/// > numeric operation). CScriptNum enforces those semantics by storing results as
56+
/// > an int64 and allowing out-of-range values to be returned as a vector of bytes but
57+
/// > throwing an exception if arithmetic is done or the result is interpreted as an integer.
58+
///
59+
/// Does not check whether `n` is in the range of [-2^31 +1...2^31 -1].
5060
pub fn push_int_unchecked(self, n: i64) -> Builder {
51-
// We can special-case -1, 1-16
52-
if n == -1 || (1..=16).contains(&n) {
53-
let opcode = Opcode::from((n - 1 + opcodes::OP_TRUE.to_u8() as i64) as u8);
54-
self.push_opcode(opcode)
55-
}
56-
// We can also special-case zero
57-
else if n == 0 {
58-
self.push_opcode(opcodes::OP_0)
59-
}
60-
// Otherwise encode it as data
61-
else {
62-
self.push_int_non_minimal(n)
61+
match n {
62+
-1 => self.push_opcode(OP_PUSHNUM_NEG1),
63+
0 => self.push_opcode(OP_PUSHBYTES_0),
64+
1..=16 => self.push_opcode(Opcode::from(n as u8 + (OP_PUSHNUM_1.to_u8() - 1))),
65+
_ => self.push_int_non_minimal(n),
6366
}
6467
}
6568

bitcoin/src/blockdata/script/tests.rs

+6
Original file line numberDiff line numberDiff line change
@@ -916,3 +916,9 @@ fn instruction_script_num_parse() {
916916
Some(Ok(Instruction::PushBytes(PushBytes::empty()))),
917917
);
918918
}
919+
920+
#[test]
921+
fn script_push_int_overflow() {
922+
// Only errors if `data == i32::MIN` (CScriptNum cannot have value -2^31).
923+
assert_eq!(Builder::new().push_int(i32::MIN), Err(Error::NumericOverflow));
924+
}

0 commit comments

Comments
 (0)