Skip to content

Commit 59064a6

Browse files
koushirovmx
authored andcommitted
fix: scale-codec for multihash
BREAKING CHANGE: Write the digest of multihash directly to dest when encoding multihash, since we have known the size of digest. We do not choose to encode &[u8] directly, because it will add extra bytes (the compact length of digest) at the beinging of encoding result. For a valid multihash, the length of digest must equal to `size` field of multihash. Therefore, we can only read raw bytes whose length is equal to `size` when decoding. And the digest of Multihash<U64> is of type [u8; 64], taking Code::Sha2_256 as an example, if the digest is treated as &[u8] when encoding, useless data will be added to the encoding result. Signed-off-by: koushiro <koushiro.cqx@gmail.com>
1 parent 4a895f7 commit 59064a6

File tree

2 files changed

+55
-12
lines changed

2 files changed

+55
-12
lines changed

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@
5252
#![deny(missing_docs)]
5353
#![cfg_attr(not(feature = "std"), no_std)]
5454

55+
#[cfg(not(feature = "std"))]
56+
extern crate alloc;
57+
5558
#[cfg(any(test, feature = "arb"))]
5659
mod arb;
5760
mod error;

src/multihash.rs

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use crate::hasher::{Digest, Size};
22
use crate::Error;
3+
#[cfg(all(not(feature = "std"), feature = "scale-codec"))]
4+
use alloc::vec;
35
use core::convert::TryFrom;
46
#[cfg(feature = "std")]
57
use core::convert::TryInto;
@@ -186,7 +188,12 @@ impl parity_scale_codec::Encode for Multihash<crate::U32> {
186188
digest.copy_from_slice(&self.digest);
187189
self.code.encode_to(dest);
188190
self.size.encode_to(dest);
189-
digest.encode_to(dest);
191+
// **NOTE** We write the digest directly to dest, since we have known the size of digest.
192+
//
193+
// We do not choose to encode &[u8] directly, because it will add extra bytes (the compact length of digest).
194+
// For a valid multihash, the length of digest must equal to `size`.
195+
// Therefore, we can only read raw bytes whose length is equal to `size` when decoding.
196+
dest.write(&digest[..self.size as usize]);
190197
}
191198
}
192199

@@ -198,11 +205,17 @@ impl parity_scale_codec::Decode for Multihash<crate::U32> {
198205
fn decode<DecIn: parity_scale_codec::Input>(
199206
input: &mut DecIn,
200207
) -> Result<Self, parity_scale_codec::Error> {
208+
let code = parity_scale_codec::Decode::decode(input)?;
209+
let size = parity_scale_codec::Decode::decode(input)?;
210+
// For a valid multihash, the length of digest must equal to the size.
211+
let mut buf = vec![0u8; size as usize];
212+
input.read(&mut buf)?;
201213
Ok(Multihash {
202-
code: parity_scale_codec::Decode::decode(input)?,
203-
size: parity_scale_codec::Decode::decode(input)?,
214+
code,
215+
size,
204216
digest: {
205-
let digest = <[u8; 32]>::decode(input)?;
217+
let mut digest = [0u8; 32];
218+
digest[..size as usize].copy_from_slice(&buf);
206219
GenericArray::clone_from_slice(&digest)
207220
},
208221
})
@@ -216,7 +229,12 @@ impl parity_scale_codec::Encode for Multihash<crate::U64> {
216229
digest.copy_from_slice(&self.digest);
217230
self.code.encode_to(dest);
218231
self.size.encode_to(dest);
219-
digest.encode_to(dest);
232+
// **NOTE** We write the digest directly to dest, since we have known the size of digest.
233+
//
234+
// We do not choose to encode &[u8] directly, because it will add extra bytes (the compact length of digest).
235+
// For a valid multihash, the length of digest must equal to `size`.
236+
// Therefore, we can only read raw bytes whose length is equal to `size` when decoding.
237+
dest.write(&digest[..self.size as usize]);
220238
}
221239
}
222240

@@ -228,11 +246,17 @@ impl parity_scale_codec::Decode for Multihash<crate::U64> {
228246
fn decode<DecIn: parity_scale_codec::Input>(
229247
input: &mut DecIn,
230248
) -> Result<Self, parity_scale_codec::Error> {
249+
let code = parity_scale_codec::Decode::decode(input)?;
250+
let size = parity_scale_codec::Decode::decode(input)?;
251+
// For a valid multihash, the length of digest must equal to the size.
252+
let mut buf = vec![0u8; size as usize];
253+
input.read(&mut buf)?;
231254
Ok(Multihash {
232-
code: parity_scale_codec::Decode::decode(input)?,
233-
size: parity_scale_codec::Decode::decode(input)?,
255+
code,
256+
size,
234257
digest: {
235-
let digest = <[u8; 64]>::decode(input)?;
258+
let mut digest = [0u8; 64];
259+
digest[..size as usize].copy_from_slice(&buf);
236260
GenericArray::clone_from_slice(&digest)
237261
},
238262
})
@@ -302,12 +326,28 @@ mod tests {
302326
#[test]
303327
#[cfg(feature = "scale-codec")]
304328
fn test_scale() {
329+
use crate::{Hasher, Sha2_256};
305330
use parity_scale_codec::{Decode, Encode};
306331

307-
let mh = Multihash::<crate::U32>::default();
308-
let bytes = mh.encode();
309-
let mh2: Multihash<crate::U32> = Decode::decode(&mut &bytes[..]).unwrap();
310-
assert_eq!(mh, mh2);
332+
let mh1 = Multihash::<crate::U32>::wrap(
333+
Code::Sha2_256.into(),
334+
Sha2_256::digest(b"hello world").as_ref(),
335+
)
336+
.unwrap();
337+
// println!("mh1: code = {}, size = {}, digest = {:?}", mh1.code(), mh1.size(), mh1.digest());
338+
let mh1_bytes = mh1.encode();
339+
// println!("Multihash<32>: {}", hex::encode(&mh1_bytes));
340+
let mh2: Multihash<crate::U32> = Decode::decode(&mut &mh1_bytes[..]).unwrap();
341+
assert_eq!(mh1, mh2);
342+
343+
let mh3: Multihash<crate::U64> = Code::Sha2_256.digest(b"hello world");
344+
// println!("mh3: code = {}, size = {}, digest = {:?}", mh3.code(), mh3.size(), mh3.digest());
345+
let mh3_bytes = mh3.encode();
346+
// println!("Multihash<64>: {}", hex::encode(&mh3_bytes));
347+
let mh4: Multihash<crate::U64> = Decode::decode(&mut &mh3_bytes[..]).unwrap();
348+
assert_eq!(mh3, mh4);
349+
350+
assert_eq!(mh1_bytes, mh3_bytes);
311351
}
312352

313353
#[test]

0 commit comments

Comments
 (0)