From 9045e34ad24d18b07ab35704fe52da448109e575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 12 Aug 2024 16:45:47 +0200 Subject: [PATCH] fix: Generate t_aux for snap when not present (#143) --- lib/ffi/snap_funcs.go | 5 + lib/proof/bincode_util.go | 42 +++++++ lib/proof/porep_vproof_bin_decode.go | 6 - lib/proof/t_aux_gen.go | 84 +++++++++++++ lib/proof/t_aux_test.go | 48 ++++++++ lib/proof/t_aux_types.go | 176 +++++++++++++++++++++++++++ 6 files changed, 355 insertions(+), 6 deletions(-) create mode 100644 lib/proof/bincode_util.go create mode 100644 lib/proof/t_aux_gen.go create mode 100644 lib/proof/t_aux_test.go create mode 100644 lib/proof/t_aux_types.go diff --git a/lib/ffi/snap_funcs.go b/lib/ffi/snap_funcs.go index e615aed1f..571e04cc9 100644 --- a/lib/ffi/snap_funcs.go +++ b/lib/ffi/snap_funcs.go @@ -20,6 +20,7 @@ import ( "github.com/filecoin-project/curio/lib/asyncwrite" "github.com/filecoin-project/curio/lib/ffiselect" paths2 "github.com/filecoin-project/curio/lib/paths" + "github.com/filecoin-project/curio/lib/proof" "github.com/filecoin-project/curio/lib/tarutil" "github.com/filecoin-project/lotus/storage/sealer/fr32" @@ -185,6 +186,10 @@ func (sb *SealCalls) EncodeUpdate( if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("extracting cache: %w", err) } + + if err := proof.EnsureTauxForType(sector.ProofType, keyCachePath); err != nil { + return cid.Cid{}, cid.Cid{}, xerrors.Errorf("ensuring t_aux exists: %w", err) + } } // allocate update file diff --git a/lib/proof/bincode_util.go b/lib/proof/bincode_util.go new file mode 100644 index 000000000..77bc101f8 --- /dev/null +++ b/lib/proof/bincode_util.go @@ -0,0 +1,42 @@ +package proof + +import ( + "encoding/binary" + "io" + + "golang.org/x/xerrors" +) + +func ReadLE[T any](r io.Reader) (T, error) { + var out T + err := binary.Read(r, binary.LittleEndian, &out) + return out, err +} + +func ReadString(r io.Reader) (string, error) { + l, err := ReadLE[uint64](r) + if err != nil { + return "", xerrors.Errorf("failed to read string length: %w", err) + } + + buf := make([]byte, l) + if _, err := io.ReadFull(r, buf); err != nil { + return "", xerrors.Errorf("failed to read string: %w", err) + } + + return string(buf), nil +} + +func WriteLE[T any](w io.Writer, data T) error { + return binary.Write(w, binary.LittleEndian, data) +} + +func WriteString(w io.Writer, s string) error { + if err := WriteLE(w, uint64(len(s))); err != nil { + return xerrors.Errorf("failed to write string length: %w", err) + } + if _, err := w.Write([]byte(s)); err != nil { + return xerrors.Errorf("failed to write string: %w", err) + } + return nil +} diff --git a/lib/proof/porep_vproof_bin_decode.go b/lib/proof/porep_vproof_bin_decode.go index 985d6a87e..c27d11255 100644 --- a/lib/proof/porep_vproof_bin_decode.go +++ b/lib/proof/porep_vproof_bin_decode.go @@ -10,12 +10,6 @@ import ( // This is the format output by the C++ supraseal C1 implementation. // bincode - https://github.com/bincode-org/bincode -func ReadLE[T any](r io.Reader) (T, error) { - var out T - err := binary.Read(r, binary.LittleEndian, &out) - return out, err -} - func DecodeCommit1OutRaw(r io.Reader) (Commit1OutRaw, error) { var out Commit1OutRaw var err error diff --git a/lib/proof/t_aux_gen.go b/lib/proof/t_aux_gen.go new file mode 100644 index 000000000..8548f6530 --- /dev/null +++ b/lib/proof/t_aux_gen.go @@ -0,0 +1,84 @@ +package proof + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-state-types/abi" +) + +var tauxName = "t_aux" + +// EnsureTauxForType ensures that the t_aux file exists in the specified path for the specified seal proof type. +// If the file does not exist, it will be created with the default values for the specified seal proof type. +func EnsureTauxForType(spt abi.RegisteredSealProof, path string) error { + // check if the file exists + auxPath := filepath.Join(path, tauxName) + if _, err := os.Stat(auxPath); !os.IsNotExist(err) { + return nil // file exists, nothing to do + } + + // no t_aux, create one that will satisfy rust-fil-proofs + + if spt != abi.RegisteredSealProof_StackedDrg32GiBV1_1 && spt != abi.RegisteredSealProof_StackedDrg32GiBV1_1_Feat_SyntheticPoRep { + return xerrors.Errorf("unsupported seal proof type: %v", spt) + } + + treeRRowsToDiscard := uint64(2) + if envset := os.Getenv("FIL_PROOFS_ROWS_TO_DISCARD"); envset != "" { + _, err := fmt.Sscan(envset, &treeRRowsToDiscard) + if err != nil { + return xerrors.Errorf("failed to parse FIL_PROOFS_ROWS_TO_DISCARD: %w", err) + } + } + + taux := TemporaryAux{ + Labels: TAuxLabels{}, + TreeDConfig: StoreConfig{ + Path: path, + ID: "tree-d", + Size: iptr(2147483647), + RowsToDiscard: 0, + }, + TreeRConfig: StoreConfig{ + Path: path, + ID: "tree-r-last", + Size: iptr(153391689), + RowsToDiscard: treeRRowsToDiscard, + }, + TreeCConfig: StoreConfig{ + Path: path, + ID: "tree-c", + Size: iptr(153391689), + RowsToDiscard: 0, + }, + } + + for i := 0; i < 11; i++ { + taux.Labels.Labels = append(taux.Labels.Labels, StoreConfig{ + Path: path, + ID: fmt.Sprintf("layer-%d", i+1), + Size: iptr(1073741824), + RowsToDiscard: 0, + }) + } + + var buf bytes.Buffer + if err := EncodeTAux(&buf, taux); err != nil { + return xerrors.Errorf("failed to encode taux: %w", err) + } + + if err := os.WriteFile(auxPath, buf.Bytes(), 0644); err != nil { + return xerrors.Errorf("failed to write taux file: %w", err) + } + + return nil +} + +func iptr(i uint64) *uint64 { + return &i +} diff --git a/lib/proof/t_aux_test.go b/lib/proof/t_aux_test.go new file mode 100644 index 000000000..f53106f81 --- /dev/null +++ b/lib/proof/t_aux_test.go @@ -0,0 +1,48 @@ +package proof + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "testing" + + "github.com/snadrus/must" + "github.com/stretchr/testify/require" +) + +const TAuxGolden = `CwAAAAAAAAAhAAAAAAAAAC9kYXRhLzEvc3RvcmUvY2FjaGUvcy10MDI2MjAtMjAxMAcAAAAAAAAA +bGF5ZXItMQEAAABAAAAAAAAAAAAAAAAAIQAAAAAAAAAvZGF0YS8xL3N0b3JlL2NhY2hlL3MtdDAy +NjIwLTIwMTAHAAAAAAAAAGxheWVyLTIBAAAAQAAAAAAAAAAAAAAAACEAAAAAAAAAL2RhdGEvMS9z +dG9yZS9jYWNoZS9zLXQwMjYyMC0yMDEwBwAAAAAAAABsYXllci0zAQAAAEAAAAAAAAAAAAAAAAAh +AAAAAAAAAC9kYXRhLzEvc3RvcmUvY2FjaGUvcy10MDI2MjAtMjAxMAcAAAAAAAAAbGF5ZXItNAEA +AABAAAAAAAAAAAAAAAAAIQAAAAAAAAAvZGF0YS8xL3N0b3JlL2NhY2hlL3MtdDAyNjIwLTIwMTAH +AAAAAAAAAGxheWVyLTUBAAAAQAAAAAAAAAAAAAAAACEAAAAAAAAAL2RhdGEvMS9zdG9yZS9jYWNo +ZS9zLXQwMjYyMC0yMDEwBwAAAAAAAABsYXllci02AQAAAEAAAAAAAAAAAAAAAAAhAAAAAAAAAC9k +YXRhLzEvc3RvcmUvY2FjaGUvcy10MDI2MjAtMjAxMAcAAAAAAAAAbGF5ZXItNwEAAABAAAAAAAAA +AAAAAAAAIQAAAAAAAAAvZGF0YS8xL3N0b3JlL2NhY2hlL3MtdDAyNjIwLTIwMTAHAAAAAAAAAGxh +eWVyLTgBAAAAQAAAAAAAAAAAAAAAACEAAAAAAAAAL2RhdGEvMS9zdG9yZS9jYWNoZS9zLXQwMjYy +MC0yMDEwBwAAAAAAAABsYXllci05AQAAAEAAAAAAAAAAAAAAAAAhAAAAAAAAAC9kYXRhLzEvc3Rv +cmUvY2FjaGUvcy10MDI2MjAtMjAxMAgAAAAAAAAAbGF5ZXItMTABAAAAQAAAAAAAAAAAAAAAACEA +AAAAAAAAL2RhdGEvMS9zdG9yZS9jYWNoZS9zLXQwMjYyMC0yMDEwCAAAAAAAAABsYXllci0xMQEA +AABAAAAAAAAAAAAAAAAAIQAAAAAAAAAvZGF0YS8xL3N0b3JlL2NhY2hlL3MtdDAyNjIwLTIwMTAG +AAAAAAAAAHRyZWUtZAH///9/AAAAAAAAAAAAAAAAIQAAAAAAAAAvZGF0YS8xL3N0b3JlL2NhY2hl +L3MtdDAyNjIwLTIwMTALAAAAAAAAAHRyZWUtci1sYXN0AUmSJAkAAAAAAgAAAAAAAAAhAAAAAAAA +AC9kYXRhLzEvc3RvcmUvY2FjaGUvcy10MDI2MjAtMjAxMAYAAAAAAAAAdHJlZS1jAUmSJAkAAAAA +AAAAAAAAAAA=` + +func TestTAuxRoundtrip(t *testing.T) { + gbytes := must.One(base64.StdEncoding.DecodeString(TAuxGolden)) + + dec, err := DecodeTAux(bytes.NewReader(gbytes)) + require.NoError(t, err) + + // print as json + enc, err := json.MarshalIndent(dec, "", " ") + require.NoError(t, err) + t.Log(string(enc)) + + var reenc bytes.Buffer + require.NoError(t, EncodeTAux(&reenc, *dec)) + + require.Equal(t, gbytes, reenc.Bytes()) +} diff --git a/lib/proof/t_aux_types.go b/lib/proof/t_aux_types.go new file mode 100644 index 000000000..716b53a26 --- /dev/null +++ b/lib/proof/t_aux_types.go @@ -0,0 +1,176 @@ +package proof + +import ( + "io" + + "golang.org/x/xerrors" +) + +type StoreConfig struct { + // A directory in which data (a merkle tree) can be persisted. + Path string + + // A unique identifier used to help specify the on-disk store location for this particular data. + ID string + + // The number of elements in the DiskStore. This field is optional, and unused internally. + Size *uint64 + + // The number of merkle tree rows_to_discard then cache on disk. + RowsToDiscard uint64 +} + +type TAuxLabels struct { + Labels []StoreConfig +} + +type TemporaryAux struct { + Labels TAuxLabels + TreeDConfig StoreConfig + TreeRConfig StoreConfig + TreeCConfig StoreConfig +} + +func DecodeStoreConfig(r io.Reader) (StoreConfig, error) { + var sc StoreConfig + var err error + + sc.Path, err = ReadString(r) + if err != nil { + return StoreConfig{}, xerrors.Errorf("failed to decode path: %w", err) + } + + sc.ID, err = ReadString(r) + if err != nil { + return StoreConfig{}, xerrors.Errorf("failed to decode ID: %w", err) + } + + // Size in an option, so prefixed with a 0x01 byte if present or 0x00 if not. + var b [1]byte + if _, err := io.ReadFull(r, b[:]); err != nil { + return StoreConfig{}, xerrors.Errorf("failed to read size present byte: %w", err) + } + if b[0] == 0x01 { + size, err := ReadLE[uint64](r) + if err != nil { + return StoreConfig{}, xerrors.Errorf("failed to read size: %w", err) + } + sc.Size = &size + } + + sc.RowsToDiscard, err = ReadLE[uint64](r) + if err != nil { + return StoreConfig{}, xerrors.Errorf("failed to read rows to discard: %w", err) + } + + return sc, nil +} + +func DecodeLabels(r io.Reader) (*TAuxLabels, error) { + numLabels, err := ReadLE[uint64](r) + if err != nil { + return nil, xerrors.Errorf("failed to read number of labels: %w", err) + } + + labels := make([]StoreConfig, numLabels) + for i := range labels { + labels[i], err = DecodeStoreConfig(r) + if err != nil { + return nil, xerrors.Errorf("failed to decode label %d: %w", i, err) + } + } + + return &TAuxLabels{Labels: labels}, nil +} + +func DecodeTAux(r io.Reader) (*TemporaryAux, error) { + labels, err := DecodeLabels(r) + if err != nil { + return nil, xerrors.Errorf("failed to decode labels: %w", err) + } + + treeDConfig, err := DecodeStoreConfig(r) + if err != nil { + return nil, xerrors.Errorf("failed to decode treeDConfig: %w", err) + } + + treeRConfig, err := DecodeStoreConfig(r) + if err != nil { + return nil, xerrors.Errorf("failed to decode treeRConfig: %w", err) + } + + treeCConfig, err := DecodeStoreConfig(r) + if err != nil { + return nil, xerrors.Errorf("failed to decode treeCConfig: %w", err) + } + + return &TemporaryAux{ + Labels: *labels, + TreeDConfig: treeDConfig, + TreeRConfig: treeRConfig, + TreeCConfig: treeCConfig, + }, nil +} + +func EncodeStoreConfig(w io.Writer, sc StoreConfig) error { + if err := WriteString(w, sc.Path); err != nil { + return xerrors.Errorf("failed to encode path: %w", err) + } + + if err := WriteString(w, sc.ID); err != nil { + return xerrors.Errorf("failed to encode ID: %w", err) + } + + if sc.Size != nil { + if _, err := w.Write([]byte{0x01}); err != nil { + return xerrors.Errorf("failed to write size present byte: %w", err) + } + if err := WriteLE(w, *sc.Size); err != nil { + return xerrors.Errorf("failed to write size: %w", err) + } + } else { + if _, err := w.Write([]byte{0x00}); err != nil { + return xerrors.Errorf("failed to write size absent byte: %w", err) + } + } + + if err := WriteLE(w, sc.RowsToDiscard); err != nil { + return xerrors.Errorf("failed to write rows to discard: %w", err) + } + + return nil +} + +func EncodeLabels(w io.Writer, labels TAuxLabels) error { + if err := WriteLE(w, uint64(len(labels.Labels))); err != nil { + return xerrors.Errorf("failed to write number of labels: %w", err) + } + + for i, label := range labels.Labels { + if err := EncodeStoreConfig(w, label); err != nil { + return xerrors.Errorf("failed to encode label %d: %w", i, err) + } + } + + return nil +} + +func EncodeTAux(w io.Writer, taux TemporaryAux) error { + if err := EncodeLabels(w, taux.Labels); err != nil { + return xerrors.Errorf("failed to encode labels: %w", err) + } + + if err := EncodeStoreConfig(w, taux.TreeDConfig); err != nil { + return xerrors.Errorf("failed to encode treeDConfig: %w", err) + } + + if err := EncodeStoreConfig(w, taux.TreeRConfig); err != nil { + return xerrors.Errorf("failed to encode treeRConfig: %w", err) + } + + if err := EncodeStoreConfig(w, taux.TreeCConfig); err != nil { + return xerrors.Errorf("failed to encode treeCConfig: %w", err) + } + + return nil +}