ipfs-cluster/adder/sharding/verify.go
Hector Sanjuan a97ed10d0b Adopt api.Cid type - replaces cid.Cid everwhere.
This commit introduces an api.Cid type and replaces the usage of cid.Cid
everywhere.

The main motivation here is to override MarshalJSON so that Cids are
JSON-ified as '"Qm...."' instead of '{ "/": "Qm....." }', as this "ipld"
representation of IDs is horrible to work with, and our APIs are not issuing
IPLD objects to start with.

Unfortunately, there is no way to do this cleanly, and the best way is to just
switch everything to our own type.
2022-04-07 14:27:39 +02:00

107 lines
2.8 KiB
Go

package sharding
import (
"context"
"errors"
"fmt"
"testing"
"github.com/ipfs/ipfs-cluster/api"
)
// MockPinStore is used in VerifyShards
type MockPinStore interface {
// Gets a pin
PinGet(context.Context, api.Cid) (api.Pin, error)
}
// MockBlockStore is used in VerifyShards
type MockBlockStore interface {
// Gets a block
BlockGet(context.Context, api.Cid) ([]byte, error)
}
// VerifyShards checks that a sharded CID has been correctly formed and stored.
// This is a helper function for testing. It returns a map with all the blocks
// from all shards.
func VerifyShards(t *testing.T, rootCid api.Cid, pins MockPinStore, ipfs MockBlockStore, expectedShards int) (map[string]struct{}, error) {
ctx := context.Background()
metaPin, err := pins.PinGet(ctx, rootCid)
if err != nil {
return nil, fmt.Errorf("meta pin was not pinned: %s", err)
}
if api.PinType(metaPin.Type) != api.MetaType {
return nil, fmt.Errorf("bad MetaPin type")
}
if metaPin.Reference == nil {
return nil, errors.New("metaPin.Reference is unset")
}
clusterPin, err := pins.PinGet(ctx, *metaPin.Reference)
if err != nil {
return nil, fmt.Errorf("cluster pin was not pinned: %s", err)
}
if api.PinType(clusterPin.Type) != api.ClusterDAGType {
return nil, fmt.Errorf("bad ClusterDAGPin type")
}
if !clusterPin.Reference.Equals(metaPin.Cid) {
return nil, fmt.Errorf("clusterDAG should reference the MetaPin")
}
clusterDAGBlock, err := ipfs.BlockGet(ctx, clusterPin.Cid)
if err != nil {
return nil, fmt.Errorf("cluster pin was not stored: %s", err)
}
clusterDAGNode, err := CborDataToNode(clusterDAGBlock, "cbor")
if err != nil {
return nil, err
}
shards := clusterDAGNode.Links()
if len(shards) != expectedShards {
return nil, fmt.Errorf("bad number of shards")
}
shardBlocks := make(map[string]struct{})
var ref api.Cid
// traverse shards in order
for i := 0; i < len(shards); i++ {
sh, _, err := clusterDAGNode.ResolveLink([]string{fmt.Sprintf("%d", i)})
if err != nil {
return nil, err
}
shardPin, err := pins.PinGet(ctx, api.NewCid(sh.Cid))
if err != nil {
return nil, fmt.Errorf("shard was not pinned: %s %s", sh.Cid, err)
}
if ref != api.CidUndef && !shardPin.Reference.Equals(ref) {
t.Errorf("Ref (%s) should point to previous shard (%s)", ref, shardPin.Reference)
}
ref = shardPin.Cid
shardBlock, err := ipfs.BlockGet(ctx, shardPin.Cid)
if err != nil {
return nil, fmt.Errorf("shard block was not stored: %s", err)
}
shardNode, err := CborDataToNode(shardBlock, "cbor")
if err != nil {
return nil, err
}
for _, l := range shardNode.Links() {
ci := l.Cid.String()
_, ok := shardBlocks[ci]
if ok {
return nil, fmt.Errorf("block belongs to two shards: %s", ci)
}
shardBlocks[ci] = struct{}{}
}
}
return shardBlocks, nil
}