Merge rust-bitcoin/rust-bitcoin#1244: BIP 34: Check last minimal encoding accoriding of heights
840520042a
BIP 34: Check last minimal encoding of encoded heights in coinbase tx (sanket1729)fd3e01d6e3
Fix minimality of reading/writing CScriptNums (sanket1729) Pull request description: See: https://github.com/bitcoin/bips/blob/master/bip-0034.mediawiki The BIP was updated with commit:08844fd6ef
Found while reviewing #1240 Also cleanly deals with negative heights as errors. ACKs for top commit: apoelstra: ACK840520042a
tcharding: ACK840520042a
Tree-SHA512: 85eb1fbb428fb600f864390c3ac2e8e62efc1fc4b03b842e384ddb4ed9273c66d2274d0de219f1c7a6bad7b02bd8723f8f257c864577614df674b44bfc02010a
This commit is contained in:
commit
94a4c51c10
|
@ -379,7 +379,7 @@ impl Block {
|
||||||
// Citing the spec:
|
// Citing the spec:
|
||||||
// Add height as the first item in the coinbase transaction's scriptSig,
|
// Add height as the first item in the coinbase transaction's scriptSig,
|
||||||
// and increase block version to 2. The format of the height is
|
// and increase block version to 2. The format of the height is
|
||||||
// "serialized CScript" -- first byte is number of bytes in the number
|
// "minimally encoded serialized CScript"" -- first byte is number of bytes in the number
|
||||||
// (will be 0x03 on main net for the next 150 or so years with 2^23-1
|
// (will be 0x03 on main net for the next 150 or so years with 2^23-1
|
||||||
// blocks), following bytes are little-endian representation of the
|
// blocks), following bytes are little-endian representation of the
|
||||||
// number (including a sign bit). Height is the height of the mined
|
// number (including a sign bit). Height is the height of the mined
|
||||||
|
@ -393,14 +393,14 @@ impl Block {
|
||||||
let input = cb.input.first().ok_or(Bip34Error::NotPresent)?;
|
let input = cb.input.first().ok_or(Bip34Error::NotPresent)?;
|
||||||
let push = input.script_sig.instructions_minimal().next().ok_or(Bip34Error::NotPresent)?;
|
let push = input.script_sig.instructions_minimal().next().ok_or(Bip34Error::NotPresent)?;
|
||||||
match push.map_err(|_| Bip34Error::NotPresent)? {
|
match push.map_err(|_| Bip34Error::NotPresent)? {
|
||||||
script::Instruction::PushBytes(b) if b.len() <= 8 => {
|
script::Instruction::PushBytes(b) => {
|
||||||
// Expand the push to exactly 8 bytes (LE).
|
// Check that the number is encoded in the minimal way.
|
||||||
let mut full = [0; 8];
|
let h = script::read_scriptint(b).map_err(|_e| Bip34Error::UnexpectedPush(b.to_vec()))?;
|
||||||
full[0..b.len()].copy_from_slice(b);
|
if h < 0 {
|
||||||
Ok(util::endian::slice_to_u64_le(&full))
|
Err(Bip34Error::NegativeHeight)
|
||||||
}
|
} else {
|
||||||
script::Instruction::PushBytes(b) if b.len() > 8 => {
|
Ok(h as u64)
|
||||||
Err(Bip34Error::UnexpectedPush(b.to_vec()))
|
}
|
||||||
}
|
}
|
||||||
_ => Err(Bip34Error::NotPresent),
|
_ => Err(Bip34Error::NotPresent),
|
||||||
}
|
}
|
||||||
|
@ -417,6 +417,8 @@ pub enum Bip34Error {
|
||||||
NotPresent,
|
NotPresent,
|
||||||
/// The BIP34 push was larger than 8 bytes.
|
/// The BIP34 push was larger than 8 bytes.
|
||||||
UnexpectedPush(Vec<u8>),
|
UnexpectedPush(Vec<u8>),
|
||||||
|
/// The BIP34 push was negative.
|
||||||
|
NegativeHeight,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Bip34Error {
|
impl fmt::Display for Bip34Error {
|
||||||
|
@ -427,6 +429,7 @@ impl fmt::Display for Bip34Error {
|
||||||
Bip34Error::UnexpectedPush(ref p) => {
|
Bip34Error::UnexpectedPush(ref p) => {
|
||||||
write!(f, "unexpected byte push of > 8 bytes: {:?}", p)
|
write!(f, "unexpected byte push of > 8 bytes: {:?}", p)
|
||||||
}
|
}
|
||||||
|
Bip34Error::NegativeHeight => write!(f, "negative BIP34 height"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -438,7 +441,10 @@ impl std::error::Error for Bip34Error {
|
||||||
use self::Bip34Error::*;
|
use self::Bip34Error::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Unsupported | NotPresent | UnexpectedPush(_) => None,
|
Unsupported |
|
||||||
|
NotPresent |
|
||||||
|
UnexpectedPush(_) |
|
||||||
|
NegativeHeight => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -225,9 +225,9 @@ impl From<bitcoinconsensus::Error> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper to encode an integer in script format.
|
/// Helper to encode an integer in script(minimal CScriptNum) format.
|
||||||
/// Writes bytes into the buffer and returns the number of bytes written.
|
/// Writes bytes into the buffer and returns the number of bytes written.
|
||||||
fn write_scriptint(out: &mut [u8; 8], n: i64) -> usize {
|
pub fn write_scriptint(out: &mut [u8; 8], n: i64) -> usize {
|
||||||
let mut len = 0;
|
let mut len = 0;
|
||||||
if n == 0 { return len; }
|
if n == 0 { return len; }
|
||||||
|
|
||||||
|
@ -256,7 +256,7 @@ fn write_scriptint(out: &mut [u8; 8], n: i64) -> usize {
|
||||||
len
|
len
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper to decode an integer in script format
|
/// Helper to decode an integer in script(minimal CScriptNum) format
|
||||||
/// Notice that this fails on overflow: the result is the same as in
|
/// Notice that this fails on overflow: the result is the same as in
|
||||||
/// bitcoind, that only 4-byte signed-magnitude values may be read as
|
/// bitcoind, that only 4-byte signed-magnitude values may be read as
|
||||||
/// numbers. They can be added or subtracted (and a long time ago,
|
/// numbers. They can be added or subtracted (and a long time ago,
|
||||||
|
@ -272,8 +272,26 @@ fn write_scriptint(out: &mut [u8; 8], n: i64) -> usize {
|
||||||
/// This is basically a ranged type implementation.
|
/// This is basically a ranged type implementation.
|
||||||
pub fn read_scriptint(v: &[u8]) -> Result<i64, Error> {
|
pub fn read_scriptint(v: &[u8]) -> Result<i64, Error> {
|
||||||
let len = v.len();
|
let len = v.len();
|
||||||
if len == 0 { return Ok(0); }
|
|
||||||
if len > 4 { return Err(Error::NumericOverflow); }
|
if len > 4 { return Err(Error::NumericOverflow); }
|
||||||
|
let last = match v.last() {
|
||||||
|
Some(last) => last,
|
||||||
|
None => return Ok(0),
|
||||||
|
};
|
||||||
|
// Comment and code copied from bitcoin core:
|
||||||
|
// https://github.com/bitcoin/bitcoin/blob/447f50e4aed9a8b1d80e1891cda85801aeb80b4e/src/script/script.h#L247-L262
|
||||||
|
// If the most-significant-byte - excluding the sign bit - is zero
|
||||||
|
// then we're not minimal. Note how this test also rejects the
|
||||||
|
// negative-zero encoding, 0x80.
|
||||||
|
if (last & 0x7f) == 0 {
|
||||||
|
// One exception: if there's more than one byte and the most
|
||||||
|
// significant bit of the second-most-significant-byte is set
|
||||||
|
// it would conflict with the sign bit. An example of this case
|
||||||
|
// is +-255, which encode to 0xff00 and 0xff80 respectively.
|
||||||
|
// (big-endian).
|
||||||
|
if v.len() <= 1 || (v[v.len() - 2] & 0x80) == 0 {
|
||||||
|
return Err(Error::NonMinimalPush);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let (mut ret, sh) = v.iter()
|
let (mut ret, sh) = v.iter()
|
||||||
.fold((0, 0), |(acc, sh), n| (acc + ((*n as i64) << sh), sh + 8));
|
.fold((0, 0), |(acc, sh), n| (acc + ((*n as i64) << sh), sh + 8));
|
||||||
|
@ -1300,6 +1318,14 @@ mod test {
|
||||||
assert!(read_scriptint(&build_scriptint(-(1 << 31))).is_err());
|
assert!(read_scriptint(&build_scriptint(-(1 << 31))).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn non_minimal_scriptints() {
|
||||||
|
assert_eq!(read_scriptint(&[0x80, 0x00]), Ok(0x80));
|
||||||
|
assert_eq!(read_scriptint(&[0xff, 0x00]), Ok(0xff));
|
||||||
|
assert_eq!(read_scriptint(&[0x8f, 0x00, 0x00]), Err(Error::NonMinimalPush));
|
||||||
|
assert_eq!(read_scriptint(&[0x7f, 0x00]), Err(Error::NonMinimalPush));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn script_hashes() {
|
fn script_hashes() {
|
||||||
let script = hex_script!("410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac");
|
let script = hex_script!("410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac");
|
||||||
|
|
Loading…
Reference in New Issue