Skip to content
This repository has been archived by the owner on Jun 19, 2023. It is now read-only.

refactor: BlockPutSettings.CidPrefix #80

Merged
merged 7 commits into from Apr 21, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Expand Up @@ -11,6 +11,7 @@ require (
github.com/libp2p/go-libp2p-core v0.8.5
github.com/multiformats/go-multiaddr v0.3.3
github.com/multiformats/go-multibase v0.0.3
github.com/multiformats/go-multicodec v0.3.0
github.com/multiformats/go-multihash v0.0.15
)

Expand Down
2 changes: 2 additions & 0 deletions go.sum
Expand Up @@ -713,6 +713,8 @@ github.com/multiformats/go-multiaddr-net v0.2.0/go.mod h1:gGdH3UXny6U3cKKYCvpXI5
github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs=
github.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk=
github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc=
github.com/multiformats/go-multicodec v0.3.0 h1:tstDwfIjiHbnIjeM5Lp+pMrSeN+LCMsEwOrkPmWm03A=
github.com/multiformats/go-multicodec v0.3.0/go.mod h1:qGGaQmioCDh+TeFOnxrbU0DaIPw8yFgAZgFG0V7p1qQ=
github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=
github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po=
github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
Expand Down
134 changes: 86 additions & 48 deletions options/block.go
Expand Up @@ -2,15 +2,15 @@ package options

import (
"fmt"

cid "github.com/ipfs/go-cid"
mc "github.com/multiformats/go-multicodec"
mh "github.com/multiformats/go-multihash"
)

type BlockPutSettings struct {
Codec string
MhType uint64
MhLength int
Pin bool
CidPrefix cid.Prefix
Pin bool
}
Comment on lines 11 to 14
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ this is the core change in this PR – codec/mh were present twice, this folds them into cid.Prefix that is then used in go-ipfs in https://github.com/ipfs/go-ipfs/pull/8568/files#diff-0c55f05d877cead8d5b53c5dea1912d738456bc774332cd9406efef74e224afcR44


