1use alloy_primitives::{Address, B256, FixedBytes, Keccak256, Signature, address, b256, hex};
7use alloy_signer::SignerSync;
8use alloy_signer_local::PrivateKeySigner;
9use bytes::{Bytes, BytesMut};
10use std::fmt;
11use std::marker::PhantomData;
12
13use crate::PrimitivesError;
14use crate::bmt::DEFAULT_BODY_SIZE;
15use crate::cache::OnceCache;
16use crate::chunk::error::{self, ChunkError};
17use crate::error::Result;
18
19use super::bmt_body::BmtBody;
20use super::traits::{BmtChunk, Chunk, ChunkAddress, ChunkHeader, ChunkMetadata};
21
22const ID_SIZE: usize = std::mem::size_of::<B256>();
24const SIGNATURE_SIZE: usize = 65;
25const MIN_SOC_FIELDS_SIZE: usize = ID_SIZE + SIGNATURE_SIZE;
26
27const DISPERSED_REPLICA_OWNER: Address = address!("0xdc5b20847f43d67928f49cd4f85d696b5a7617b5");
29const DISPERSED_REPLICA_OWNER_PK: B256 =
31 b256!("0x0100000000000000000000000000000000000000000000000000000000000000");
32
33#[derive(Debug, Clone)]
38pub struct SingleOwnerChunk<const BODY_SIZE: usize = DEFAULT_BODY_SIZE> {
39 header: SingleOwnerChunkHeader,
41 body: BmtBody<BODY_SIZE>,
43 chunk_address_cache: OnceCache<ChunkAddress>,
45 owner_cache: OnceCache<Address>,
47}
48
49#[derive(Debug, Clone)]
51pub struct SingleOwnerChunkMetadata {
52 id: B256,
54 signature: Signature,
56}
57
58impl SingleOwnerChunkMetadata {
59 pub const fn new(id: B256, signature: Signature) -> Self {
61 Self { id, signature }
62 }
63
64 pub const fn id(&self) -> B256 {
66 self.id
67 }
68
69 pub const fn signature(&self) -> &Signature {
71 &self.signature
72 }
73}
74
75impl ChunkMetadata for SingleOwnerChunkMetadata {
76 fn bytes(&self) -> Bytes {
77 let mut bytes = BytesMut::with_capacity(ID_SIZE + SIGNATURE_SIZE);
78 bytes.extend_from_slice(self.id.as_ref());
79 bytes.extend_from_slice(&self.signature.as_bytes());
80 bytes.freeze()
81 }
82}
83
84#[derive(Debug, Clone)]
86pub struct SingleOwnerChunkHeader {
87 metadata: SingleOwnerChunkMetadata,
88}
89
90impl SingleOwnerChunkHeader {
91 pub const fn new(metadata: SingleOwnerChunkMetadata) -> Self {
93 Self { metadata }
94 }
95}
96
97impl ChunkHeader for SingleOwnerChunkHeader {
98 type Metadata = SingleOwnerChunkMetadata;
99
100 fn id(&self) -> u8 {
101 1
102 }
103
104 fn version(&self) -> u8 {
105 1
106 }
107
108 fn metadata(&self) -> &Self::Metadata {
109 &self.metadata
110 }
111
112 fn bytes(&self) -> Bytes {
113 self.metadata.bytes()
114 }
115}
116
117impl<const BODY_SIZE: usize> SingleOwnerChunk<BODY_SIZE> {
118 #[must_use = "this returns a new chunk without modifying the input"]
133 pub fn new(id: B256, data: impl Into<Bytes>, signer: &impl SignerSync) -> Result<Self> {
134 SingleOwnerChunkBuilderImpl::<BODY_SIZE, Initial>::default()
135 .auto_from_data(data)?
136 .with_id(id)
137 .with_signer(signer)?
138 .build()
139 }
140
141 #[must_use = "this returns a new chunk without modifying the input"]
156 pub fn with_signature(id: B256, signature: Signature, data: impl Into<Bytes>) -> Result<Self> {
157 SingleOwnerChunkBuilderImpl::<BODY_SIZE, Initial>::default()
158 .auto_from_data(data)?
159 .with_id(id)
160 .with_signature(signature)?
161 .build()
162 }
163
164 #[must_use = "this returns a new chunk without modifying the input"]
170 pub fn new_dispersed_replica(mined_byte: u8, body: BmtBody<BODY_SIZE>) -> Result<Self> {
171 SingleOwnerChunkBuilderImpl::<BODY_SIZE, Initial>::default()
172 .with_body(body)
173 .dispersed_replica(mined_byte)?
174 .build()
175 }
176
177 #[must_use]
188 pub const fn from_parts(id: B256, signature: Signature, body: BmtBody<BODY_SIZE>) -> Self {
189 let metadata = SingleOwnerChunkMetadata::new(id, signature);
190 let header = SingleOwnerChunkHeader::new(metadata);
191
192 Self {
193 header,
194 body,
195 chunk_address_cache: OnceCache::new(),
196 owner_cache: OnceCache::new(),
197 }
198 }
199
200 #[must_use]
205 pub fn from_parts_with_caches(
206 id: B256,
207 signature: Signature,
208 body: BmtBody<BODY_SIZE>,
209 address: ChunkAddress,
210 owner: Address,
211 ) -> Self {
212 let metadata = SingleOwnerChunkMetadata::new(id, signature);
213 let header = SingleOwnerChunkHeader::new(metadata);
214
215 Self {
216 header,
217 body,
218 chunk_address_cache: OnceCache::with_value(address),
219 owner_cache: OnceCache::with_value(owner),
220 }
221 }
222
223 pub fn owner(&self) -> error::Result<Address> {
238 if let Some(addr) = self.owner_cache.get() {
240 return Ok(*addr);
241 }
242
243 let addr = self.calculate_owner()?;
245 let _ = self.owner_cache.try_set(addr);
247 Ok(addr)
248 }
249
250 fn calculate_owner(&self) -> error::Result<Address> {
252 let hash = Self::to_sign(&self.header.metadata.id, &self.body);
254
255 self.signature()
257 .recover_address_from_msg(hash)
258 .map_err(Into::into)
259 }
260
261 fn to_sign(id: &B256, body: &BmtBody<BODY_SIZE>) -> B256 {
275 let mut hasher = Keccak256::new();
276 hasher.update(id);
277 hasher.update(body.hash());
278 hasher.finalize()
279 }
280
281 fn is_valid_replica(&self) -> bool {
283 self.id()[1..] == self.body.hash().as_slice()[1..]
284 }
285
286 pub const fn id(&self) -> B256 {
288 self.header.metadata.id
289 }
290
291 pub const fn signature(&self) -> &Signature {
293 &self.header.metadata.signature
294 }
295}
296
297impl<const BODY_SIZE: usize> Chunk for SingleOwnerChunk<BODY_SIZE> {
298 type Header = SingleOwnerChunkHeader;
299
300 fn address(&self) -> &ChunkAddress {
301 self.chunk_address_cache.get_or_compute(|| {
302 let owner = self.owner().unwrap_or(Address::ZERO);
306 let mut hasher = Keccak256::new();
307 hasher.update(self.id());
308 hasher.update(owner);
309
310 hasher.finalize().into()
311 })
312 }
313
314 fn data(&self) -> &Bytes {
315 self.body.data()
316 }
317
318 fn size(&self) -> usize {
319 self.header().bytes().len() + self.body.size()
320 }
321
322 fn header(&self) -> &Self::Header {
323 &self.header
324 }
325
326 fn verify(&self, expected: &ChunkAddress) -> Result<()> {
327 let actual = self.address();
328
329 let owner = self.owner()?;
332 if owner == DISPERSED_REPLICA_OWNER && !self.is_valid_replica() {
333 return Err(error::ChunkError::invalid_format("invalid dispersed replica").into());
334 }
335
336 if actual != expected {
337 return Err(error::ChunkError::verification_failed(*expected, *actual).into());
338 }
339 Ok(())
340 }
341}
342
343impl<const BODY_SIZE: usize> BmtChunk for SingleOwnerChunk<BODY_SIZE> {
344 fn span(&self) -> u64 {
345 self.body.span()
346 }
347}
348
349impl<const BODY_SIZE: usize> From<SingleOwnerChunk<BODY_SIZE>> for Bytes {
350 fn from(chunk: SingleOwnerChunk<BODY_SIZE>) -> Self {
351 let mut bytes = BytesMut::with_capacity(chunk.size());
352 bytes.extend_from_slice(chunk.header().bytes().as_ref());
353 bytes.extend_from_slice(&Self::from(chunk.body));
354 bytes.freeze()
355 }
356}
357
358impl<const BODY_SIZE: usize> TryFrom<Bytes> for SingleOwnerChunk<BODY_SIZE> {
359 type Error = PrimitivesError;
360
361 fn try_from(bytes: Bytes) -> Result<Self> {
362 if bytes.len() < MIN_SOC_FIELDS_SIZE {
363 return Err(ChunkError::invalid_size(
364 "insufficient data for single-owner chunk",
365 MIN_SOC_FIELDS_SIZE,
366 bytes.len(),
367 )
368 .into());
369 }
370
371 let id_slice = &bytes.slice(0..ID_SIZE);
373 let mut id = FixedBytes::<32>::default();
374 id.copy_from_slice(id_slice);
375
376 let sig_slice = &bytes.slice(ID_SIZE..ID_SIZE + SIGNATURE_SIZE);
378 let signature = Signature::from_raw(sig_slice).map_err(ChunkError::from)?;
379
380 let body_bytes = bytes.slice(ID_SIZE + SIGNATURE_SIZE..);
382 let body = BmtBody::try_from(body_bytes)?;
383
384 let metadata = SingleOwnerChunkMetadata::new(id, signature);
386 let header = SingleOwnerChunkHeader::new(metadata);
387
388 Ok(Self {
389 header,
390 body,
391 chunk_address_cache: OnceCache::new(),
392 owner_cache: OnceCache::new(),
393 })
394 }
395}
396
397impl<const BODY_SIZE: usize> TryFrom<&[u8]> for SingleOwnerChunk<BODY_SIZE> {
398 type Error = PrimitivesError;
399
400 fn try_from(bytes: &[u8]) -> Result<Self> {
401 Self::try_from(Bytes::copy_from_slice(bytes))
402 }
403}
404
405impl<const BODY_SIZE: usize> fmt::Display for SingleOwnerChunk<BODY_SIZE> {
406 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
407 let owner_str = self.owner().map_or_else(
408 |_| "invalid".to_string(),
409 |addr| hex::encode(addr.as_slice()),
410 );
411 write!(
412 f,
413 "SingleOwnerChunk[id={}, owner={}]",
414 hex::encode(&self.id()[..8]),
415 owner_str
416 )
417 }
418}
419
420impl<const BODY_SIZE: usize> PartialEq for SingleOwnerChunk<BODY_SIZE> {
421 fn eq(&self, other: &Self) -> bool {
422 match (self.owner(), other.owner()) {
424 (Ok(a), Ok(b)) => self.id() == other.id() && a == b,
425 _ => false,
426 }
427 }
428}
429
430impl<const BODY_SIZE: usize> Eq for SingleOwnerChunk<BODY_SIZE> {}
431
432impl<const BODY_SIZE: usize> super::chunk_type::ChunkType for SingleOwnerChunk<BODY_SIZE> {
433 const TYPE_ID: super::type_id::ChunkTypeId = super::type_id::ChunkTypeId::SINGLE_OWNER;
434 const TYPE_NAME: &'static str = "single_owner";
435}
436
437trait BuilderState {}
439
440#[derive(Debug, Default)]
441struct Initial;
442impl BuilderState for Initial {}
443
444#[derive(Debug)]
445struct WithData;
446impl BuilderState for WithData {}
447
448#[derive(Debug)]
449struct WithId;
450impl BuilderState for WithId {}
451
452#[derive(Debug)]
453struct ReadyToBuild;
454impl BuilderState for ReadyToBuild {}
455
456#[derive(Debug)]
458struct SingleOwnerChunkBuilderImpl<const BODY_SIZE: usize, S: BuilderState = Initial> {
459 body: Option<BmtBody<BODY_SIZE>>,
461 id: Option<B256>,
463 signature: Option<Signature>,
465 _state: PhantomData<S>,
467}
468
469impl<const BODY_SIZE: usize> Default for SingleOwnerChunkBuilderImpl<BODY_SIZE, Initial> {
470 fn default() -> Self {
471 Self {
472 body: None,
473 id: None,
474 signature: None,
475 _state: PhantomData,
476 }
477 }
478}
479
480impl<const BODY_SIZE: usize> SingleOwnerChunkBuilderImpl<BODY_SIZE, Initial> {
481 fn auto_from_data(
483 mut self,
484 data: impl Into<Bytes>,
485 ) -> Result<SingleOwnerChunkBuilderImpl<BODY_SIZE, WithData>> {
486 let body = BmtBody::<BODY_SIZE>::builder()
487 .auto_from_data(data)?
488 .build()?;
489 self.body = Some(body);
490
491 Ok(SingleOwnerChunkBuilderImpl {
492 body: self.body,
493 id: self.id,
494 signature: self.signature,
495 _state: PhantomData,
496 })
497 }
498
499 fn with_body(
501 mut self,
502 body: BmtBody<BODY_SIZE>,
503 ) -> SingleOwnerChunkBuilderImpl<BODY_SIZE, WithData> {
504 self.body = Some(body);
505
506 SingleOwnerChunkBuilderImpl {
507 body: self.body,
508 id: self.id,
509 signature: self.signature,
510 _state: PhantomData,
511 }
512 }
513}
514
515impl<const BODY_SIZE: usize> SingleOwnerChunkBuilderImpl<BODY_SIZE, WithData> {
516 fn with_id(mut self, id: B256) -> SingleOwnerChunkBuilderImpl<BODY_SIZE, WithId> {
518 self.id = Some(id);
519
520 SingleOwnerChunkBuilderImpl {
521 body: self.body,
522 id: self.id,
523 signature: self.signature,
524 _state: PhantomData,
525 }
526 }
527
528 fn dispersed_replica(
530 self,
531 first_byte: u8,
532 ) -> Result<SingleOwnerChunkBuilderImpl<BODY_SIZE, ReadyToBuild>> {
533 let body_hash = self.body.as_ref().unwrap().hash();
534 let mut id = B256::default();
535 id[0] = first_byte;
536 id[1..].copy_from_slice(&body_hash.as_slice()[1..]);
537
538 let signer = PrivateKeySigner::from_slice(DISPERSED_REPLICA_OWNER_PK.as_slice()).unwrap();
539
540 self.with_id(id).with_signer(&signer)
541 }
542}
543
544impl<const BODY_SIZE: usize> SingleOwnerChunkBuilderImpl<BODY_SIZE, WithId> {
545 fn with_signer(
547 self,
548 signer: &impl SignerSync,
549 ) -> Result<SingleOwnerChunkBuilderImpl<BODY_SIZE, ReadyToBuild>> {
550 let body = self.body.as_ref().unwrap();
552 let id = self.id.as_ref().unwrap();
553
554 let hash = SingleOwnerChunk::<BODY_SIZE>::to_sign(id, body);
556
557 let signature = signer
559 .sign_message_sync(hash.as_ref())
560 .map_err(ChunkError::from)?;
561
562 self.with_signature(signature)
563 }
564
565 fn with_signature(
567 mut self,
568 signature: Signature,
569 ) -> Result<SingleOwnerChunkBuilderImpl<BODY_SIZE, ReadyToBuild>> {
570 self.signature = Some(signature);
571
572 Ok(SingleOwnerChunkBuilderImpl {
573 body: self.body,
574 id: self.id,
575 signature: self.signature,
576 _state: PhantomData,
577 })
578 }
579}
580
581impl<const BODY_SIZE: usize> SingleOwnerChunkBuilderImpl<BODY_SIZE, ReadyToBuild> {
582 fn build(self) -> Result<SingleOwnerChunk<BODY_SIZE>> {
584 let body = self.body.unwrap();
585 let id = self.id.unwrap();
586 let signature = self.signature.unwrap();
587
588 Ok(SingleOwnerChunk::from_parts(id, signature, body))
589 }
590}
591
592#[cfg(any(test, feature = "arbitrary"))]
593impl<'a, const BODY_SIZE: usize> arbitrary::Arbitrary<'a> for SingleOwnerChunk<BODY_SIZE> {
594 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
595 let id = B256::arbitrary(u)?;
596 let body = BmtBody::<BODY_SIZE>::arbitrary(u)?;
597 let signer = alloy_signer_local::PrivateKeySigner::random();
598
599 Ok(SingleOwnerChunkBuilderImpl::<BODY_SIZE, Initial>::default()
600 .with_body(body)
601 .with_id(id)
602 .with_signer(&signer)
603 .unwrap()
604 .build()
605 .unwrap())
606 }
607}
608
609#[cfg(test)]
610mod tests {
611 use crate::DEFAULT_BODY_SIZE;
612
613 use super::*;
614 use alloy_primitives::hex;
615 use proptest::prelude::*;
616 use proptest_arbitrary_interop::arb;
617
618 type DefaultSingleOwnerChunk = SingleOwnerChunk<DEFAULT_BODY_SIZE>;
619
620 fn get_test_wallet() -> PrivateKeySigner {
621 let pk = hex!("2c7536e3605d9c16a7a3d7b1898e529396a65c23a3bcbd4012a11cf2731b0fbc");
623 PrivateKeySigner::from_slice(&pk).unwrap()
624 }
625
626 fn chunk_strategy() -> impl Strategy<Value = DefaultSingleOwnerChunk> {
628 arb::<DefaultSingleOwnerChunk>()
629 }
630
631 proptest! {
632 #[test]
633 fn test_chunk_properties(chunk in chunk_strategy()) {
634 prop_assert!(chunk.size() >= MIN_SOC_FIELDS_SIZE);
635
636 let bytes: Bytes = chunk.clone().into();
638 let decoded = DefaultSingleOwnerChunk::try_from(bytes.as_ref()).unwrap();
639 prop_assert_eq!(chunk.id(), decoded.id());
640 prop_assert_eq!(chunk.signature(), decoded.signature());
641 prop_assert_eq!(chunk.data(), decoded.data());
642 prop_assert_eq!(chunk.owner().unwrap(), decoded.owner().unwrap());
643
644 let address = chunk.address();
646 prop_assert!(chunk.verify(address).is_ok());
647 }
648
649 #[test]
650 fn test_dispersed_replica_properties(first_byte in any::<u8>(), data in proptest::collection::vec(any::<u8>(), 1..DEFAULT_BODY_SIZE)) {
651 let chunk = DefaultSingleOwnerChunk::new_dispersed_replica(first_byte, BmtBody::<DEFAULT_BODY_SIZE>::builder().auto_from_data(data).unwrap().build().unwrap()).unwrap();
652
653 prop_assert!(chunk.is_valid_replica());
655 prop_assert_eq!(chunk.id()[0], first_byte);
656 prop_assert_eq!(chunk.owner().unwrap(), DISPERSED_REPLICA_OWNER);
657
658 prop_assert!(chunk.verify(chunk.address()).is_ok());
660 }
661
662 #[test]
663 fn test_chunk_creation(id in arb::<B256>(), data in proptest::collection::vec(any::<u8>(), 1..DEFAULT_BODY_SIZE)) {
664 let wallet = get_test_wallet();
665
666 let chunk = SingleOwnerChunkBuilderImpl::<DEFAULT_BODY_SIZE, Initial>::default()
668 .with_body(
669 BmtBody::<DEFAULT_BODY_SIZE>::builder()
670 .auto_from_data(data.clone())
671 .unwrap()
672 .build()
673 .unwrap(),
674 )
675 .with_id(id)
676 .with_signer(&wallet)
677 .unwrap()
678 .build()
679 .unwrap();
680
681 prop_assert_eq!(chunk.id(), id);
682 prop_assert_eq!(chunk.data(), &data);
683 prop_assert!(!chunk.owner().unwrap().is_zero());
684 }
685
686 #[test]
687 fn test_dispersed_replica_mismatched_address(first_byte in any::<u8>(), data in proptest::collection::vec(any::<u8>(), 1..DEFAULT_BODY_SIZE)) {
688 let chunk = SingleOwnerChunkBuilderImpl::<DEFAULT_BODY_SIZE, Initial>::default().with_body(
689 BmtBody::<DEFAULT_BODY_SIZE>::builder()
690 .auto_from_data(data)
691 .unwrap()
692 .build()
693 .unwrap(),
694 ).dispersed_replica(first_byte).unwrap().build().unwrap();
695 let replica_address = *chunk.address();
696 let bytes: Bytes = chunk.into();
698
699 let mut modified_bytes = bytes.to_vec();
702 modified_bytes[1..ID_SIZE].copy_from_slice(&[0x01; 31]);
703
704 let modified_chunk = DefaultSingleOwnerChunk::try_from(modified_bytes.as_slice()).unwrap();
705 prop_assert!(!modified_chunk.is_valid_replica());
706 prop_assert!(modified_chunk.verify(&replica_address).is_err());
707 }
708
709 #[test]
710 fn test_chunk_invalid_signature(id in arb::<B256>(), data in proptest::collection::vec(any::<u8>(), 1..DEFAULT_BODY_SIZE)) {
711 let wallet = get_test_wallet();
712
713 let chunk = DefaultSingleOwnerChunk::new(id, data, &wallet).unwrap();
715 let original_address = *chunk.address();
716
717 let bytes: Bytes = chunk.into();
719
720 let mut modified_bytes = bytes.to_vec();
723 modified_bytes[ID_SIZE..ID_SIZE + 65].copy_from_slice(&[0xff; 65]);
724
725 let modified_chunk = DefaultSingleOwnerChunk::try_from(modified_bytes.as_slice()).unwrap();
726 prop_assert!(modified_chunk.verify(&original_address).is_err());
727 prop_assert!(modified_chunk.owner().is_err());
729 }
730
731 #[test]
732 fn test_chunk_too_small(data in proptest::collection::vec(any::<u8>(), 1..MIN_SOC_FIELDS_SIZE)) {
733 let chunk = DefaultSingleOwnerChunk::try_from(data.as_slice());
735 prop_assert!(chunk.is_err());
736 }
737 }
738
739 #[test]
740 fn test_new() {
741 let id = B256::ZERO;
742 let data = b"foo".to_vec();
743 let wallet = get_test_wallet();
744
745 let chunk = DefaultSingleOwnerChunk::new(id, data.clone(), &wallet).unwrap();
746
747 assert_eq!(chunk.id(), id);
748 assert_eq!(chunk.data(), &data);
749 }
750
751 #[test]
752 fn test_new_signed() {
753 let id = B256::ZERO;
754 let data = b"foo".to_vec();
755
756 let sig = hex!(
758 "5acd384febc133b7b245e5ddc62d82d2cded9182d2716126cd8844509af65a053deb418208027f548e3e88343af6f84a8772fb3cebc0a1833a0ea7ec0c1348311b"
759 );
760 let signature = Signature::try_from(sig.as_slice()).unwrap();
761
762 let chunk = SingleOwnerChunkBuilderImpl::<DEFAULT_BODY_SIZE, Initial>::default()
763 .auto_from_data(data.clone())
764 .unwrap()
765 .with_id(id)
766 .with_signature(signature)
767 .unwrap()
768 .build()
769 .unwrap();
770
771 assert_eq!(chunk.id(), id);
772 assert_eq!(chunk.data(), &data);
773 assert_eq!(chunk.signature().as_bytes(), sig);
774
775 let expected_owner = address!("8d3766440f0d7b949a5e32995d09619a7f86e632");
777 assert_eq!(chunk.owner().unwrap(), expected_owner);
778 }
779
780 fn get_test_chunk_data() -> Vec<u8> {
781 hex!(
782 "000000000000000000000000000000000000000000000000000000000000000\
783 05acd384febc133b7b245e5ddc62d82d2cded9182d2716126cd8844509af65a05\
784 3deb418208027f548e3e88343af6f84a8772fb3cebc0a1833a0ea7ec0c134831\
785 1b0300000000000000666f6f"
786 )
787 .to_vec()
788 }
789
790 #[test]
791 fn test_chunk_address() {
792 let chunk = DefaultSingleOwnerChunk::try_from(get_test_chunk_data().as_slice()).unwrap();
794
795 let expected_owner = address!("8d3766440f0d7b949a5e32995d09619a7f86e632");
797 assert_eq!(chunk.owner().unwrap(), expected_owner);
798
799 let expected_address =
801 b256!("9d453ebb73b2fedaaf44ceddcf7a0aa37f3e3d6453fea5841c31f0ea6d61dc85");
802 assert_eq!(chunk.address().as_ref(), expected_address);
803 }
804
805 #[test]
806 fn test_invalid_dispersed_replica() -> Result<()> {
807 let test_data = b"test data".to_vec();
808 let dispersed_replica_wallet =
809 PrivateKeySigner::from_slice(DISPERSED_REPLICA_OWNER_PK.as_slice()).unwrap();
810
811 let chunk = SingleOwnerChunkBuilderImpl::<DEFAULT_BODY_SIZE, Initial>::default()
812 .with_body(
813 BmtBody::<DEFAULT_BODY_SIZE>::builder()
814 .auto_from_data(test_data)?
815 .build()?,
816 )
817 .with_id(B256::ZERO)
818 .with_signer(&dispersed_replica_wallet)?
819 .build()?;
820 let replica_address = chunk.address();
821
822 assert!(!chunk.is_valid_replica());
823 assert!(matches!(
824 chunk.verify(replica_address),
825 Err(PrimitivesError::Chunk(ChunkError::InvalidFormat { .. }))
826 ));
827
828 Ok(())
829 }
830}