Repository: paritytech/parity-common (rust)

Issue:
bool false RLP encoding should match u8
[Version 0.5.0 of the rlp crate](https://docs.rs/rlp/0.5.0/rlp/) encodes false to 0x00, but u8 (byte) zero to 0x80.

The [RLP library in Geth](https://github.com/ethereum/go-ethereum/blob/master/rlp/decode.go#L41) expects 0x80 for `false`, and produces this error:

```
rlp: non-canonical integer (leading zero bytes) for bool
```

This is how it behaves today:

```rust
    #[test]
    fn encode_false() {
        #[derive(rlp_derive::RlpEncodable)]
        struct MeHasBool {
            flag: bool,
        }

        assert_eq!(*rlp::encode(&MeHasBool { flag: false }), [0xC1, 0x00]);
    }

    #[test]
    fn encode_zero_byte() {
        #[derive(rlp_derive::RlpEncodable)]
        struct MeHasByte {
            flag: u8,
        }

        assert_eq!(*rlp::encode(&MeHasByte { flag: 0 }), [0xC1, 0x80]);
    }
```

I'd expect both encodings match.

Note: the tests above also use [rlp-derive 0.1.0](https://docs.rs/rlp-derive/0.1.0/rlp_derive/)


Pull request description:
rlp: fix bool encoding/decoding
Fixes #571.

I don't know if `bool`s are actually used anywhere where rlp encoding is expected, but if they are, this is a breaking change.

Patch:
diff --git a/rlp/CHANGELOG.md b/rlp/CHANGELOG.md
index afd84d9..189ff85 100644
--- a/rlp/CHANGELOG.md
+++ b/rlp/CHANGELOG.md
@@ -5,6 +5,7 @@ The format is based on [Keep a Changelog].
 [Keep a Changelog]: http://keepachangelog.com/en/1.0.0/
 
 ## [Unreleased]
+- Fix rlp encoding/decoding for bool. [#572](https://github.com/paritytech/parity-common/pull/572)
 
 ## [0.5.0] - 2021-01-05
 ### Breaking
diff --git a/rlp/src/impls.rs b/rlp/src/impls.rs
index 7aa0139..aeedb11 100644
--- a/rlp/src/impls.rs
+++ b/rlp/src/impls.rs
@@ -48,17 +48,19 @@ impl<T: Decodable> Decodable for Box<T> {
 
 impl Encodable for bool {
 	fn rlp_append(&self, s: &mut RlpStream) {
-		s.encoder().encode_iter(once(if *self { 1u8 } else { 0 }));
+		let as_uint = u8::from(*self);
+		Encodable::rlp_append(&as_uint, s);
 	}
 }
 
 impl Decodable for bool {
 	fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
-		rlp.decoder().decode_value(|bytes| match bytes.len() {
+		let as_uint = <u8 as Decodable>::decode(rlp)?;
+		match as_uint {
 			0 => Ok(false),
-			1 => Ok(bytes[0] != 0),
-			_ => Err(DecoderError::RlpIsTooBig),
-		})
+			1 => Ok(true),
+			_ => Err(DecoderError::Custom("invalid boolean value")),
+		}
 	}
 }
 


Test patch:
diff --git a/rlp/tests/tests.rs b/rlp/tests/tests.rs
index 9ba2e35..ec5ed62 100644
--- a/rlp/tests/tests.rs
+++ b/rlp/tests/tests.rs
@@ -619,10 +619,19 @@ fn test_rlp_is_int() {
 	for b in 0xb8..0xc0 {
 		let data: Vec<u8> = vec![b];
 		let rlp = Rlp::new(&data);
-		assert_eq!(rlp.is_int(), false);
+		assert!(!rlp.is_int());
 	}
 }
 
+#[test]
+fn test_bool_same_as_int() {
+	assert_eq!(rlp::encode(&false), rlp::encode(&0x00u8));
+	assert_eq!(rlp::encode(&true), rlp::encode(&0x01u8));
+	let two = rlp::encode(&0x02u8);
+	let invalid: Result<bool, _> = rlp::decode(&two);
+	invalid.unwrap_err();
+}
+
 // test described in
 //
 // https://github.com/paritytech/parity-common/issues/49