From d179f8317a60096665ccaf7afe81c29da90a0d0c Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Fri, 23 Jun 2023 11:40:52 +0200 Subject: [PATCH 01/16] ci: run against ipip-412 PR [REVERT BEFORE MERGING] --- .github/workflows/test-kubo-e2e.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-kubo-e2e.yml b/.github/workflows/test-kubo-e2e.yml index 424f24715..abbccfbb8 100644 --- a/.github/workflows/test-kubo-e2e.yml +++ b/.github/workflows/test-kubo-e2e.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - target: ['latest', 'master'] + target: ['latest', 'master', 'ipip-412'] defaults: run: shell: bash From a14160d0e2ecbc2b0149b505129c2df1bdc0d413 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Fri, 23 Jun 2023 11:41:05 +0200 Subject: [PATCH 02/16] feat: add duplicates test --- .../dir-with-duplicate-files.car | Bin 0 -> 1939 bytes tests/trustless_gateway_car_test.go | 73 ++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 fixtures/trustless_gateway_car/dir-with-duplicate-files.car diff --git a/fixtures/trustless_gateway_car/dir-with-duplicate-files.car b/fixtures/trustless_gateway_car/dir-with-duplicate-files.car new file mode 100644 index 0000000000000000000000000000000000000000..d0accae1597921dc5f64832b872382a3af23c2bb GIT binary patch literal 1939 zcma)7ZERFU6m8cgwreeBsi95LPOKVJ*Vkeb6=`Uot;MuhqG=@>iL>)&w^L@`yw1mh z7;UMVnwTgW6-k4N@J9`ipy7u_BK}cHiUB2jXt2RlH1Qh~h!F73zTKAo+9sRqX6AmJ zd(OGD_rFJ#ltynp(YW@ulF~4N&a@8q*P^oVW>PXHSq(dYcw>jlZ&aE9-8q>Av1Oa&WTo=Fcy_ zaCF;_*LHO!Dh4R$GM7}DSPGqB^e;NzZTon)H+875p}hLQ^P+9>9eZMB)&JFB866~d zZ`zWm5|$g5;*zY-WuC8y$I46F=QCff9l~MA8NmklYsv<`$3pIV1gm(nt5FPeRAYlw z+>dX|v0oJNoe_uI*T24cPg`qKr@!{{sn5i^C#J`D?mO}Gp~sgW8}D2G@WMn%%}6Yf z55ClJxNK@k?3wiG6?^%DOP};L-22fVy=TwAdDGdh_D_ea=LCOuf%Wswo$Uv%oUR+b zr~TSvwZHqTHtc)gmzLeXj=a^oV@`1Qx^GtAxbla(#jj3&`OhsAXI?&4cX3SoRQdIy zjW3EnJC{bmjS=R(Z~s_%_>JCY_m@|yw(WnlHe7cuc6{rF?>in_bo9uQsplX#7GGE% zs~IgvBeB^}Y*);YS#0DUsi;6K)K(y0a;cDJ24O)>5)m0_N(?ntA?&k|fi;5+qMR8I zJ!Aud0vRACLaKa54GQ4MLu-^|0m1|gSuBu+W0o3#B+Ak>@Db*0$WkcKPc@jxa%hYL zL-QCy#RSs`Dx|R`aIcY6gduK;f#1*tyr&qU8ii6@swg5h#W=(8tuS4N9-%(sEMqy2 z21;IqP~!|U2I^x)EEG$kh^1m$YwCtXh6RNAHUJPRz#i(50VNKTfqS41+l-}27#uoi z$-(2ySRZBqI<59lJ0MKK5s(}ew47r>q;kb)h*^!Tf&v4;mZA#O(9*{o2Ie4!;~)ba z(^Lr>7%K{d&*_()<(5bxq%VPQmc;_n9ppkK8$mAgMn1~~Olu}`QJ}xia$O>pikV!X zL8}e4dN4o(V1NNPEY_N$kYhtZ4+aAW4dUl#%u?>CfNtOkFCH&oOfxWz)k#DHz=93p z7_0{xN4zET`YE`{D4bYfinS)JT!9$?07k!IRA1gqh#l4rw7V1c-$4v zI}%?dyN^SMD<6qM%d;%ND(|<5rDEGX3o&Q=89O@ Date: Fri, 23 Jun 2023 11:54:24 +0200 Subject: [PATCH 03/16] feat: add order=unk test --- tests/trustless_gateway_car_test.go | 30 ++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/tests/trustless_gateway_car_test.go b/tests/trustless_gateway_car_test.go index 429e9baea..762c6a5ad 100644 --- a/tests/trustless_gateway_car_test.go +++ b/tests/trustless_gateway_car_test.go @@ -629,7 +629,7 @@ func TestTrustlessCarEntityBytes(t *testing.T) { RunWithSpecs(t, helpers.StandardCARTestTransforms(t, tests), specs.TrustlessGatewayCAR) } -func TestTrustlessCarDuplicates(t *testing.T) { +func TestTrustlessCarOrderAndDuplicates(t *testing.T) { dirWithDuplicateFiles := car.MustOpenUnixfsCar("t0118/dir-with-duplicate-files.car") tests := SugarTests{ @@ -696,6 +696,34 @@ func TestTrustlessCarDuplicates(t *testing.T) { InThatOrder(), ), }, + { + Name: "GET CAR with order=unk of UnixFS Directors", + Hint: ` + The response with order=unk MUST contain all the blocks required to construct + the requested CID. However, it can be in any order and duplicates MAY occur. + `, + Request: Request(). + Path("/ipfs/{{cid}}", dirWithDuplicateFiles.MustGetCid()). + Header("Accept", "application/vnd.ipld.car; version=1; order=unk"), + Response: Expect(). + Status(200). + Headers( + Header("Content-Type").Contains("application/vnd.ipld.car"), + Header("Content-Type").Contains("order="), + ). + Body( + IsCar(). + IgnoreRoots(). + HasBlocks(flattenStrings(t, + dirWithDuplicateFiles.MustGetCid(), + dirWithDuplicateFiles.MustGetCid("ascii.txt"), + dirWithDuplicateFiles.MustGetCid("ascii-copy.txt"), + dirWithDuplicateFiles.MustGetCid("hello.txt"), + dirWithDuplicateFiles.MustGetCid("multiblock.txt"), + dirWithDuplicateFiles.MustGetChildrenCids("multiblock.txt"), + )...), + ), + }, } // TODO: add sub-specification for these tests. From 31263ca4cd65dfa1f5a24f43f62ccc694422cb7e Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 29 Jun 2023 13:45:54 +0200 Subject: [PATCH 04/16] chore: apply suggestions and rebase --- tests/trustless_gateway_car_test.go | 42 +++++++++++++---------------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/tests/trustless_gateway_car_test.go b/tests/trustless_gateway_car_test.go index 762c6a5ad..4b2edc67d 100644 --- a/tests/trustless_gateway_car_test.go +++ b/tests/trustless_gateway_car_test.go @@ -630,7 +630,7 @@ func TestTrustlessCarEntityBytes(t *testing.T) { } func TestTrustlessCarOrderAndDuplicates(t *testing.T) { - dirWithDuplicateFiles := car.MustOpenUnixfsCar("t0118/dir-with-duplicate-files.car") + dirWithDuplicateFiles := car.MustOpenUnixfsCar("trustless_gateway_car/dir-with-duplicate-files.car") tests := SugarTests{ { @@ -653,14 +653,12 @@ func TestTrustlessCarOrderAndDuplicates(t *testing.T) { Body( IsCar(). IgnoreRoots(). - HasBlocks(flattenStrings(t, - dirWithDuplicateFiles.MustGetCid(), - dirWithDuplicateFiles.MustGetCid("ascii.txt"), // ascii.txt = ascii-copy.txt - dirWithDuplicateFiles.MustGetCid("ascii-copy.txt"), - dirWithDuplicateFiles.MustGetCid("hello.txt"), - dirWithDuplicateFiles.MustGetCid("multiblock.txt"), - dirWithDuplicateFiles.MustGetChildrenCids("multiblock.txt"), - )...). + HasBlock(dirWithDuplicateFiles.MustGetCid()). + HasBlock(dirWithDuplicateFiles.MustGetCid("ascii.txt")). // ascii.txt = ascii-copy.txt + HasBlock(dirWithDuplicateFiles.MustGetCid("ascii-copy.txt")). + HasBlock(dirWithDuplicateFiles.MustGetCid("hello.txt")). + HasBlock(dirWithDuplicateFiles.MustGetCid("multiblock.txt")). + HasBlocks(dirWithDuplicateFiles.MustGetChildrenCids("multiblock.txt")...). Exactly(). InThatOrder(), ), @@ -685,13 +683,11 @@ func TestTrustlessCarOrderAndDuplicates(t *testing.T) { Body( IsCar(). IgnoreRoots(). - HasBlocks(flattenStrings(t, - dirWithDuplicateFiles.MustGetCid(), - dirWithDuplicateFiles.MustGetCid("ascii.txt"), // ascii.txt = ascii-copy.txt - dirWithDuplicateFiles.MustGetCid("hello.txt"), - dirWithDuplicateFiles.MustGetCid("multiblock.txt"), - dirWithDuplicateFiles.MustGetChildrenCids("multiblock.txt"), - )...). + HasBlock(dirWithDuplicateFiles.MustGetCid()). + HasBlock(dirWithDuplicateFiles.MustGetCid("ascii.txt")). // ascii.txt = ascii-copy.txt + HasBlock(dirWithDuplicateFiles.MustGetCid("hello.txt")). + HasBlock(dirWithDuplicateFiles.MustGetCid("multiblock.txt")). + HasBlocks(dirWithDuplicateFiles.MustGetChildrenCids("multiblock.txt")...). Exactly(). InThatOrder(), ), @@ -714,14 +710,12 @@ func TestTrustlessCarOrderAndDuplicates(t *testing.T) { Body( IsCar(). IgnoreRoots(). - HasBlocks(flattenStrings(t, - dirWithDuplicateFiles.MustGetCid(), - dirWithDuplicateFiles.MustGetCid("ascii.txt"), - dirWithDuplicateFiles.MustGetCid("ascii-copy.txt"), - dirWithDuplicateFiles.MustGetCid("hello.txt"), - dirWithDuplicateFiles.MustGetCid("multiblock.txt"), - dirWithDuplicateFiles.MustGetChildrenCids("multiblock.txt"), - )...), + HasBlock(dirWithDuplicateFiles.MustGetCid()). + HasBlock(dirWithDuplicateFiles.MustGetCid("ascii.txt")). + HasBlock(dirWithDuplicateFiles.MustGetCid("ascii-copy.txt")). + HasBlock(dirWithDuplicateFiles.MustGetCid("hello.txt")). + HasBlock(dirWithDuplicateFiles.MustGetCid("multiblock.txt")). + HasBlocks(dirWithDuplicateFiles.MustGetChildrenCids("multiblock.txt")...), ), }, } From aa013ff67de1d09270db3502da90ee4fe8c69416 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Fri, 14 Jul 2023 20:48:21 +0200 Subject: [PATCH 05/16] docs: improve ipip-412 names and hints --- tests/trustless_gateway_car_test.go | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/tests/trustless_gateway_car_test.go b/tests/trustless_gateway_car_test.go index 4b2edc67d..a20001bb0 100644 --- a/tests/trustless_gateway_car_test.go +++ b/tests/trustless_gateway_car_test.go @@ -634,7 +634,7 @@ func TestTrustlessCarOrderAndDuplicates(t *testing.T) { tests := SugarTests{ { - Name: "GET CAR with dups=y of UnixFS Directory With Duplicate Files", + Name: "GET CAR with order=dfs and dups=y of UnixFS Directory With Duplicate Files", Hint: ` The response MUST contain all the blocks found during traversal even if they are duplicate. In this test, a directory that contains duplicate files is @@ -664,11 +664,11 @@ func TestTrustlessCarOrderAndDuplicates(t *testing.T) { ), }, { - Name: "GET CAR with dups=n of UnixFS Directory With Duplicate Files", + Name: "GET CAR with order=dfs and dups=n of UnixFS Directory With Duplicate Files", Hint: ` - The response MUST NOT contain duplicate blocks.In this test, a directory that - contains duplicate files is requested. The blocks corresponding to the duplicate - files must be returned only ONCE. + The response MUST NOT contain duplicate blocks. Tested + directory contains duplicate files. The blocks corresponding to + the duplicate files must be returned only ONCE. `, Request: Request(). Path("/ipfs/{{cid}}", dirWithDuplicateFiles.MustGetCid()). @@ -693,10 +693,17 @@ func TestTrustlessCarOrderAndDuplicates(t *testing.T) { ), }, { - Name: "GET CAR with order=unk of UnixFS Directors", + Name: "GET CAR smoke-test with order=unk of UnixFS Directory", Hint: ` - The response with order=unk MUST contain all the blocks required to construct - the requested CID. However, it can be in any order and duplicates MAY occur. + The order=unk is usually used by gateway to explicitly indicate + it does not guarantee any block order. In this case, we use it + for basic smoke-test to confirm support of IPIP-412. The + response for request with explicit order=unk MUST include an + explicit order in returned Content-Type and contain all the + blocks required to construct the requested CID. However, the + gateway is free to return default ordering of own choosing, + which means the returned blocks can be in any order and + duplicates MAY occur. `, Request: Request(). Path("/ipfs/{{cid}}", dirWithDuplicateFiles.MustGetCid()). From 3c772f8c435fdd153537af3a6bef89254b739ba2 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Fri, 14 Jul 2023 22:11:34 +0200 Subject: [PATCH 06/16] test: identity CID and CAR --- Makefile | 2 +- tests/trustless_gateway_car_test.go | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2f98ec76d..55300d517 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ provision-cargateway: ./fixtures.car car -c ./fixtures.car & provision-kubo: - find ./fixtures -name '*.car' -exec ipfs dag import {} \; + find ./fixtures -name '*.car' -exec ipfs dag import --stats --pin-roots=false {} \; find ./fixtures -name '*.ipns-record' -exec sh -c 'ipfs routing put --allow-offline /ipns/$$(basename -s .ipns-record "{}") "{}"' \; # tools diff --git a/tests/trustless_gateway_car_test.go b/tests/trustless_gateway_car_test.go index a20001bb0..dcb7a7a07 100644 --- a/tests/trustless_gateway_car_test.go +++ b/tests/trustless_gateway_car_test.go @@ -725,6 +725,29 @@ func TestTrustlessCarOrderAndDuplicates(t *testing.T) { HasBlocks(dirWithDuplicateFiles.MustGetChildrenCids("multiblock.txt")...), ), }, + { + Name: "GET CAR with order=dfs and dups=y of identity CID", + Hint: ` + Identity hashes MUST never be manifested as read blocks. + These are virtual ones and even when dups=y is set, they never + should be returned in CAR response body. + `, + Request: Request(). + Path("/ipfs/{{cid}}", "bafkqaf3imvwgy3zaneqgc3janfxgy2lomvscay3jmqfa"). + Header("Accept", "application/vnd.ipld.car; dups=y"), + Response: Expect(). + Status(200). + Headers( + Header("Content-Type").Contains("application/vnd.ipld.car"), + Header("Content-Type").Contains("dups=y"), + ). + Body( + IsCar(). + IgnoreRoots(). + Exactly(). + InThatOrder(), + ), + }, } // TODO: add sub-specification for these tests. From a34f42357fdfc5fd6bb8feb9d8d923d6ad7b3982 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Mon, 24 Jul 2023 11:09:57 +0200 Subject: [PATCH 07/16] Revert "ci: run against ipip-412 PR [REVERT BEFORE MERGING]" This reverts commit 7dde477a5e8f36f3767217644964ded9e6860f25. --- .github/workflows/test-kubo-e2e.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-kubo-e2e.yml b/.github/workflows/test-kubo-e2e.yml index abbccfbb8..424f24715 100644 --- a/.github/workflows/test-kubo-e2e.yml +++ b/.github/workflows/test-kubo-e2e.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - target: ['latest', 'master', 'ipip-412'] + target: ['latest', 'master'] defaults: run: shell: bash From 1225388506f58c158c1964dc9ae8ea9c52db0c49 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Mon, 24 Jul 2023 11:10:17 +0200 Subject: [PATCH 08/16] chore: bump boxo --- go.mod | 6 +++--- go.sum | 11 ++++++----- tooling/ipns/record.go | 31 +++++++++++++++++++++---------- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index e9b626a8c..e4a132b7b 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,11 @@ module github.com/ipfs/gateway-conformance go 1.20 require ( - github.com/ipfs/boxo v0.10.0 + github.com/ipfs/boxo v0.10.3-0.20230724084731-f6b448b4263a github.com/ipfs/go-cid v0.4.1 github.com/ipfs/go-unixfsnode v1.7.1 github.com/ipld/go-car v0.6.1 - github.com/ipld/go-car/v2 v2.9.1-0.20230325062757-fff0e4397a3d + github.com/ipld/go-car/v2 v2.10.2-0.20230622090957-499d0c909d33 github.com/ipld/go-codec-dagpb v1.6.0 github.com/ipld/go-ipld-prime v0.20.0 github.com/libp2p/go-libp2p v0.26.3 @@ -22,6 +22,7 @@ require ( github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.1 // indirect github.com/ipfs/go-blockservice v0.5.0 // indirect github.com/ipfs/go-ipfs-blockstore v1.3.0 // indirect github.com/ipfs/go-ipfs-ds-help v1.1.0 // indirect @@ -32,7 +33,6 @@ require ( github.com/libp2p/go-libp2p-record v0.2.0 // indirect github.com/multiformats/go-multiaddr v0.8.0 // indirect github.com/multiformats/go-multistream v0.4.1 // indirect - github.com/pkg/errors v0.9.1 // 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 diff --git a/go.sum b/go.sum index 06b031e57..2c2863426 100644 --- a/go.sum +++ b/go.sum @@ -43,11 +43,13 @@ github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfm github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnxuVTqZ6x4= +github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= 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.10.0 h1:tdDAxq8jrsbRkYoF+5Rcqyeb91hgWe2hp7iLu7ORZLY= -github.com/ipfs/boxo v0.10.0/go.mod h1:Fg+BnfxZ0RPzR0nOodzdIq3A7KgoWAOWsEIImrIQdBM= +github.com/ipfs/boxo v0.10.3-0.20230724084731-f6b448b4263a h1:eDO++KVTMF7wNqHHhNCZnW/Ocf2K6zCSizAbWoIMREo= +github.com/ipfs/boxo v0.10.3-0.20230724084731-f6b448b4263a/go.mod h1:8IfDmp+FzFGcF4zjAgHMVPpwYw4AjN9ePEzDfkaYJ1w= 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-bitswap v0.11.0 h1:j1WVvhDX1yhG32NTC9xfxnqycqYIlhzEzLXG/cU1HyQ= @@ -107,8 +109,8 @@ github.com/ipfs/go-verifcid v0.0.2 h1:XPnUv0XmdH+ZIhLGKg6U2vaPaRDXb9urMyNVCE7uvT github.com/ipfs/go-verifcid v0.0.2/go.mod h1:40cD9x1y4OWnFXbLNJYRe7MpNvWlMn3LZAG5Wb4xnPU= github.com/ipld/go-car v0.6.1 h1:blWbEHf1j62JMWFIqWE//YR0m7k5ZMw0AuUOU5hjrH8= github.com/ipld/go-car v0.6.1/go.mod h1:oEGXdwp6bmxJCZ+rARSkDliTeYnVzv3++eXajZ+Bmr8= -github.com/ipld/go-car/v2 v2.9.1-0.20230325062757-fff0e4397a3d h1:22g+x1tgWSXK34i25qjs+afr7basaneEkHaglBshd2g= -github.com/ipld/go-car/v2 v2.9.1-0.20230325062757-fff0e4397a3d/go.mod h1:SH2pi/NgfGBsV/CGBAQPxMfghIgwzbh5lQ2N+6dNRI8= +github.com/ipld/go-car/v2 v2.10.2-0.20230622090957-499d0c909d33 h1:0OZwzSYWIuiKEOXd/2vm5cMcEmmGLFn+1h6lHELCm3s= +github.com/ipld/go-car/v2 v2.10.2-0.20230622090957-499d0c909d33/go.mod h1:sQEkXVM3csejlb1kCCb+vQ/pWBKX9QtvsrysMQjOgOg= github.com/ipld/go-codec-dagpb v1.6.0 h1:9nYazfyu9B1p3NAgfVdpRco3Fs2nFC72DqVsMj6rOcc= github.com/ipld/go-codec-dagpb v1.6.0/go.mod h1:ANzFhfP2uMJxRBr8CE+WQWs5UsNa0pYtmKZ+agnUw9s= github.com/ipld/go-ipld-prime v0.20.0 h1:Ud3VwE9ClxpO2LkCYP7vWPc0Fo+dYdYzgxUJZ3uRG4g= @@ -193,7 +195,6 @@ github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 h1:1/WtZae0yGtPq+TI6+ github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9/go.mod h1:x3N5drFsm2uilKKuuYo6LdyD8vZAW55sH/9w+pbo1sw= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polydawn/refmt v0.0.0-20190221155625-df39d6c2d992/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= diff --git a/tooling/ipns/record.go b/tooling/ipns/record.go index 31ed5f059..0946f6bc9 100644 --- a/tooling/ipns/record.go +++ b/tooling/ipns/record.go @@ -5,7 +5,6 @@ import ( "time" "github.com/ipfs/boxo/ipns" - ipns_pb "github.com/ipfs/boxo/ipns/pb" "github.com/ipfs/go-cid" "github.com/libp2p/go-libp2p/core/peer" mbase "github.com/multiformats/go-multibase" @@ -13,19 +12,25 @@ import ( ) type IpnsRecord struct { - pb *ipns_pb.IpnsEntry + rec *ipns.Record key string - id peer.ID + value string + name ipns.Name validity time.Time } func UnmarshalIpnsRecord(data []byte, pubKey string) (*IpnsRecord, error) { - pb, err := ipns.UnmarshalIpnsEntry(data) + pb, err := ipns.UnmarshalRecord(data) if err != nil { return nil, err } - validity, err := ipns.GetEOL(pb) + validity, err := pb.Validity() + if err != nil { + return nil, err + } + + value, err := pb.Value() if err != nil { return nil, err } @@ -35,11 +40,17 @@ func UnmarshalIpnsRecord(data []byte, pubKey string) (*IpnsRecord, error) { return nil, err } - return &IpnsRecord{pb: pb, key: pubKey, id: id, validity: validity}, nil + return &IpnsRecord{ + rec: pb, + key: pubKey, + name: ipns.NameFromPeer(id), + validity: validity, + value: value.String(), + }, nil } func (i *IpnsRecord) Value() string { - return string(i.pb.Value) + return i.value } func (i *IpnsRecord) Key() string { @@ -51,11 +62,11 @@ func (i *IpnsRecord) Validity() time.Time { } func (i *IpnsRecord) Valid() error { - return ipns.ValidateWithPeerID(i.id, i.pb) + return ipns.ValidateWithName(i.rec, i.name) } func (i *IpnsRecord) idV1(codec multicodec.Code, base mbase.Encoding) (string, error) { - c := peer.ToCid(i.id) + c := i.name.Cid() c = cid.NewCidV1(uint64(codec), c.Hash()) s, err := c.StringOfBase(base) if err != nil { @@ -85,5 +96,5 @@ func (i *IpnsRecord) IdV1() string { } func (i *IpnsRecord) B58MH() string { - return i.id.String() + return i.name.Peer().String() } From 097a5fd9c8b1fe42708932ec0f70867f3df839ef Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Mon, 24 Jul 2023 11:30:48 +0200 Subject: [PATCH 09/16] refactor: hardcode children CIDs with DFS order --- tests/trustless_gateway_car_test.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/trustless_gateway_car_test.go b/tests/trustless_gateway_car_test.go index dcb7a7a07..33f8cc8fb 100644 --- a/tests/trustless_gateway_car_test.go +++ b/tests/trustless_gateway_car_test.go @@ -398,7 +398,7 @@ func TestTrustlessCarEntityBytes(t *testing.T) { IgnoreRoots(). HasBlocks( missingBlockFixture.MustGetCid(), - missingBlockFixture.MustGetChildrenCids()[0], + "QmPKt7ptM2ZYSGPUc8PmPT2VBkLDK3iqpG9TBJY7PCE9rF", ). Exactly(), ), @@ -423,7 +423,7 @@ func TestTrustlessCarEntityBytes(t *testing.T) { IgnoreRoots(). HasBlocks( missingBlockFixture.MustGetCid(), - missingBlockFixture.MustGetChildrenCids()[2], + "QmWXY482zQdwecnfBsj78poUUuPXvyw2JAFAEMw4tzTavV", ). Exactly(), ), @@ -631,6 +631,13 @@ func TestTrustlessCarEntityBytes(t *testing.T) { func TestTrustlessCarOrderAndDuplicates(t *testing.T) { dirWithDuplicateFiles := car.MustOpenUnixfsCar("trustless_gateway_car/dir-with-duplicate-files.car") + multiblockCIDs := []string{ + "bafkreie5noke3mb7hqxukzcy73nl23k6lxszxi5w3dtmuwz62wnvkpsscm", + "bafkreih4ephajybraj6wnxsbwjwa77fukurtpl7oj7t7pfq545duhot7cq", + "bafkreigu7buvm3cfunb35766dn7tmqyh2um62zcio63en2btvxuybgcpue", + "bafkreicll3huefkc3qnrzeony7zcfo7cr3nbx64hnxrqzsixpceg332fhe", + "bafkreifst3pqztuvj57lycamoi7z34b4emf7gawxs74nwrc2c7jncmpaqm", + } tests := SugarTests{ { @@ -658,7 +665,7 @@ func TestTrustlessCarOrderAndDuplicates(t *testing.T) { HasBlock(dirWithDuplicateFiles.MustGetCid("ascii-copy.txt")). HasBlock(dirWithDuplicateFiles.MustGetCid("hello.txt")). HasBlock(dirWithDuplicateFiles.MustGetCid("multiblock.txt")). - HasBlocks(dirWithDuplicateFiles.MustGetChildrenCids("multiblock.txt")...). + HasBlocks(multiblockCIDs...). Exactly(). InThatOrder(), ), @@ -687,7 +694,7 @@ func TestTrustlessCarOrderAndDuplicates(t *testing.T) { HasBlock(dirWithDuplicateFiles.MustGetCid("ascii.txt")). // ascii.txt = ascii-copy.txt HasBlock(dirWithDuplicateFiles.MustGetCid("hello.txt")). HasBlock(dirWithDuplicateFiles.MustGetCid("multiblock.txt")). - HasBlocks(dirWithDuplicateFiles.MustGetChildrenCids("multiblock.txt")...). + HasBlocks(multiblockCIDs...). Exactly(). InThatOrder(), ), @@ -722,7 +729,7 @@ func TestTrustlessCarOrderAndDuplicates(t *testing.T) { HasBlock(dirWithDuplicateFiles.MustGetCid("ascii-copy.txt")). HasBlock(dirWithDuplicateFiles.MustGetCid("hello.txt")). HasBlock(dirWithDuplicateFiles.MustGetCid("multiblock.txt")). - HasBlocks(dirWithDuplicateFiles.MustGetChildrenCids("multiblock.txt")...), + HasBlocks(multiblockCIDs...), ), }, { From e0082d4e1a671e29787685d317ce524124bdde05 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Mon, 24 Jul 2023 11:37:17 +0200 Subject: [PATCH 10/16] feat: add test with both accept header and format query --- tests/trustless_gateway_car_test.go | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/trustless_gateway_car_test.go b/tests/trustless_gateway_car_test.go index 33f8cc8fb..8abc52b24 100644 --- a/tests/trustless_gateway_car_test.go +++ b/tests/trustless_gateway_car_test.go @@ -755,6 +755,37 @@ func TestTrustlessCarOrderAndDuplicates(t *testing.T) { InThatOrder(), ), }, + { + Name: "GET CAR with Accept and ?format, specific Accept header is prioritized", + Hint: ` + The response MUST contain all the blocks found during traversal even if they + are duplicate. In this test, a directory that contains duplicate files is + requested. The blocks corresponding to the duplicate files must be returned. + `, + Request: Request(). + Path("/ipfs/{{cid}}", dirWithDuplicateFiles.MustGetCid()). + Query("format", "car"). + Header("Accept", "application/vnd.ipld.car; version=1; order=dfs; dups=y"), + Response: Expect(). + Status(200). + Headers( + Header("Content-Type").Contains("application/vnd.ipld.car"), + Header("Content-Type").Contains("order=dfs"), + Header("Content-Type").Contains("dups=y"), + ). + Body( + IsCar(). + IgnoreRoots(). + HasBlock(dirWithDuplicateFiles.MustGetCid()). + HasBlock(dirWithDuplicateFiles.MustGetCid("ascii.txt")). // ascii.txt = ascii-copy.txt + HasBlock(dirWithDuplicateFiles.MustGetCid("ascii-copy.txt")). + HasBlock(dirWithDuplicateFiles.MustGetCid("hello.txt")). + HasBlock(dirWithDuplicateFiles.MustGetCid("multiblock.txt")). + HasBlocks(multiblockCIDs...). + Exactly(). + InThatOrder(), + ), + }, } // TODO: add sub-specification for these tests. From febe878c7526fc911835eb6a48d6601c8ffe5358 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Mon, 24 Jul 2023 13:15:43 +0200 Subject: [PATCH 11/16] docs: add recipe for dir-with-duplicate-files.car --- fixtures/trustless_gateway_car/README.md | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/fixtures/trustless_gateway_car/README.md b/fixtures/trustless_gateway_car/README.md index f60da5f32..f279d2f02 100644 --- a/fixtures/trustless_gateway_car/README.md +++ b/fixtures/trustless_gateway_car/README.md @@ -17,3 +17,29 @@ REMOVE_BLOCK=$(ipfs dag get $CID | jq '.Links[1].Hash["/"]' -r) echo $REMOVE_BLOCK | car filter --version 1 --inverse ./file-3k-and-3-blocks.car ./file-3k-and-3-blocks-missing-block.car ipfs pin rm $CID; ipfs repo gc ``` + +### [dir-with-duplicate-files.car](./dir-with-duplicate-files.car) + +```sh +ipfs version +# ipfs version 0.21.0 +TEXT=$(cat <<-EOF +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non imperdiet nunc. Proin ac quam ut nibh eleifend aliquet. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Sed ligula dolor, imperdiet sagittis arcu et, semper tincidunt urna. Donec et tempor augue, quis sollicitudin metus. Curabitur semper ullamcorper aliquet. Mauris hendrerit sodales lectus eget fermentum. Proin sollicitudin vestibulum commodo. Vivamus nec lectus eu augue aliquet dignissim nec condimentum justo. In hac habitasse platea dictumst. Mauris vel sem neque. + +Vivamus finibus, enim at lacinia semper, arcu erat gravida lacus, sit amet gravida magna orci sit amet est. Sed non leo lacus. Nullam viverra ipsum a tincidunt dapibus. Nulla pulvinar ligula sit amet ante ultrices tempus. Proin purus urna, semper sed lobortis quis, gravida vitae ipsum. Aliquam mi urna, pulvinar eu bibendum quis, convallis ac dolor. In gravida justo sed risus ullamcorper, vitae luctus massa hendrerit. Pellentesque habitant amet. +EOF +) + +ASCII_CID=$(echo "hello application/vnd.ipld.car" | ipfs add --cid-version=1 -q) +HELLO_CID=$(echo "hello world" | ipfs add --cid-version=1 -q) +MULTIBLOCK_CID=$(echo -n $TEXT | ipfs add --cid-version=1 --chunker=size-256 -q) + +ipfs files mkdir -p --cid-version 1 /dir-with-duplicate-files +ipfs files cp /ipfs/$ASCII_CID /dir-with-duplicate-files/ascii-copy.txt +ipfs files cp /ipfs/$ASCII_CID /dir-with-duplicate-files/ascii.txt +ipfs files cp /ipfs/$HELLO_CID /dir-with-duplicate-files/hello.txt +ipfs files cp /ipfs/$MULTIBLOCK_CID /dir-with-duplicate-files/multiblock.txt +ipfs files ls -l +# Manually CID of "dir-with-duplicate-files" and then... +ipfs dag export $CID +``` From 6d06b7f35dacdcae9ef55a723a7956625f9bf045 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Mon, 24 Jul 2023 13:25:13 +0200 Subject: [PATCH 12/16] feat: add TrustlessGatewayCAROptional --- tests/trustless_gateway_car_test.go | 3 +-- tooling/specs/specs.go | 32 +++++++++++++++-------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/tests/trustless_gateway_car_test.go b/tests/trustless_gateway_car_test.go index 8abc52b24..6d217eb26 100644 --- a/tests/trustless_gateway_car_test.go +++ b/tests/trustless_gateway_car_test.go @@ -788,8 +788,7 @@ func TestTrustlessCarOrderAndDuplicates(t *testing.T) { }, } - // TODO: add sub-specification for these tests. - RunWithSpecs(t, tests, specs.TrustlessGatewayCAR) + RunWithSpecs(t, tests, specs.TrustlessGatewayCAROptional) } func flattenStrings(t *testing.T, values ...interface{}) []string { diff --git a/tooling/specs/specs.go b/tooling/specs/specs.go index 9773d1904..af4d136e8 100644 --- a/tooling/specs/specs.go +++ b/tooling/specs/specs.go @@ -105,27 +105,29 @@ func (c Collection) Disable() { } var ( - TrustlessGatewayRaw = Leaf{"trustless-block-gateway", stable} - TrustlessGatewayCAR = Leaf{"trustless-car-gateway", stable} - TrustlessGatewayIPNS = Leaf{"trustless-ipns-gateway", stable} - TrustlessGateway = Collection{"trustless-gateway", []Spec{TrustlessGatewayRaw, TrustlessGatewayCAR, TrustlessGatewayIPNS}} - PathGatewayUnixFS = Leaf{"path-unixfs-gateway", stable} - PathGatewayIPNS = Leaf{"path-ipns-gateway", stable} - PathGatewayTAR = Leaf{"path-tar-gateway", stable} - PathGatewayDAG = Leaf{"path-dag-gateway", stable} - PathGatewayRaw = Leaf{"path-raw-gateway", stable} - PathGateway = Collection{"path-gateway", []Spec{PathGatewayUnixFS, PathGatewayIPNS, PathGatewayTAR, PathGatewayDAG, PathGatewayRaw}} - SubdomainGatewayIPFS = Leaf{"subdomain-ipfs-gateway", stable} - SubdomainGatewayIPNS = Leaf{"subdomain-ipns-gateway", stable} - SubdomainGateway = Collection{"subdomain-gateway", []Spec{SubdomainGatewayIPFS, SubdomainGatewayIPNS}} - DNSLinkGateway = Leaf{"dnslink-gateway", stable} - RedirectsFile = Leaf{"redirects-file", stable} + TrustlessGatewayRaw = Leaf{"trustless-block-gateway", stable} + TrustlessGatewayCAR = Leaf{"trustless-car-gateway", stable} + TrustlessGatewayCAROptional = Leaf{"trustless-car-gateway-optional", stable} + TrustlessGatewayIPNS = Leaf{"trustless-ipns-gateway", stable} + TrustlessGateway = Collection{"trustless-gateway", []Spec{TrustlessGatewayRaw, TrustlessGatewayCAR, TrustlessGatewayCAROptional, TrustlessGatewayIPNS}} + PathGatewayUnixFS = Leaf{"path-unixfs-gateway", stable} + PathGatewayIPNS = Leaf{"path-ipns-gateway", stable} + PathGatewayTAR = Leaf{"path-tar-gateway", stable} + PathGatewayDAG = Leaf{"path-dag-gateway", stable} + PathGatewayRaw = Leaf{"path-raw-gateway", stable} + PathGateway = Collection{"path-gateway", []Spec{PathGatewayUnixFS, PathGatewayIPNS, PathGatewayTAR, PathGatewayDAG, PathGatewayRaw}} + SubdomainGatewayIPFS = Leaf{"subdomain-ipfs-gateway", stable} + SubdomainGatewayIPNS = Leaf{"subdomain-ipns-gateway", stable} + SubdomainGateway = Collection{"subdomain-gateway", []Spec{SubdomainGatewayIPFS, SubdomainGatewayIPNS}} + DNSLinkGateway = Leaf{"dnslink-gateway", stable} + RedirectsFile = Leaf{"redirects-file", stable} ) // All specs MUST be listed here. var specs = []Spec{ TrustlessGatewayRaw, TrustlessGatewayCAR, + TrustlessGatewayCAROptional, TrustlessGatewayIPNS, TrustlessGateway, PathGatewayUnixFS, From 9692d8a519ec6d2494bd163f463a9f159a0c0fac Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 26 Jul 2023 09:41:55 +0200 Subject: [PATCH 13/16] docs: add printing the multiblock CIDs --- fixtures/trustless_gateway_car/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fixtures/trustless_gateway_car/README.md b/fixtures/trustless_gateway_car/README.md index f279d2f02..07d0b41cb 100644 --- a/fixtures/trustless_gateway_car/README.md +++ b/fixtures/trustless_gateway_car/README.md @@ -33,7 +33,8 @@ EOF ASCII_CID=$(echo "hello application/vnd.ipld.car" | ipfs add --cid-version=1 -q) HELLO_CID=$(echo "hello world" | ipfs add --cid-version=1 -q) MULTIBLOCK_CID=$(echo -n $TEXT | ipfs add --cid-version=1 --chunker=size-256 -q) - +# Print the Multiblock CIDs (required for some tests) +ipfs dag get $MULTIBLOCK_CID | jq .Links | jq -r '.[].Hash."/"' ipfs files mkdir -p --cid-version 1 /dir-with-duplicate-files ipfs files cp /ipfs/$ASCII_CID /dir-with-duplicate-files/ascii-copy.txt ipfs files cp /ipfs/$ASCII_CID /dir-with-duplicate-files/ascii.txt From 56f8839a37f2e4e1a6ddfa56a40a52136d6abdf3 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 26 Jul 2023 09:52:21 +0200 Subject: [PATCH 14/16] docs: add printing of remaining cids --- fixtures/trustless_gateway_car/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fixtures/trustless_gateway_car/README.md b/fixtures/trustless_gateway_car/README.md index 07d0b41cb..f8f8f813c 100644 --- a/fixtures/trustless_gateway_car/README.md +++ b/fixtures/trustless_gateway_car/README.md @@ -16,6 +16,8 @@ ipfs dag export $CID > file-3k-and-3-blocks.car REMOVE_BLOCK=$(ipfs dag get $CID | jq '.Links[1].Hash["/"]' -r) echo $REMOVE_BLOCK | car filter --version 1 --inverse ./file-3k-and-3-blocks.car ./file-3k-and-3-blocks-missing-block.car ipfs pin rm $CID; ipfs repo gc +# First and third outputted CIDs are used in the missing blocks tests. +ipfs dag get $CID | jq .Links | jq -r '.[].Hash."/"' ``` ### [dir-with-duplicate-files.car](./dir-with-duplicate-files.car) From 5e5b08f0ef0e5aeda2b92695621e89e2c90f8232 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 26 Jul 2023 09:58:55 +0200 Subject: [PATCH 15/16] Apply suggestions from code review Co-authored-by: Laurent Senta --- tests/trustless_gateway_car_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/trustless_gateway_car_test.go b/tests/trustless_gateway_car_test.go index 6d217eb26..34f42acf8 100644 --- a/tests/trustless_gateway_car_test.go +++ b/tests/trustless_gateway_car_test.go @@ -398,6 +398,8 @@ func TestTrustlessCarEntityBytes(t *testing.T) { IgnoreRoots(). HasBlocks( missingBlockFixture.MustGetCid(), + // This CID is defined at the SPEC level + // See the recipe for `file-3k-and-3-blocks-missing-block.car` "QmPKt7ptM2ZYSGPUc8PmPT2VBkLDK3iqpG9TBJY7PCE9rF", ). Exactly(), @@ -423,6 +425,8 @@ func TestTrustlessCarEntityBytes(t *testing.T) { IgnoreRoots(). HasBlocks( missingBlockFixture.MustGetCid(), + // This CID is defined at the SPEC level + // See the recipe for `file-3k-and-3-blocks-missing-block.car` "QmWXY482zQdwecnfBsj78poUUuPXvyw2JAFAEMw4tzTavV", ). Exactly(), @@ -631,6 +635,8 @@ func TestTrustlessCarEntityBytes(t *testing.T) { func TestTrustlessCarOrderAndDuplicates(t *testing.T) { dirWithDuplicateFiles := car.MustOpenUnixfsCar("trustless_gateway_car/dir-with-duplicate-files.car") + // This array is defined at the SPEC level and should not depend on library behavior + // See the recipe for `dir-with-duplicate-files.car` multiblockCIDs := []string{ "bafkreie5noke3mb7hqxukzcy73nl23k6lxszxi5w3dtmuwz62wnvkpsscm", "bafkreih4ephajybraj6wnxsbwjwa77fukurtpl7oj7t7pfq545duhot7cq", From 8b2c2c7b23338ba42261b8fa443b234e6cf94170 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 26 Jul 2023 10:26:53 +0200 Subject: [PATCH 16/16] fix: go formatting --- tests/trustless_gateway_car_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/trustless_gateway_car_test.go b/tests/trustless_gateway_car_test.go index 34f42acf8..a81bb772e 100644 --- a/tests/trustless_gateway_car_test.go +++ b/tests/trustless_gateway_car_test.go @@ -398,8 +398,8 @@ func TestTrustlessCarEntityBytes(t *testing.T) { IgnoreRoots(). HasBlocks( missingBlockFixture.MustGetCid(), - // This CID is defined at the SPEC level - // See the recipe for `file-3k-and-3-blocks-missing-block.car` + // This CID is defined at the SPEC level + // See the recipe for `file-3k-and-3-blocks-missing-block.car` "QmPKt7ptM2ZYSGPUc8PmPT2VBkLDK3iqpG9TBJY7PCE9rF", ). Exactly(), @@ -425,8 +425,8 @@ func TestTrustlessCarEntityBytes(t *testing.T) { IgnoreRoots(). HasBlocks( missingBlockFixture.MustGetCid(), - // This CID is defined at the SPEC level - // See the recipe for `file-3k-and-3-blocks-missing-block.car` + // This CID is defined at the SPEC level + // See the recipe for `file-3k-and-3-blocks-missing-block.car` "QmWXY482zQdwecnfBsj78poUUuPXvyw2JAFAEMw4tzTavV", ). Exactly(), @@ -635,8 +635,8 @@ func TestTrustlessCarEntityBytes(t *testing.T) { func TestTrustlessCarOrderAndDuplicates(t *testing.T) { dirWithDuplicateFiles := car.MustOpenUnixfsCar("trustless_gateway_car/dir-with-duplicate-files.car") - // This array is defined at the SPEC level and should not depend on library behavior - // See the recipe for `dir-with-duplicate-files.car` + // This array is defined at the SPEC level and should not depend on library behavior + // See the recipe for `dir-with-duplicate-files.car` multiblockCIDs := []string{ "bafkreie5noke3mb7hqxukzcy73nl23k6lxszxi5w3dtmuwz62wnvkpsscm", "bafkreih4ephajybraj6wnxsbwjwa77fukurtpl7oj7t7pfq545duhot7cq",