Fixed extrinsic encoding (#794)

* Fixed extrinsic encoding

* Reserve heuristic

* Fixed no-std build
This commit is contained in:
Arkadiy Paronyan
2018-09-25 18:01:10 +02:00
committed by Gav Wood
parent 68e3e3ee11
commit a613c62dc1
4 changed files with 74 additions and 40 deletions
@@ -35,3 +35,28 @@ pub use self::checked_extrinsic::CheckedExtrinsic;
pub use self::header::Header;
pub use self::block::{Block, SignedBlock, BlockId};
pub use self::digest::{Digest, DigestItem, DigestItemRef};
use codec::Encode;
use rstd::prelude::*;
fn encode_with_vec_prefix<T: Encode, F: Fn(&mut Vec<u8>)>(encoder: F) -> Vec<u8> {
let size = ::rstd::mem::size_of::<T>();
let reserve = match size {
0...0b00111111 => 1,
0...0b00111111_11111111 => 2,
_ => 4,
};
let mut v = Vec::with_capacity(reserve + size);
v.resize(reserve, 0);
encoder(&mut v);
// need to prefix with the total length to ensure it's binary comptible with
// Vec<u8>.
let mut length: Vec<()> = Vec::new();
length.resize(v.len() - reserve, ());
length.using_encoded(|s| {
v.splice(0..reserve, s.iter().cloned());
});
v
}
@@ -103,9 +103,9 @@ where
fn decode<I: Input>(input: &mut I) -> Option<Self> {
// This is a little more complicated than usual since the binary format must be compatible
// with substrate's generic `Vec<u8>` type. Basically this just means accepting that there
// will be a prefix of u32, which has the total number of bytes following (we don't need
// will be a prefix of vector length (we don't need
// to use this).
let _length_do_not_remove_me_see_above: u32 = Decode::decode(input)?;
let _length_do_not_remove_me_see_above: Vec<()> = Decode::decode(input)?;
Some(UncheckedExtrinsic {
signature: Decode::decode(input)?,
@@ -123,19 +123,10 @@ where
Call: Encode,
{
fn encode(&self) -> Vec<u8> {
let mut v = Vec::new();
// need to prefix with the total length as u32 to ensure it's binary comptible with
// Vec<u8>. we'll make room for it here, then overwrite once we know the length.
v.extend(&[0u8; 4]);
self.signature.encode_to(&mut v);
self.function.encode_to(&mut v);
let length = (v.len() - 4) as u32;
length.using_encoded(|s| v[0..4].copy_from_slice(s));
v
super::encode_with_vec_prefix::<Self, _>(|v| {
self.signature.encode_to(v);
self.function.encode_to(v);
})
}
}
@@ -150,3 +141,20 @@ impl<Address, Index, Call, Signature> fmt::Debug for UncheckedExtrinsic<Address,
write!(f, "UncheckedExtrinsic({:?}, {:?})", self.signature.as_ref().map(|x| (&x.0, &x.2)), self.function)
}
}
#[cfg(test)]
mod test {
use codec::{Decode, Encode};
use super::UncheckedExtrinsic;
#[test]
fn encoding_matches_vec() {
type Extrinsic = UncheckedExtrinsic<u32, u32, u32, u32>;
let ex = Extrinsic::new_unsigned(42);
let encoded = ex.encode();
let decoded = Extrinsic::decode(&mut encoded.as_slice()).unwrap();
assert_eq!(decoded, ex);
let as_vec: Vec<u8> = Decode::decode(&mut encoded.as_slice()).unwrap();
assert_eq!(as_vec.encode(), encoded);
}
}
@@ -113,9 +113,9 @@ where
fn decode<I: Input>(input: &mut I) -> Option<Self> {
// This is a little more complicated than usual since the binary format must be compatible
// with substrate's generic `Vec<u8>` type. Basically this just means accepting that there
// will be a prefix of u32, which has the total number of bytes following (we don't need
// will be a prefix of vector length (we don't need
// to use this).
let _length_do_not_remove_me_see_above: u32 = Decode::decode(input)?;
let _length_do_not_remove_me_see_above: Vec<()> = Decode::decode(input)?;
let version = input.read_byte()?;
@@ -141,28 +141,19 @@ where
Call: Encode,
{
fn encode(&self) -> Vec<u8> {
let mut v = Vec::new();
// need to prefix with the total length as u32 to ensure it's binary comptible with
// Vec<u8>. we'll make room for it here, then overwrite once we know the length.
v.extend(&[0u8; 4]);
// 1 byte version id.
match self.signature.as_ref() {
Some(s) => {
v.push(TRANSACTION_VERSION | 0b1000_0000);
s.encode_to(&mut v);
super::encode_with_vec_prefix::<Self, _>(|v| {
// 1 byte version id.
match self.signature.as_ref() {
Some(s) => {
v.push(TRANSACTION_VERSION | 0b1000_0000);
s.encode_to(v);
}
None => {
v.push(TRANSACTION_VERSION & 0b0111_1111);
}
}
None => {
v.push(TRANSACTION_VERSION & 0b0111_1111);
}
}
self.function.encode_to(&mut v);
let length = (v.len() - 4) as u32;
length.using_encoded(|s| v[0..4].copy_from_slice(s));
v
self.function.encode_to(v);
})
}
}
@@ -275,4 +266,14 @@ mod tests {
assert!(ux.is_signed());
assert_eq!(<Ex as Checkable<TestContext>>::check(ux, &TestContext), Err("bad signature in extrinsic"));
}
}
#[test]
fn encoding_matches_vec() {
let ex = Ex::new_unsigned(DUMMY_FUNCTION);
let encoded = ex.encode();
let decoded = Ex::decode(&mut encoded.as_slice()).unwrap();
assert_eq!(decoded, ex);
let as_vec: Vec<u8> = Decode::decode(&mut encoded.as_slice()).unwrap();
assert_eq!(as_vec.encode(), encoded);
}
}