Skip to content

Commit 662699f

Browse files
authored
[Fix] Premature end for exported snapshots (#3015)
* [Fix] Premature end for exported snapshots * add snapshot validity test
1 parent fb11049 commit 662699f

5 files changed

Lines changed: 56 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535

3636
### Fixed
3737

38+
- [#3006](https://github.com/ChainSafe/forest/issues/3006): Fix `premature end`
39+
error when exporting a snapshot.
40+
3841
## Forest v0.9.0 "Fellowship"
3942

4043
Notable updates:

scripts/tests/calibnet_export_check.sh

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@ source "$(dirname "$0")/harness.sh"
99

1010
forest_init
1111

12+
echo "Cleaning up the initial snapshot"
13+
rm -rf ./*.car.*
14+
1215
echo "Exporting zstd compressed snapshot"
1316
$FOREST_CLI_PATH snapshot export
1417

15-
echo "Verifing snapshot checksum"
16-
sha256sum -c ./*.sha256sum
18+
echo "Testing snapshot validity"
19+
zstd --test ./*.car.zst
20+
21+
echo "Verifying snapshot checksum"
22+
sha256sum --check ./*.sha256sum

src/chain/store/chain_store.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,13 @@ use crate::utils::{
2929
misc::Either,
3030
};
3131
use ahash::{HashMap, HashMapExt, HashSet};
32-
use anyhow::Result;
32+
use anyhow::{Context, Result};
3333
use async_compression::futures::write::ZstdEncoder;
3434
use bls_signatures::Serialize as SerializeBls;
3535
use cid::Cid;
3636
use digest::Digest;
3737
use futures::{io::BufWriter, AsyncWrite};
38+
use futures_util::AsyncWriteExt;
3839
use fvm_ipld_amt::Amtv0 as Amt;
3940
use fvm_ipld_blockstore::Blockstore;
4041
use fvm_ipld_car::CarHeader;
@@ -624,6 +625,9 @@ where
624625
);
625626

626627
let mut writer = writer.lock().await;
628+
writer.flush().await.context("failed to flush")?;
629+
writer.close().await.context("failed to close")?;
630+
627631
let digest = match &mut *writer {
628632
Either::Left(left) => left.get_mut().finalize().await,
629633
Either::Right(right) => right.finalize().await,

src/utils/io/writer_checksum.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use std::{pin::Pin, task::Poll};
44

55
use async_trait::async_trait;
66
use digest::{Digest, Output};
7-
use futures::{io::BufWriter, AsyncWrite, AsyncWriteExt};
7+
use futures::{io::BufWriter, AsyncWrite};
8+
use futures_util::AsyncWriteExt;
89
use pin_project_lite::pin_project;
910

1011
pin_project! {
@@ -25,7 +26,7 @@ pub trait Checksum<D: Digest> {
2526
async fn finalize(&mut self) -> std::io::Result<Option<Output<D>>>;
2627
}
2728

28-
impl<D: Digest, W: AsyncWrite + Unpin> AsyncWrite for AsyncWriterWithChecksum<D, W> {
29+
impl<D: Digest, W: AsyncWriteExt + Unpin> AsyncWrite for AsyncWriterWithChecksum<D, W> {
2930
fn poll_write(
3031
mut self: std::pin::Pin<&mut Self>,
3132
cx: &mut std::task::Context<'_>,
@@ -59,9 +60,10 @@ impl<D: Digest, W: AsyncWrite + Unpin> AsyncWrite for AsyncWriterWithChecksum<D,
5960
}
6061

6162
#[async_trait]
62-
impl<D: Digest + Send, W: AsyncWrite + Send + Unpin> Checksum<D> for AsyncWriterWithChecksum<D, W> {
63+
impl<D: Digest + Send, W: AsyncWriteExt + Send + Unpin> Checksum<D>
64+
for AsyncWriterWithChecksum<D, W>
65+
{
6366
async fn finalize(&mut self) -> std::io::Result<Option<Output<D>>> {
64-
self.inner.flush().await?;
6567
if let Some(hasher) = &mut self.hasher {
6668
let hasher = std::mem::replace(hasher, D::new());
6769
Ok(Some(hasher.finalize()))
@@ -133,6 +135,9 @@ mod test {
133135
temp_file_writer.write_all(&bytes).await?;
134136
}
135137

138+
temp_file_writer.flush().await?;
139+
temp_file_writer.close().await?;
140+
136141
let checksum = temp_file_writer.finalize().await?;
137142

138143
let file_hash = {

src/utils/misc/either.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
// Copyright 2019-2023 ChainSafe Systems
22
// SPDX-License-Identifier: Apache-2.0, MIT
33

4+
use std::io::IoSlice;
45
use std::{
56
pin::Pin,
67
task::{Context, Poll},
78
};
89

9-
use futures::AsyncRead;
10+
use futures::{AsyncRead, AsyncWrite};
11+
use futures_util::io::{Close, Flush, IntoSink, Write, WriteAll, WriteVectored};
12+
use futures_util::AsyncWriteExt;
1013

1114
pub enum Either<L, R> {
1215
Left(L),
@@ -25,3 +28,30 @@ impl<L: AsyncRead + Unpin, R: AsyncRead + Unpin> AsyncRead for Either<L, R> {
2528
}
2629
}
2730
}
31+
32+
impl<L: AsyncWrite + Unpin, R: AsyncWrite + Unpin> AsyncWrite for Either<L, R> {
33+
fn poll_write(
34+
self: Pin<&mut Self>,
35+
cx: &mut Context<'_>,
36+
buf: &[u8],
37+
) -> Poll<std::io::Result<usize>> {
38+
match Pin::into_inner(self) {
39+
Self::Left(left) => Pin::new(left).poll_write(cx, buf),
40+
Self::Right(right) => Pin::new(right).poll_write(cx, buf),
41+
}
42+
}
43+
44+
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
45+
match Pin::into_inner(self) {
46+
Self::Left(left) => Pin::new(left).poll_flush(cx),
47+
Self::Right(right) => Pin::new(right).poll_flush(cx),
48+
}
49+
}
50+
51+
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
52+
match Pin::into_inner(self) {
53+
Self::Left(left) => Pin::new(left).poll_close(cx),
54+
Self::Right(right) => Pin::new(right).poll_close(cx),
55+
}
56+
}
57+
}

0 commit comments

Comments
 (0)