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 all 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
2 changes: 1 addition & 1 deletion version.json
@@ -1,3 +1,3 @@
{
"version": "v0.6.2"
"version": "v0.7.0"
}