From dc47bc6fac553c51a887c27e12e7cc1167cc213c Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Tue, 2 May 2023 13:08:45 +0200 Subject: [PATCH 1/9] feat: add tooling to check car files --- go.mod | 4 + go.sum | 6 ++ tooling/check/_fixtures/hello_ipfs.car | Bin 0 -> 107 bytes tooling/check/car.go | 132 +++++++++++++++++++++++++ tooling/check/car_test.go | 46 +++++++++ 5 files changed, 188 insertions(+) create mode 100644 tooling/check/_fixtures/hello_ipfs.car create mode 100644 tooling/check/car.go create mode 100644 tooling/check/car_test.go diff --git a/go.mod b/go.mod index 07b4cc2cd..9fdd0f9e0 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/ipfs/boxo v0.8.0-rc3 github.com/ipfs/go-cid v0.4.0 github.com/ipld/go-ipld-prime v0.20.0 + github.com/stretchr/testify v1.8.2 github.com/urfave/cli/v2 v2.25.0 ) @@ -13,11 +14,14 @@ require ( github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/ipld/go-codec-dagpb v1.6.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect go.uber.org/goleak v1.1.12 // indirect golang.org/x/sync v0.1.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( diff --git a/go.sum b/go.sum index ae56defbd..3208c7765 100644 --- a/go.sum +++ b/go.sum @@ -200,10 +200,15 @@ github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3 github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.25.0 h1:ykdZKuQey2zq0yin/l7JOm9Mh+pg72ngYMeB0ABn6q8= github.com/urfave/cli/v2 v2.25.0/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= @@ -321,6 +326,7 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= diff --git a/tooling/check/_fixtures/hello_ipfs.car b/tooling/check/_fixtures/hello_ipfs.car new file mode 100644 index 0000000000000000000000000000000000000000..5c541e430ea6d1aed7c20545c40c9a56994ac546 GIT binary patch literal 107 zcmcColv 0 { + roots, err := bs.Roots() + if err != nil { + return CheckOutput{ + Success: false, + Reason: fmt.Sprintf("failed to get roots: %v", err), + } + } + + for _, rootCID := range c.rootCIDs { + // check that rootCID is in roots: + found := false + for _, root := range roots { + if root.Equals(rootCID) { + found = true + break + } + } + + if !found { + return CheckOutput{ + Success: false, + Reason: fmt.Sprintf("root '%s' not found in car file", rootCID), + } + } + } + } + + return CheckOutput{ + Success: true, + } +} diff --git a/tooling/check/car_test.go b/tooling/check/car_test.go new file mode 100644 index 000000000..eaa58c453 --- /dev/null +++ b/tooling/check/car_test.go @@ -0,0 +1,46 @@ +package check + +import ( + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func loadCarFile(t *testing.T, carFilePath string) []byte { + file, err := os.Open(carFilePath) + if err != nil { + t.Fatalf("failed to open car file: %v", err) + } + defer file.Close() + + fileBytes, err := ioutil.ReadAll(file) + if err != nil { + t.Fatalf("failed to read car file: %v", err) + } + + return fileBytes +} + +func TestHasFile(t *testing.T) { + c1 := IsCar(). + HasBlock("bafkreidfdrlkeq4m4xnxuyx6iae76fdm4wgl5d4xzsb77ixhyqwumhz244"). + HasBlockWithContent("bafkreidfdrlkeq4m4xnxuyx6iae76fdm4wgl5d4xzsb77ixhyqwumhz244", []byte("Hello IPFS\n")). + HasRoot("bafkreidfdrlkeq4m4xnxuyx6iae76fdm4wgl5d4xzsb77ixhyqwumhz244") + + // invalid CID + c2 := IsCar(). + HasBlock("bafkreiac7wncixdkhdew6wwnzya36b54t7nxcnhps377fjgtmezddnj6em") + + // invalid content + c3 := IsCar(). + HasBlock("bafkreidfdrlkeq4m4xnxuyx6iae76fdm4wgl5d4xzsb77ixhyqwumhz244"). + HasBlockWithContent("bafkreidfdrlkeq4m4xnxuyx6iae76fdm4wgl5d4xzsb77ixhyqwumhz244", []byte("Invalid Content\n")) + + block := loadCarFile(t, "./_fixtures/hello_ipfs.car") + + assert.Equal(t, true, c1.Check(block).Success) + assert.Equal(t, false, c2.Check(block).Success) + assert.Equal(t, false, c3.Check(block).Success) +} From a75cbdd5ef4ccb9633ef645a9e2c8f59c8a55cc4 Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Tue, 2 May 2023 13:15:26 +0200 Subject: [PATCH 2/9] refactor: simplif code --- tooling/check/car.go | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/tooling/check/car.go b/tooling/check/car.go index 38ff89e0f..3de7d834d 100644 --- a/tooling/check/car.go +++ b/tooling/check/car.go @@ -22,30 +22,26 @@ func IsCar() *CheckIsCarFile { } } -func (c *CheckIsCarFile) HasBlock(cidStr string) *CheckIsCarFile { +func decoded(cidStr string) cid.Cid { cid, err := cid.Decode(cidStr) if err != nil { panic(fmt.Errorf("invalid CID: %w", err)) } - c.blockCIDs = append(c.blockCIDs, cid) + return cid +} + +func (c *CheckIsCarFile) HasBlock(cidStr string) *CheckIsCarFile { + c.blockCIDs = append(c.blockCIDs, decoded(cidStr)) return c } func (c *CheckIsCarFile) HasRoot(cidStr string) *CheckIsCarFile { - cid, err := cid.Decode(cidStr) - if err != nil { - panic(fmt.Errorf("invalid CID: %w", err)) - } - c.rootCIDs = append(c.rootCIDs, cid) + c.rootCIDs = append(c.rootCIDs, decoded(cidStr)) return c } func (c *CheckIsCarFile) HasBlockWithContent(cidStr string, content []byte) *CheckIsCarFile { - cid, err := cid.Decode(cidStr) - if err != nil { - panic(fmt.Errorf("invalid CID: %w", err)) - } - c.blocksWithContent[cid] = content + c.blocksWithContent[decoded(cidStr)] = content return c } From f5e272f9ab35528dba55ed9491ec4e3a48859cfa Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Tue, 2 May 2023 13:35:43 +0200 Subject: [PATCH 3/9] fix: no side-effects --- tooling/check/car.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tooling/check/car.go b/tooling/check/car.go index 3de7d834d..d470b8e3c 100644 --- a/tooling/check/car.go +++ b/tooling/check/car.go @@ -11,14 +11,15 @@ import ( type CheckIsCarFile struct { blockCIDs []cid.Cid - blocksWithContent map[cid.Cid][]byte rootCIDs []cid.Cid + blocksWithContent map[cid.Cid][]byte } func IsCar() *CheckIsCarFile { return &CheckIsCarFile{ blockCIDs: []cid.Cid{}, blocksWithContent: map[cid.Cid][]byte{}, + rootCIDs: []cid.Cid{}, } } @@ -30,18 +31,27 @@ func decoded(cidStr string) cid.Cid { return cid } -func (c *CheckIsCarFile) HasBlock(cidStr string) *CheckIsCarFile { +func (c CheckIsCarFile) HasBlock(cidStr string) CheckIsCarFile { c.blockCIDs = append(c.blockCIDs, decoded(cidStr)) return c } -func (c *CheckIsCarFile) HasRoot(cidStr string) *CheckIsCarFile { +func (c CheckIsCarFile) HasRoot(cidStr string) CheckIsCarFile { c.rootCIDs = append(c.rootCIDs, decoded(cidStr)) return c } -func (c *CheckIsCarFile) HasBlockWithContent(cidStr string, content []byte) *CheckIsCarFile { +func (c CheckIsCarFile) HasBlockWithContent(cidStr string, content []byte) CheckIsCarFile { + // Clone previous map to prevent side-effects + current := c.blocksWithContent + c.blocksWithContent = make(map[cid.Cid][]byte, len(c.blocksWithContent)+1) + for k, v := range current { + c.blocksWithContent[k] = v + } + + // Update with new value c.blocksWithContent[decoded(cidStr)] = content + return c } From d73651369ad3e2db0696c509a228d3a0a642019e Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Tue, 2 May 2023 14:06:30 +0200 Subject: [PATCH 4/9] fix: update to go 1.20 --- go.mod | 36 ++++++++++++++++++------------------ go.sum | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index 9fdd0f9e0..8088f2391 100644 --- a/go.mod +++ b/go.mod @@ -1,17 +1,17 @@ module github.com/ipfs/gateway-conformance -go 1.19 +go 1.20 require ( - github.com/ipfs/boxo v0.8.0-rc3 - github.com/ipfs/go-cid v0.4.0 + github.com/ipfs/boxo v0.8.1 + github.com/ipfs/go-cid v0.4.1 github.com/ipld/go-ipld-prime v0.20.0 github.com/stretchr/testify v1.8.2 - github.com/urfave/cli/v2 v2.25.0 + github.com/urfave/cli/v2 v2.25.3 ) require ( - github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a // indirect + github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -25,7 +25,7 @@ require ( ) require ( - github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/uuid v1.3.0 // indirect @@ -42,14 +42,14 @@ require ( github.com/ipfs/go-log/v2 v2.5.1 // indirect github.com/ipfs/go-metrics-interface v0.0.1 // indirect github.com/jbenet/goprocess v0.1.4 // indirect - github.com/klauspost/cpuid/v2 v2.2.3 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/mattn/go-isatty v0.0.18 // indirect github.com/minio/sha256-simd v1.0.0 // indirect github.com/mr-tron/base58 v1.2.0 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect - github.com/multiformats/go-multibase v0.1.1 - github.com/multiformats/go-multicodec v0.8.1 + github.com/multiformats/go-multibase v0.2.0 + github.com/multiformats/go-multicodec v0.9.0 github.com/multiformats/go-multihash v0.2.1 github.com/multiformats/go-varint v0.0.7 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect @@ -57,16 +57,16 @@ require ( github.com/polydawn/refmt v0.89.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 // indirect - github.com/whyrusleeping/cbor-gen v0.0.0-20230126041949-52956bd4c9aa // indirect - go.opentelemetry.io/otel v1.14.0 // indirect - go.opentelemetry.io/otel/trace v1.14.0 // indirect + github.com/whyrusleeping/cbor-gen v0.0.0-20230418232409-daab9ece03a0 // indirect + go.opentelemetry.io/otel v1.15.0 // indirect + go.opentelemetry.io/otel/trace v1.15.0 // indirect go.uber.org/atomic v1.10.0 // indirect - go.uber.org/multierr v1.9.0 // indirect + go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/crypto v0.6.0 // indirect - golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb // indirect - golang.org/x/sys v0.6.0 // indirect + golang.org/x/crypto v0.8.0 // indirect + golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect + golang.org/x/sys v0.7.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/protobuf v1.28.1 // indirect + google.golang.org/protobuf v1.30.0 // indirect lukechampine.com/blake3 v1.1.7 // indirect ) diff --git a/go.sum b/go.sum index 3208c7765..dad4a8d01 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a h1:E/8AP5dFtMhl5KPJz66Kt9G0n+7Sn41Fy1wv9/jHOrc= github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -21,6 +23,8 @@ github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0X github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= @@ -47,6 +51,8 @@ github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= github.com/ipfs/boxo v0.8.0-rc3 h1:rttpGdhLE0zeTec8f2/e5YDgCYzEQf7dI4eRglu2ktc= github.com/ipfs/boxo v0.8.0-rc3/go.mod h1:RIsi4CnTyQ7AUsNn5gXljJYZlQrHBMnJp94p73liFiA= +github.com/ipfs/boxo v0.8.1 h1:3DkKBCK+3rdEB5t77WDShUXXhktYwH99mkAsgajsKrU= +github.com/ipfs/boxo v0.8.1/go.mod h1:xJ2hVb4La5WyD7GvKYE0lq2g1rmQZoCD2K4WNrV6aZI= github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA= github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU= github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY= @@ -61,6 +67,8 @@ github.com/ipfs/go-cid v0.0.6/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqg github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= github.com/ipfs/go-cid v0.4.0 h1:a4pdZq0sx6ZSxbCizebnKiMCx/xI/aBBFlB73IgH4rA= github.com/ipfs/go-cid v0.4.0/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= +github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= +github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk= github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= @@ -109,6 +117,8 @@ github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/koron/go-ssdp v0.0.3 h1:JivLMY45N76b4p/vsWGOKewBQu6uf39y8l+AQ7sDKx8= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -129,6 +139,8 @@ github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9t github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= @@ -154,8 +166,12 @@ github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/g github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= github.com/multiformats/go-multibase v0.1.1 h1:3ASCDsuLX8+j4kx58qnJ4YFq/JWTJpCyDW27ztsVTOI= github.com/multiformats/go-multibase v0.1.1/go.mod h1:ZEjHE+IsUrgp5mhlEAYjMtZwK1k4haNkcaPg9aoe1a8= +github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= +github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= github.com/multiformats/go-multicodec v0.8.1 h1:ycepHwavHafh3grIbR1jIXnKCsFm0fqsfEOsJ8NtKE8= github.com/multiformats/go-multicodec v0.8.1/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= +github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg= +github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= @@ -212,6 +228,8 @@ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.25.0 h1:ykdZKuQey2zq0yin/l7JOm9Mh+pg72ngYMeB0ABn6q8= github.com/urfave/cli/v2 v2.25.0/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/urfave/cli/v2 v2.25.3 h1:VJkt6wvEBOoSjPFQvOkv6iWIrsJyCrKGtCtxXWwmGeY= +github.com/urfave/cli/v2 v2.25.3/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= github.com/warpfork/go-testmark v0.11.0 h1:J6LnV8KpceDvo7spaNU4+DauH2n1x+6RaO2rJrmpQ9U= github.com/warpfork/go-wish v0.0.0-20180510122957-5ad1f5abf436/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= @@ -222,6 +240,8 @@ github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11/go.mod h1:Wlo/S github.com/whyrusleeping/cbor-gen v0.0.0-20200123233031-1cdf64d27158/go.mod h1:Xj/M2wWU+QdTdRbu/L/1dIZY8/Wb2K9pAhtroQuxJJI= github.com/whyrusleeping/cbor-gen v0.0.0-20230126041949-52956bd4c9aa h1:EyA027ZAkuaCLoxVX4r1TZMPy1d31fM6hbfQ4OU4I5o= github.com/whyrusleeping/cbor-gen v0.0.0-20230126041949-52956bd4c9aa/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= +github.com/whyrusleeping/cbor-gen v0.0.0-20230418232409-daab9ece03a0 h1:XYEgH2nJgsrcrj32p+SAbx6T3s/6QknOXezXtz7kzbg= +github.com/whyrusleeping/cbor-gen v0.0.0-20230418232409-daab9ece03a0/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= @@ -230,8 +250,12 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= +go.opentelemetry.io/otel v1.15.0 h1:NIl24d4eiLJPM0vKn4HjLYM+UZf6gSfi9Z+NmCxkWbk= +go.opentelemetry.io/otel v1.15.0/go.mod h1:qfwLEbWhLPk5gyWrne4XnF0lC8wtywbuJbgfAE3zbek= go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M= go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= +go.opentelemetry.io/otel/trace v1.15.0 h1:5Fwje4O2ooOxkfyqI/kJwxWotggDLix4BSAvpE1wlpo= +go.opentelemetry.io/otel/trace v1.15.0/go.mod h1:CUsmE2Ht1CRkvE8OsMESvraoZrrcgD1J2W8GV1ev0Y4= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= @@ -243,6 +267,8 @@ go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKY go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= @@ -257,8 +283,12 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w= golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o= +golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -292,6 +322,8 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -316,6 +348,8 @@ golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNq google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= From abc2ebf40879cc323fcf154780cfb81c4a542bc1 Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Thu, 4 May 2023 13:02:35 +0200 Subject: [PATCH 5/9] feat: update with exactly checks --- tooling/check/car.go | 117 +++++++++++++-------------------- tooling/check/car_test.go | 25 +++++-- tooling/check/cid.go | 123 ++++++++++++++++++++++++++++++++++ tooling/check/cid_test.go | 134 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 322 insertions(+), 77 deletions(-) create mode 100644 tooling/check/cid.go create mode 100644 tooling/check/cid_test.go diff --git a/tooling/check/car.go b/tooling/check/car.go index d470b8e3c..f58cbf554 100644 --- a/tooling/check/car.go +++ b/tooling/check/car.go @@ -1,25 +1,24 @@ package check import ( - "bytes" - "context" "fmt" - "github.com/ipfs/boxo/ipld/car/v2/blockstore" "github.com/ipfs/go-cid" ) type CheckIsCarFile struct { - blockCIDs []cid.Cid - rootCIDs []cid.Cid - blocksWithContent map[cid.Cid][]byte + blockCIDs []cid.Cid + rootCIDs []cid.Cid + isExact bool + isOrdered bool } func IsCar() *CheckIsCarFile { return &CheckIsCarFile{ - blockCIDs: []cid.Cid{}, - blocksWithContent: map[cid.Cid][]byte{}, - rootCIDs: []cid.Cid{}, + blockCIDs: []cid.Cid{}, + rootCIDs: []cid.Cid{}, + isExact: false, + isOrdered: false, } } @@ -36,99 +35,77 @@ func (c CheckIsCarFile) HasBlock(cidStr string) CheckIsCarFile { return c } +func (c CheckIsCarFile) HasBlocks(cidStrs ...string) CheckIsCarFile { + for _, cidStr := range cidStrs { + c.blockCIDs = append(c.blockCIDs, decoded(cidStr)) + } + return c +} + func (c CheckIsCarFile) HasRoot(cidStr string) CheckIsCarFile { c.rootCIDs = append(c.rootCIDs, decoded(cidStr)) return c } -func (c CheckIsCarFile) HasBlockWithContent(cidStr string, content []byte) CheckIsCarFile { - // Clone previous map to prevent side-effects - current := c.blocksWithContent - c.blocksWithContent = make(map[cid.Cid][]byte, len(c.blocksWithContent)+1) - for k, v := range current { - c.blocksWithContent[k] = v +func (c CheckIsCarFile) HasRoots(cidStrs ...string) CheckIsCarFile { + for _, cidStr := range cidStrs { + c.rootCIDs = append(c.rootCIDs, decoded(cidStr)) } + return c +} - // Update with new value - c.blocksWithContent[decoded(cidStr)] = content +func (c CheckIsCarFile) Exactly() CheckIsCarFile { + c.isExact = true + return c +} +func (c CheckIsCarFile) InThatOrder() CheckIsCarFile { + c.isOrdered = true return c } func (c *CheckIsCarFile) Check(carContent []byte) CheckOutput { - reader := bytes.NewReader(carContent) - bs, err := blockstore.NewReadOnly(reader, nil) - + gotCIDs, err := listAllCids(carContent) if err != nil { return CheckOutput{ Success: false, - Reason: fmt.Sprintf("failed to open car file: %v", err), + Reason: fmt.Sprintf("failed to list all cids: %v", err), } } - defer bs.Close() - for _, blockCID := range c.blockCIDs { - has, err := bs.Has(context.Background(), blockCID) - if err != nil { - return CheckOutput{ - Success: false, - Reason: fmt.Sprintf("failed to check for block '%s': %v", blockCID, err), - } + cmp := CidSetContains + + if c.isExact { + if c.isOrdered { + cmp = CidArrayEquals + } else { + cmp = CidSetEquals } - if !has { - return CheckOutput{ - Success: false, - Reason: fmt.Sprintf("block '%s' not found in car file", blockCID), - } + } else { + if c.isOrdered { + cmp = CidOrderedSubsetContains } } - for blockCID, expectedContent := range c.blocksWithContent { - blockData, err := bs.Get(context.Background(), blockCID) - if err != nil { - return CheckOutput{ - Success: false, - Reason: fmt.Sprintf("failed to get block '%s': %v", blockCID, err), - } - } - - b1 := blockData.RawData() - b2 := expectedContent + output := cmp(gotCIDs, c.blockCIDs) - // diff the bytes: - if !bytes.Equal(b1, b2) { - return CheckOutput{ - Success: false, - Reason: fmt.Sprintf("block '%s' with expected content not found in car file.", blockCID), - } - } + if !output.Success { + return output } - if len(c.rootCIDs) > 0 { - roots, err := bs.Roots() + if len(c.rootCIDs) > 0 || c.isExact { + gotRoots, err := listAllRoots(carContent) if err != nil { return CheckOutput{ Success: false, - Reason: fmt.Sprintf("failed to get roots: %v", err), + Reason: fmt.Sprintf("failed to list all roots: %v", err), } } - for _, rootCID := range c.rootCIDs { - // check that rootCID is in roots: - found := false - for _, root := range roots { - if root.Equals(rootCID) { - found = true - break - } - } + output = cmp(gotRoots, c.rootCIDs) - if !found { - return CheckOutput{ - Success: false, - Reason: fmt.Sprintf("root '%s' not found in car file", rootCID), - } - } + if !output.Success { + return output } } diff --git a/tooling/check/car_test.go b/tooling/check/car_test.go index eaa58c453..947283aeb 100644 --- a/tooling/check/car_test.go +++ b/tooling/check/car_test.go @@ -24,23 +24,34 @@ func loadCarFile(t *testing.T, carFilePath string) []byte { } func TestHasFile(t *testing.T) { + block := loadCarFile(t, "./_fixtures/hello_ipfs.car") + c1 := IsCar(). HasBlock("bafkreidfdrlkeq4m4xnxuyx6iae76fdm4wgl5d4xzsb77ixhyqwumhz244"). - HasBlockWithContent("bafkreidfdrlkeq4m4xnxuyx6iae76fdm4wgl5d4xzsb77ixhyqwumhz244", []byte("Hello IPFS\n")). HasRoot("bafkreidfdrlkeq4m4xnxuyx6iae76fdm4wgl5d4xzsb77ixhyqwumhz244") + assert.True(t, c1.Check(block).Success) + // invalid CID c2 := IsCar(). HasBlock("bafkreiac7wncixdkhdew6wwnzya36b54t7nxcnhps377fjgtmezddnj6em") - // invalid content + assert.False(t, c2.Check(block).Success) + + // missing Roots c3 := IsCar(). HasBlock("bafkreidfdrlkeq4m4xnxuyx6iae76fdm4wgl5d4xzsb77ixhyqwumhz244"). - HasBlockWithContent("bafkreidfdrlkeq4m4xnxuyx6iae76fdm4wgl5d4xzsb77ixhyqwumhz244", []byte("Invalid Content\n")) + Exactly() - block := loadCarFile(t, "./_fixtures/hello_ipfs.car") + assert.False(t, c3.Check(block).Success) + + // more blocks than expected + c4 := IsCar() + assert.True(t, c4.Check(block).Success) + + // more blocks than expected, but exact + c5 := IsCar(). + Exactly() - assert.Equal(t, true, c1.Check(block).Success) - assert.Equal(t, false, c2.Check(block).Success) - assert.Equal(t, false, c3.Check(block).Success) + assert.False(t, c5.Check(block).Success) } diff --git a/tooling/check/cid.go b/tooling/check/cid.go new file mode 100644 index 000000000..488ee5389 --- /dev/null +++ b/tooling/check/cid.go @@ -0,0 +1,123 @@ +package check + +import ( + "bytes" + "fmt" + "io" + + "github.com/ipfs/boxo/ipld/car" + "github.com/ipfs/boxo/ipld/car/v2/blockstore" + "github.com/ipfs/go-cid" +) + +func CidSetContains(a, b []cid.Cid) CheckOutput { + s1 := cid.NewSet() + for _, cid := range a { + s1.Add(cid) + } + + // for each cid in b, check if it's in a + for _, cid := range b { + if !s1.Has(cid) { + return CheckOutput{ + Success: false, + Reason: fmt.Sprintf("missing CID %s", cid), + } + } + } + + return CheckOutput{ + Success: true, + } +} + +func CidSetEquals(a, b []cid.Cid) CheckOutput { + t1 := CidSetContains(a, b) + + if !t1.Success { + return t1 + } + + return CidSetContains(b, a) +} + +func CidArrayEquals(a, b []cid.Cid) CheckOutput { + if len(a) != len(b) { + return CheckOutput{ + Success: false, + Reason: fmt.Sprintf("length mismatch: %d != %d", len(a), len(b)), + } + } + + for i := 0; i < len(a); i++ { + if a[i] != b[i] { + return CheckOutput{ + Success: false, + Reason: fmt.Sprintf("mismatch at index %d: %s != %s", i, a[i], b[i]), + } + } + } + + return CheckOutput{ + Success: true, + } +} + +func CidOrderedSubsetContains(a, b []cid.Cid) CheckOutput { + i, j := 0, 0 + + for i < len(a) && j < len(b) { + if a[i] == b[j] { + i++ + j++ + continue + } + + i++ + } + + if j != len(b) { + return CheckOutput{ + Success: false, + Reason: fmt.Sprintf("missing CID %s", b[j]), + } + } + + return CheckOutput{ + Success: true, + } +} + +func listAllCids(carContent []byte) ([]cid.Cid, error) { + reader := bytes.NewReader(carContent) + cr, err := car.NewCarReader(reader) + if err != nil { + return nil, err + } + + // aggregate all blocks, ordered + var gotCIDs []cid.Cid + for { + block, err := cr.Next() + if err == io.EOF { + break + } + if err != nil { + return nil, err + } + gotCIDs = append(gotCIDs, block.Cid()) + } + + return gotCIDs, nil +} + +func listAllRoots(carContent []byte) ([]cid.Cid, error) { + reader := bytes.NewReader(carContent) + bs, err := blockstore.NewReadOnly(reader, nil) + + if err != nil { + return nil, err + } + + return bs.Roots() +} diff --git a/tooling/check/cid_test.go b/tooling/check/cid_test.go new file mode 100644 index 000000000..da27a0f57 --- /dev/null +++ b/tooling/check/cid_test.go @@ -0,0 +1,134 @@ +package check + +import ( + "testing" + + "github.com/ipfs/go-cid" + mh "github.com/multiformats/go-multihash" + "github.com/stretchr/testify/assert" +) + +func makeCID(s string) cid.Cid { + hash, err := mh.Sum([]byte(s), mh.SHA2_256, -1) + if err != nil { + panic(err) + } + + // Create a CID using the multihash + cidV1 := cid.NewCidV1(cid.Raw, hash) + + return cidV1 +} + +func cids(s ...string) []cid.Cid { + out := make([]cid.Cid, len(s)) + for i, str := range s { + out[i] = makeCID(str) + } + return out +} + +func TestCIDContains(t *testing.T) { + a := cids("hello") + b := cids() + assert.True(t, CidSetContains(a, b).Success) + + a = cids("hello") + b = cids("hello") + assert.True(t, CidSetContains(a, b).Success) + + a = cids("hello") + b = cids("world") + assert.False(t, CidSetContains(a, b).Success) + + a = cids("hello", "world") + b = cids("hello") + assert.True(t, CidSetContains(a, b).Success) +} + +func TestCIDEquals(t *testing.T) { + a := cids("hello") + b := cids() + assert.False(t, CidSetEquals(a, b).Success) + + a = cids("hello") + b = cids("hello") + assert.True(t, CidSetEquals(a, b).Success) + + a = cids("hello", "world") + b = cids("world", "hello") + assert.True(t, CidSetEquals(a, b).Success) + + a = cids("hello") + b = cids("world") + assert.False(t, CidSetEquals(a, b).Success) + + a = cids("hello", "world") + b = cids("hello") + assert.False(t, CidSetEquals(a, b).Success) +} + + +func TestCidArrayEquals(t *testing.T) { + a := cids("hello") + b := cids() + assert.False(t, CidArrayEquals(a, b).Success) + + a = cids("hello") + b = cids("hello") + assert.True(t, CidArrayEquals(a, b).Success) + + a = cids("hello", "world") + b = cids("world", "hello") + assert.False(t, CidArrayEquals(a, b).Success) + + a = cids("hello", "world") + b = cids("hello", "world", "foo") + assert.False(t, CidArrayEquals(a, b).Success) + + a = cids("hello", "world") + b = cids("hello", "world") + assert.True(t, CidArrayEquals(a, b).Success) + + a = cids("hello") + b = cids("world") + assert.False(t, CidArrayEquals(a, b).Success) + + a = cids("hello", "world") + b = cids("hello") + assert.False(t, CidArrayEquals(a, b).Success) +} + +func TestCidArrayContains(t *testing.T) { + a := cids("hello") + b := cids() + assert.True(t, CidOrderedSubsetContains(a, b).Success) + + a = cids("hello") + b = cids("hello") + assert.True(t, CidOrderedSubsetContains(a, b).Success) + + a = cids("hello", "world") + b = cids("world", "hello") + assert.False(t, CidOrderedSubsetContains(a, b).Success) + + a = cids("hello", "world") + b = cids("hello", "world", "foo") + assert.False(t, CidOrderedSubsetContains(a, b).Success) + + a = cids("hello", "world") + b = cids("hello", "world") + assert.True(t, CidOrderedSubsetContains(a, b).Success) + + a = cids("hello") + b = cids("world") + assert.False(t, CidOrderedSubsetContains(a, b).Success) + + a = cids("hello", "world") + b = cids("hello") + assert.True(t, CidOrderedSubsetContains(a, b).Success) + + a = cids("this", "hello", "world", "is", "a", "test") + b = cids("hello", "a", "test") + assert.True(t, CidOrderedSubsetContains(a, b).Success) +} From 3450907a979630239b7b9e8e29d786680c9a68cd Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Thu, 4 May 2023 15:58:46 +0200 Subject: [PATCH 6/9] feat: add mustgetchildren with testing fixtures --- tooling/car/_fixtures/Makefile | 4 + tooling/car/_fixtures/dag.car | Bin 0 -> 62198 bytes tooling/car/_fixtures/dir/a-file.txt | 2 + tooling/car/_fixtures/dir/b-file.txt | 98 +++++++++++++++++++++ tooling/car/_fixtures/dir/subdir/leaf.txt | 100 ++++++++++++++++++++++ tooling/car/fixture_test.go | 56 ++++++++++++ tooling/car/unixfs.go | 31 +++++++ tooling/check/car_test.go | 6 +- 8 files changed, 294 insertions(+), 3 deletions(-) create mode 100644 tooling/car/_fixtures/Makefile create mode 100644 tooling/car/_fixtures/dag.car create mode 100644 tooling/car/_fixtures/dir/a-file.txt create mode 100644 tooling/car/_fixtures/dir/b-file.txt create mode 100644 tooling/car/_fixtures/dir/subdir/leaf.txt create mode 100644 tooling/car/fixture_test.go diff --git a/tooling/car/_fixtures/Makefile b/tooling/car/_fixtures/Makefile new file mode 100644 index 000000000..185016cd4 --- /dev/null +++ b/tooling/car/_fixtures/Makefile @@ -0,0 +1,4 @@ +all: dag.car + +dag.car: + npx ipfs-car pack --no-wrap ./dir --output ./dag.car diff --git a/tooling/car/_fixtures/dag.car b/tooling/car/_fixtures/dag.car new file mode 100644 index 0000000000000000000000000000000000000000..b2d6a95473a72b611d3587dd86ab6a5e136aed7a GIT binary patch literal 62198 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 Date: Thu, 4 May 2023 16:58:55 +0200 Subject: [PATCH 7/9] fix: fix recursion --- tooling/car/fixture_test.go | 11 +++-- tooling/car/unixfs.go | 86 +++++++++++++++++++++++++++++-------- tooling/check/car_test.go | 2 +- tooling/check/cid_test.go | 5 +-- 4 files changed, 77 insertions(+), 27 deletions(-) diff --git a/tooling/car/fixture_test.go b/tooling/car/fixture_test.go index 94404ed17..801d9fc99 100644 --- a/tooling/car/fixture_test.go +++ b/tooling/car/fixture_test.go @@ -39,18 +39,17 @@ func TestGetNodes(t *testing.T) { assert.Equal(t, "bafkreihdhgb5vyuqu7jssreyo3h567obewtqq37fi5hr2w4um5icacry7m", leaf) nodes := f.MustGetChildren() - // TODO: We only get 3 for now, the ./subdir/leaf.txt is not included - - // assert.Len(t, nodes, 4) + + assert.Len(t, nodes, 4) assert.Equal(t, "bafkreidw23elffhagxz3oi6ctoibqouzfowfn3bwcvq2yzgd5n5h4gjyou", nodes[0].Cid().String()) assert.Equal(t, "bafkreiaeqsxxqwmsnhzhrlyr2udn25hpj24bs7gzcgkhbrkmhcuikcgh4a", nodes[1].Cid().String()) assert.Equal(t, "bafybeiaq6e55xratife7s5cmzjcmwy4adzzlk74sbdpfcq72gus6cweeeq", nodes[2].Cid().String()) - // assert.Equal(t, "bafkreihdhgb5vyuqu7jssreyo3h567obewtqq37fi5hr2w4um5icacry7m", nodes[3].Cid().String()) + assert.Equal(t, "bafkreihdhgb5vyuqu7jssreyo3h567obewtqq37fi5hr2w4um5icacry7m", nodes[3].Cid().String()) cids := f.MustGetChildrenCids() - // assert.Len(t, nodes, 4) + assert.Len(t, nodes, 4) assert.Equal(t, "bafkreidw23elffhagxz3oi6ctoibqouzfowfn3bwcvq2yzgd5n5h4gjyou", cids[0]) assert.Equal(t, "bafkreiaeqsxxqwmsnhzhrlyr2udn25hpj24bs7gzcgkhbrkmhcuikcgh4a", cids[1]) assert.Equal(t, "bafybeiaq6e55xratife7s5cmzjcmwy4adzzlk74sbdpfcq72gus6cweeeq", cids[2]) - // assert.Equal(t, "bafkreihdhgb5vyuqu7jssreyo3h567obewtqq37fi5hr2w4um5icacry7m", cids[3]) + assert.Equal(t, "bafkreihdhgb5vyuqu7jssreyo3h567obewtqq37fi5hr2w4um5icacry7m", cids[3]) } diff --git a/tooling/car/unixfs.go b/tooling/car/unixfs.go index 53efc8180..90437f1ca 100644 --- a/tooling/car/unixfs.go +++ b/tooling/car/unixfs.go @@ -52,25 +52,35 @@ func newUnixfsDagFromCar(file string) (*UnixfsDag, error) { return &UnixfsDag{dsvc: dsvc, cid: root[0]}, nil } +func (d *UnixfsDag) loadLinks(node format.Node) (map[string]*UnixfsDag, error) { + result := make(map[string]*UnixfsDag) + dir, err := io.NewDirectoryFromNode(d.dsvc, node) + if err != nil { + return nil, err + } + links, err := dir.Links(context.Background()) + if err != nil { + return nil, err + } + for _, l := range links { + result[l.Name] = &UnixfsDag{dsvc: d.dsvc, cid: l.Cid} + } + + return result, nil +} + func (d *UnixfsDag) getNode(names ...string) (format.Node, error) { for _, name := range names { node, err := d.getNode() if err != nil { return nil, err } + if d.links == nil { - d.links = make(map[string]*UnixfsDag) - dir, err := io.NewDirectoryFromNode(d.dsvc, node) + d.links, err = d.loadLinks(node) if err != nil { return nil, err } - links, err := dir.Links(context.Background()) - if err != nil { - return nil, err - } - for _, l := range links { - d.links[l.Name] = &UnixfsDag{dsvc: d.dsvc, cid: l.Cid} - } } d = d.links[name] @@ -78,6 +88,7 @@ func (d *UnixfsDag) getNode(names ...string) (format.Node, error) { return nil, fmt.Errorf("no link named %s", strings.Join(names, "/")) } } + if d.node == nil { node, err := d.dsvc.Get(context.Background(), d.cid) if err != nil { @@ -85,9 +96,53 @@ func (d *UnixfsDag) getNode(names ...string) (format.Node, error) { } d.node = node } + return d.node, nil } +func (d *UnixfsDag) listChildren(names ...string) ([][]string, error) { + node, err := d.getNode(names...) + if err != nil { + return nil, err + } + + result := [][]string{} + + var recursive func(format.Node, []string) error + + recursive = func(node format.Node, path []string) error { + result = append(result, path) + + links, err := d.loadLinks(node) + if err != nil { + fmt.Println(err) + } + // ignore the error: we might descend through files. + + var names []string + for name := range links { + names = append(names, name) + } + sort.Strings(names) + + for _, name := range names { + err := recursive(links[name].mustGetNode(), append(path, name)) + if err != nil { + return err + } + } + + return nil + } + + err = recursive(node, names) + if err != nil { + return nil, err + } + + return result[1:], nil +} + func (d *UnixfsDag) mustGetNode(names ...string) format.Node { node, err := d.getNode(names...) if err != nil { @@ -101,17 +156,14 @@ func (d *UnixfsDag) MustGetNode(names ...string) *FixtureNode { } func (d *UnixfsDag) MustGetChildren(names ...string) [](*FixtureNode) { - root := d.mustGetNode(names...) - - paths := root.Tree("", -1) - sort.Strings(paths) // depth first traversal paths is equivalent to lexicographic sort - fmt.Println("paths:", paths) + paths, err := d.listChildren(names...) + if err != nil { + panic(err) + } var nodes [](*FixtureNode) for _, path := range paths { - fmt.Println("paths:", path) - p := strings.Split(path, "/") - nodes = append(nodes, d.MustGetNode(p...)) + nodes = append(nodes, d.MustGetNode(path...)) } return nodes diff --git a/tooling/check/car_test.go b/tooling/check/car_test.go index b884d10a1..5fa0e7ca7 100644 --- a/tooling/check/car_test.go +++ b/tooling/check/car_test.go @@ -54,4 +54,4 @@ func TestHasFile(t *testing.T) { Exactly() assert.False(t, c5.Check(block).Success) -} \ No newline at end of file +} diff --git a/tooling/check/cid_test.go b/tooling/check/cid_test.go index da27a0f57..d2aae7473 100644 --- a/tooling/check/cid_test.go +++ b/tooling/check/cid_test.go @@ -68,7 +68,6 @@ func TestCIDEquals(t *testing.T) { assert.False(t, CidSetEquals(a, b).Success) } - func TestCidArrayEquals(t *testing.T) { a := cids("hello") b := cids() @@ -81,7 +80,7 @@ func TestCidArrayEquals(t *testing.T) { a = cids("hello", "world") b = cids("world", "hello") assert.False(t, CidArrayEquals(a, b).Success) - + a = cids("hello", "world") b = cids("hello", "world", "foo") assert.False(t, CidArrayEquals(a, b).Success) @@ -111,7 +110,7 @@ func TestCidArrayContains(t *testing.T) { a = cids("hello", "world") b = cids("world", "hello") assert.False(t, CidOrderedSubsetContains(a, b).Success) - + a = cids("hello", "world") b = cids("hello", "world", "foo") assert.False(t, CidOrderedSubsetContains(a, b).Success) From 6c11770a0829adf6765321b928d4160721c95362 Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Thu, 4 May 2023 17:00:30 +0200 Subject: [PATCH 8/9] refactor: remove dead code --- tooling/car/fixture_test.go | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/tooling/car/fixture_test.go b/tooling/car/fixture_test.go index 801d9fc99..95f8ca817 100644 --- a/tooling/car/fixture_test.go +++ b/tooling/car/fixture_test.go @@ -1,28 +1,11 @@ package car import ( - "io/ioutil" - "os" "testing" "github.com/stretchr/testify/assert" ) -func loadFile(t *testing.T, carFilePath string) []byte { - file, err := os.Open(carFilePath) - if err != nil { - t.Fatalf("failed to open car file: %v", err) - } - defer file.Close() - - fileBytes, err := ioutil.ReadAll(file) - if err != nil { - t.Fatalf("failed to read car file: %v", err) - } - - return fileBytes -} - func TestGetNodes(t *testing.T) { f := MustOpenUnixfsCar("./_fixtures/dag.car") From 0da5b932401843ca48cadc080cf2034a494cc975 Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Thu, 4 May 2023 17:09:05 +0200 Subject: [PATCH 9/9] test: improve testing in check --- tooling/check/_fixtures/Makefile | 4 + tooling/check/_fixtures/dag.car | Bin 0 -> 62198 bytes tooling/check/_fixtures/dir/a-file.txt | 2 + tooling/check/_fixtures/dir/b-file.txt | 98 +++++++++++++++++++ tooling/check/_fixtures/dir/subdir/leaf.txt | 100 ++++++++++++++++++++ tooling/check/car_test.go | 65 ++++++++++++- 6 files changed, 264 insertions(+), 5 deletions(-) create mode 100644 tooling/check/_fixtures/Makefile create mode 100644 tooling/check/_fixtures/dag.car create mode 100644 tooling/check/_fixtures/dir/a-file.txt create mode 100644 tooling/check/_fixtures/dir/b-file.txt create mode 100644 tooling/check/_fixtures/dir/subdir/leaf.txt diff --git a/tooling/check/_fixtures/Makefile b/tooling/check/_fixtures/Makefile new file mode 100644 index 000000000..185016cd4 --- /dev/null +++ b/tooling/check/_fixtures/Makefile @@ -0,0 +1,4 @@ +all: dag.car + +dag.car: + npx ipfs-car pack --no-wrap ./dir --output ./dag.car diff --git a/tooling/check/_fixtures/dag.car b/tooling/check/_fixtures/dag.car new file mode 100644 index 0000000000000000000000000000000000000000..b2d6a95473a72b611d3587dd86ab6a5e136aed7a GIT binary patch literal 62198 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