From 1d4f5397a200e7b0c0bdb17803313dc53fe28b8b Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 6 Mar 2024 11:38:06 +0000 Subject: [PATCH] Add tests to sanity-check embedded built-in actors metadata (#11684) Add tests that assert the embedded built-in actors metadata is correct: * the corresponding CAR file is present in built-in actors released assets as a CAR file. * manifest CID is the only root CID in the corresponding CAR file. * actor CIDs are present in the corresponding CAR file. Fixes #11683 --- .github/workflows/builtin-actor-tests.yml | 18 ++++ build/builtin_actors_gen_test.go | 107 ++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 .github/workflows/builtin-actor-tests.yml create mode 100644 build/builtin_actors_gen_test.go diff --git a/.github/workflows/builtin-actor-tests.yml b/.github/workflows/builtin-actor-tests.yml new file mode 100644 index 00000000000..6cc49cc4688 --- /dev/null +++ b/.github/workflows/builtin-actor-tests.yml @@ -0,0 +1,18 @@ +name: Built-in Actors +on: + push: + paths: + - build/actors + - build/builtin_actors_gen.go + branches: + - release/* +jobs: + release: + name: Release Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v3 + with: + go-version: 1.21 + - run: go test -tags=release ./build \ No newline at end of file diff --git a/build/builtin_actors_gen_test.go b/build/builtin_actors_gen_test.go new file mode 100644 index 00000000000..1338097f7b2 --- /dev/null +++ b/build/builtin_actors_gen_test.go @@ -0,0 +1,107 @@ +//go:build release +// +build release + +package build_test + +import ( + "archive/tar" + "crypto/sha256" + "encoding/hex" + "errors" + "fmt" + "io" + "net/http" + "os" + "strings" + "testing" + + "github.com/DataDog/zstd" + "github.com/ipfs/go-cid" + "github.com/ipld/go-car/v2" + "github.com/stretchr/testify/require" + + actorstypes "github.com/filecoin-project/go-state-types/actors" + + "github.com/filecoin-project/lotus/build" +) + +func TestEmbeddedBuiltinActorsMetadata(t *testing.T) { + subjectsByVersionByNetworks := make(map[actorstypes.Version]map[string]*build.BuiltinActorsMetadata) + for _, subject := range build.EmbeddedBuiltinActorsMetadata { + if subject.BundleGitTag == "" { + // BundleGitTag is required to verify the SHA-256 checksum. + // The pack script only includes this for the latest network version, and it is good enough to only + // check the latest network version metadata. Hence the skip. + continue + } + v, ok := subjectsByVersionByNetworks[subject.Version] + if !ok { + v = make(map[string]*build.BuiltinActorsMetadata) + } + v[subject.Network] = subject + subjectsByVersionByNetworks[subject.Version] = v + } + + for version, networks := range subjectsByVersionByNetworks { + cachedCar, err := os.Open(fmt.Sprintf("./actors/v%v.tar.zst", version)) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, cachedCar.Close()) }) + tarReader := tar.NewReader(zstd.NewReader(cachedCar)) + for { + header, err := tarReader.Next() + if errors.Is(err, io.EOF) { + break + } + require.NoError(t, err) + + network := strings.TrimSuffix(strings.TrimPrefix(header.Name, "builtin-actors-"), ".car") + subject, found := networks[network] + if !found { + continue + } + + shaURL := fmt.Sprintf("https://github.com/filecoin-project/builtin-actors/releases/download/%s/builtin-actors-%s.sha256", subject.BundleGitTag, subject.Network) + resp, err := http.Get(shaURL) + require.NoError(t, err, "failed to retrieve CAR SHA") + require.Equal(t, http.StatusOK, resp.StatusCode, "unexpected response status code while retrieving CAR SHA") + + respBody, err := io.ReadAll(resp.Body) + require.NoError(t, resp.Body.Close()) + require.NoError(t, err) + fields := strings.Fields(string(respBody)) + require.Len(t, fields, 2) + wantShaHex := fields[0] + + hasher := sha256.New() + reader, err := car.NewBlockReader(io.TeeReader(tarReader, hasher)) + require.NoError(t, err) + + require.EqualValues(t, 1, reader.Version) + require.Len(t, reader.Roots, 1, "expected exactly one root CID for builtin actors bundle network %s, version %v", subject.Network, subject.Version) + require.True(t, reader.Roots[0].Equals(subject.ManifestCid), "manifest CID does not match") + + subjectActorsByCid := make(map[cid.Cid]string) + for name, c := range subject.Actors { + subjectActorsByCid[c] = name + } + for { + next, err := reader.Next() + if errors.Is(err, io.EOF) { + break + } + require.NoError(t, err) + name, found := subjectActorsByCid[next.Cid()] + if found { + t.Logf("OK: %sv%v/%s -> %s", subject.Network, subject.Version, name, next.Cid()) + delete(subjectActorsByCid, next.Cid()) + } + } + require.Empty(t, subjectActorsByCid, "ZST CAR bundle did not contain CIDs for all actors; missing: %v", subjectActorsByCid) + + gotShaHex := hex.EncodeToString(hasher.Sum(nil)) + require.Equal(t, wantShaHex, gotShaHex, "SHA-256 digest of ZST CAR bundle does not match builtin-actors release") + delete(networks, network) + } + require.Empty(t, networks, "CAR bundle did not contain CIDs for network; missing: %v", networks) + } +}