type BlockRmSettings struct {
Expand All @@ -20,53 +20,29 @@ type BlockRmSettings struct {
type BlockPutOption func(*BlockPutSettings) error
type BlockRmOption func(*BlockRmSettings) error

func BlockPutOptions(opts ...BlockPutOption) (*BlockPutSettings, cid.Prefix, error) {
func BlockPutOptions(opts ...BlockPutOption) (*BlockPutSettings, error) {
var cidPrefix cid.Prefix

// Baseline is CIDv1 raw sha2-255-32 (can be tweaked later via opts)
cidPrefix.Version = 1
cidPrefix.Codec = uint64(mc.Raw)
cidPrefix.MhType = mh.SHA2_256
cidPrefix.MhLength = -1 // -1 means len is to be calculated during mh.Sum()

options := &BlockPutSettings{
Codec: "",
MhType: mh.SHA2_256,
MhLength: -1,
Pin: false,
CidPrefix: cidPrefix,
Pin: false,
}

// Apply any overrides
for _, opt := range opts {
err := opt(options)
if err != nil {
return nil, cid.Prefix{}, err
}
}

var pref cid.Prefix
pref.Version = 1

if options.Codec == "" {
if options.MhType != mh.SHA2_256 || (options.MhLength != -1 && options.MhLength != 32) {
options.Codec = "protobuf"
} else {
options.Codec = "v0"
}
}

if options.Codec == "v0" && options.MhType == mh.SHA2_256 {
pref.Version = 0
}

formatval, ok := cid.Codecs[options.Codec]
if !ok {
return nil, cid.Prefix{}, fmt.Errorf("unrecognized format: %s", options.Codec)
}

if options.Codec == "v0" {
if options.MhType != mh.SHA2_256 || (options.MhLength != -1 && options.MhLength != 32) {
return nil, cid.Prefix{}, fmt.Errorf("only sha2-255-32 is allowed with CIDv0")
return nil, err
}
}

pref.Codec = formatval

pref.MhType = options.MhType
pref.MhLength = options.MhLength

return options, pref, nil
return options, nil
}

func BlockRmOptions(opts ...BlockRmOption) (*BlockRmSettings, error) {
Expand All @@ -87,22 +63,84 @@ type blockOpts struct{}

var Block blockOpts

// Format is an option for Block.Put which specifies the multicodec to use to
// serialize the object. Default is "v0"
func (blockOpts) Format(codec string) BlockPutOption {
// CidCodec is the modern option for Block.Put which specifies the multicodec to use
// in the CID returned by the Block.Put operation.
// It uses correct codes from go-multicodec and replaces the old Format now with CIDv1 as the default.
func (blockOpts) CidCodec(codecName string) BlockPutOption {
return func(settings *BlockPutSettings) error {
if codecName == "" {
return nil
}
code, err := codeFromName(codecName)
if err != nil {
return err
}
settings.CidPrefix.Codec = uint64(code)
return nil
}
}

// Map string to code from go-multicodec
func codeFromName(codecName string) (mc.Code, error) {
var cidCodec mc.Code
err := cidCodec.Set(codecName)
return cidCodec, err
}

// Format is a legacy option for Block.Put which specifies the multicodec to
// use to serialize the object.
// Provided for backward-compatibility only. Use CidCodec instead.
func (blockOpts) Format(format string) BlockPutOption {
return func(settings *BlockPutSettings) error {
settings.Codec = codec
if format == "" {
return nil
}
// Opt-in CIDv0 support for backward-compatibility
if format == "v0" {
settings.CidPrefix.Version = 0
}

// Fixup a legacy (invalid) names for dag-pb (0x70)
if format == "v0" || format == "protobuf" {
format = "dag-pb"
}

// Fixup invalid name for dag-cbor (0x71)
if format == "cbor" {
format = "dag-cbor"
}
Comment on lines +98 to +111
Copy link
Member

@lidel lidel Apr 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ this maintains backward-compatibility for existing users of ipfs block put --format – see sharness+CI tests in ipfs/kubo#8568


// Set code based on name passed as "format"
code, err := codeFromName(format)
if err != nil {
return err
}
settings.CidPrefix.Codec = uint64(code)

// If CIDv0, ensure all parameters are compatible
// (in theory go-cid would validate this anyway, but we want to provide better errors)
pref := settings.CidPrefix
if pref.Version == 0 {
if pref.Codec != uint64(mc.DagPb) {
return fmt.Errorf("only dag-pb is allowed with CIDv0")
}
if pref.MhType != mh.SHA2_256 || (pref.MhLength != -1 && pref.MhLength != 32) {
return fmt.Errorf("only sha2-255-32 is allowed with CIDv0")
}
}

return nil
}

}

// Hash is an option for Block.Put which specifies the multihash settings to use
// when hashing the object. Default is mh.SHA2_256 (0x12).
// If mhLen is set to -1, default length for the hash will be used
func (blockOpts) Hash(mhType uint64, mhLen int) BlockPutOption {
return func(settings *BlockPutSettings) error {
settings.MhType = mhType
settings.MhLength = mhLen
settings.CidPrefix.MhType = mhType
settings.CidPrefix.MhLength = mhLen
return nil
}
}
Expand Down
103 changes: 95 additions & 8 deletions tests/block.go
Expand Up @@ -17,15 +17,19 @@ import (
)

var (
pbCid = "QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN"
cborCid = "bafyreicnga62zhxnmnlt6ymq5hcbsg7gdhqdu6z4ehu3wpjhvqnflfy6nm"
cborKCid = "bafyr2qgsohbwdlk7ajmmbb4lhoytmest4wdbe5xnexfvtxeatuyqqmwv3fgxp3pmhpc27gwey2cct56gloqefoqwcf3yqiqzsaqb7p4jefhcw"
pbCidV0 = "QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN" // dag-pb
pbCid = "bafybeiffndsajwhk3lwjewwdxqntmjm4b5wxaaanokonsggenkbw6slwk4" // dag-pb
rawCid = "bafkreiffndsajwhk3lwjewwdxqntmjm4b5wxaaanokonsggenkbw6slwk4" // raw bytes
cborCid = "bafyreicnga62zhxnmnlt6ymq5hcbsg7gdhqdu6z4ehu3wpjhvqnflfy6nm" // dag-cbor
cborKCid = "bafyr2qgsohbwdlk7ajmmbb4lhoytmest4wdbe5xnexfvtxeatuyqqmwv3fgxp3pmhpc27gwey2cct56gloqefoqwcf3yqiqzsaqb7p4jefhcw" // dag-cbor keccak-512
)

// dag-pb
func pbBlock() io.Reader {
return bytes.NewReader([]byte{10, 12, 8, 2, 18, 6, 104, 101, 108, 108, 111, 10, 24, 6})
}

// dag-cbor
func cborBlock() io.Reader {
return bytes.NewReader([]byte{101, 72, 101, 108, 108, 111})
}
Expand All @@ -38,15 +42,20 @@ func (tp *TestSuite) TestBlock(t *testing.T) {
return nil
})

t.Run("TestBlockPut", tp.TestBlockPut)
t.Run("TestBlockPutFormat", tp.TestBlockPutFormat)
t.Run("TestBlockPut (get raw CIDv1)", tp.TestBlockPut)
t.Run("TestBlockPutCidCodec: dag-pb", tp.TestBlockPutCidCodecDagPb)
t.Run("TestBlockPutCidCodec: dag-cbor", tp.TestBlockPutCidCodecDagCbor)
t.Run("TestBlockPutFormat (legacy): cbor → dag-cbor", tp.TestBlockPutFormatDagCbor)
t.Run("TestBlockPutFormat (legacy): protobuf → dag-pb", tp.TestBlockPutFormatDagPb)
t.Run("TestBlockPutFormat (legacy): v0 → CIDv0", tp.TestBlockPutFormatV0)
Comment on lines +45 to +50
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ these tests run in ipfs/kubo#8568

t.Run("TestBlockPutHash", tp.TestBlockPutHash)
t.Run("TestBlockGet", tp.TestBlockGet)
t.Run("TestBlockRm", tp.TestBlockRm)
t.Run("TestBlockStat", tp.TestBlockStat)
t.Run("TestBlockPin", tp.TestBlockPin)
}

// when no opts are passed, produced CID has 'raw' codec
func (tp *TestSuite) TestBlockPut(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
Expand All @@ -60,12 +69,14 @@ func (tp *TestSuite) TestBlockPut(t *testing.T) {
t.Fatal(err)
}

if res.Path().Cid().String() != pbCid {
if res.Path().Cid().String() != rawCid {
t.Errorf("got wrong cid: %s", res.Path().Cid().String())
}
}

func (tp *TestSuite) TestBlockPutFormat(t *testing.T) {
// Format is deprecated, it used invalid codec names.
// Confirm 'cbor' gets fixed to 'dag-cbor'
func (tp *TestSuite) TestBlockPutFormatDagCbor(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
api, err := tp.makeAPI(ctx)
Expand All @@ -83,6 +94,82 @@ func (tp *TestSuite) TestBlockPutFormat(t *testing.T) {
}
}

// Format is deprecated, it used invalid codec names.
// Confirm 'protobuf' got fixed to 'dag-pb'
func (tp *TestSuite) TestBlockPutFormatDagPb(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
api, err := tp.makeAPI(ctx)
if err != nil {
t.Fatal(err)
}

res, err := api.Block().Put(ctx, pbBlock(), opt.Block.Format("protobuf"))
if err != nil {
t.Fatal(err)
}

if res.Path().Cid().String() != pbCid {
t.Errorf("got wrong cid: %s", res.Path().Cid().String())
}
}

// Format is deprecated, it used invalid codec names.
// Confirm fake codec 'v0' got fixed to CIDv0 (with implicit dag-pb codec)
func (tp *TestSuite) TestBlockPutFormatV0(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
api, err := tp.makeAPI(ctx)
if err != nil {
t.Fatal(err)
}

res, err := api.Block().Put(ctx, pbBlock(), opt.Block.Format("v0"))
if err != nil {
t.Fatal(err)
}

if res.Path().Cid().String() != pbCidV0 {
t.Errorf("got wrong cid: %s", res.Path().Cid().String())
}
}

func (tp *TestSuite) TestBlockPutCidCodecDagCbor(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
api, err := tp.makeAPI(ctx)
if err != nil {
t.Fatal(err)
}

res, err := api.Block().Put(ctx, cborBlock(), opt.Block.CidCodec("dag-cbor"))
if err != nil {
t.Fatal(err)
}

if res.Path().Cid().String() != cborCid {
t.Errorf("got wrong cid: %s", res.Path().Cid().String())
}
}

func (tp *TestSuite) TestBlockPutCidCodecDagPb(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
api, err := tp.makeAPI(ctx)
if err != nil {
t.Fatal(err)
}

res, err := api.Block().Put(ctx, pbBlock(), opt.Block.CidCodec("dag-pb"))
if err != nil {
t.Fatal(err)
}

if res.Path().Cid().String() != pbCid {
t.Errorf("got wrong cid: %s", res.Path().Cid().String())
}
}

func (tp *TestSuite) TestBlockPutHash(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
Expand All @@ -95,7 +182,7 @@ func (tp *TestSuite) TestBlockPutHash(t *testing.T) {
ctx,
cborBlock(),
opt.Block.Hash(mh.KECCAK_512, -1),
opt.Block.Format("cbor"),
opt.Block.CidCodec("dag-cbor"),
)
if err != nil {
t.Fatal(err)
Expand Down