106 lines
2.8 KiB
Go
106 lines
2.8 KiB
Go
package sharding
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/ipfs-cluster/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
|
|
}
|